1 //
2 // Copyright (c) 2017-2018 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 // clang-format off
24 //
25 // Source: https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
26 // THIS FILE HAS BEEN CHANGED FROM THE ORIGINAL VERSION
27 //
28 // Change Log:
29 // 3/27/19 - Make changes to suppress warnings from GCC
30 // 4/18/19 - Make changes to suppress warnings from clang
31 // 6/05/19 - Make changes to suppress warnings from clang 3.8.0
32 // 6/05/19 - Make changes to suppress more warnings from GCC
33 // 8/09/19 - Make changes to suppress dead code warnings (from upstream master branch)
34 //
35
36 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
37 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
38
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42
43 /** \mainpage Vulkan Memory Allocator
44
45 <b>Version 2.2.0</b> (2018-12-13)
46
47 Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
48 License: MIT
49
50 Documentation of all members: vk_mem_alloc.h
51
52 \section main_table_of_contents Table of contents
53
54 - <b>User guide</b>
55 - \subpage quick_start
56 - [Project setup](@ref quick_start_project_setup)
57 - [Initialization](@ref quick_start_initialization)
58 - [Resource allocation](@ref quick_start_resource_allocation)
59 - \subpage choosing_memory_type
60 - [Usage](@ref choosing_memory_type_usage)
61 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
62 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
63 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
64 - \subpage memory_mapping
65 - [Mapping functions](@ref memory_mapping_mapping_functions)
66 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
67 - [Cache control](@ref memory_mapping_cache_control)
68 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
69 - \subpage custom_memory_pools
70 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
71 - [Linear allocation algorithm](@ref linear_algorithm)
72 - [Free-at-once](@ref linear_algorithm_free_at_once)
73 - [Stack](@ref linear_algorithm_stack)
74 - [Double stack](@ref linear_algorithm_double_stack)
75 - [Ring buffer](@ref linear_algorithm_ring_buffer)
76 - [Buddy allocation algorithm](@ref buddy_algorithm)
77 - \subpage defragmentation
78 - [Defragmenting CPU memory](@ref defragmentation_cpu)
79 - [Defragmenting GPU memory](@ref defragmentation_gpu)
80 - [Additional notes](@ref defragmentation_additional_notes)
81 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
82 - \subpage lost_allocations
83 - \subpage statistics
84 - [Numeric statistics](@ref statistics_numeric_statistics)
85 - [JSON dump](@ref statistics_json_dump)
86 - \subpage allocation_annotation
87 - [Allocation user data](@ref allocation_user_data)
88 - [Allocation names](@ref allocation_names)
89 - \subpage debugging_memory_usage
90 - [Memory initialization](@ref debugging_memory_usage_initialization)
91 - [Margins](@ref debugging_memory_usage_margins)
92 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
93 - \subpage record_and_replay
94 - \subpage usage_patterns
95 - [Simple patterns](@ref usage_patterns_simple)
96 - [Advanced patterns](@ref usage_patterns_advanced)
97 - \subpage configuration
98 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
99 - [Custom host memory allocator](@ref custom_memory_allocator)
100 - [Device memory allocation callbacks](@ref allocation_callbacks)
101 - [Device heap memory limit](@ref heap_memory_limit)
102 - \subpage vk_khr_dedicated_allocation
103 - \subpage general_considerations
104 - [Thread safety](@ref general_considerations_thread_safety)
105 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
106 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
107 - [Features not supported](@ref general_considerations_features_not_supported)
108
109 \section main_see_also See also
110
111 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
112 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
113
114
115
116
117 \page quick_start Quick start
118
119 \section quick_start_project_setup Project setup
120
121 Vulkan Memory Allocator comes in form of a single header file.
122 You don't need to build it as a separate library project.
123 You can add this file directly to your project and submit it to code repository next to your other source files.
124
125 "Single header" doesn't mean that everything is contained in C/C++ declarations,
126 like it tends to be in case of inline functions or C++ templates.
127 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
128 If you don't do it properly, you will get linker errors.
129
130 To do it properly:
131
132 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
133 This includes declarations of all members of the library.
134 -# In exacly one CPP file define following macro before this include.
135 It enables also internal definitions.
136
137 \code
138 #define VMA_IMPLEMENTATION
139 #include "vk_mem_alloc.h"
140 \endcode
141
142 It may be a good idea to create dedicated CPP file just for this purpose.
143
144 Note on language: This library is written in C++, but has C-compatible interface.
145 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
146 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
147
148 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
149 includes `<windows.h>` on Windows. If you need some specific macros defined
150 before including these headers (like `WIN32_LEAN_AND_MEAN` or
151 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
152 them before every `#include` of this library.
153
154
155 \section quick_start_initialization Initialization
156
157 At program startup:
158
159 -# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
160 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
161 calling vmaCreateAllocator().
162
163 \code
164 VmaAllocatorCreateInfo allocatorInfo = {};
165 allocatorInfo.physicalDevice = physicalDevice;
166 allocatorInfo.device = device;
167
168 VmaAllocator allocator;
169 vmaCreateAllocator(&allocatorInfo, &allocator);
170 \endcode
171
172 \section quick_start_resource_allocation Resource allocation
173
174 When you want to create a buffer or image:
175
176 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
177 -# Fill VmaAllocationCreateInfo structure.
178 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
179 already allocated and bound to it.
180
181 \code
182 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
183 bufferInfo.size = 65536;
184 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
185
186 VmaAllocationCreateInfo allocInfo = {};
187 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
188
189 VkBuffer buffer;
190 VmaAllocation allocation;
191 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
192 \endcode
193
194 Don't forget to destroy your objects when no longer needed:
195
196 \code
197 vmaDestroyBuffer(allocator, buffer, allocation);
198 vmaDestroyAllocator(allocator);
199 \endcode
200
201
202 \page choosing_memory_type Choosing memory type
203
204 Physical devices in Vulkan support various combinations of memory heaps and
205 types. Help with choosing correct and optimal memory type for your specific
206 resource is one of the key features of this library. You can use it by filling
207 appropriate members of VmaAllocationCreateInfo structure, as described below.
208 You can also combine multiple methods.
209
210 -# If you just want to find memory type index that meets your requirements, you
211 can use function vmaFindMemoryTypeIndex().
212 -# If you want to allocate a region of device memory without association with any
213 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
214 this function is not recommended and usually not needed.
215 -# If you already have a buffer or an image created, you want to allocate memory
216 for it and then you will bind it yourself, you can use function
217 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
218 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
219 -# If you want to create a buffer or an image, allocate memory for it and bind
220 them together, all in one call, you can use function vmaCreateBuffer(),
221 vmaCreateImage(). This is the recommended way to use this library.
222
223 When using 3. or 4., the library internally queries Vulkan for memory types
224 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
225 and uses only one of these types.
226
227 If no memory type can be found that meets all the requirements, these functions
228 return `VK_ERROR_FEATURE_NOT_PRESENT`.
229
230 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
231 It means no requirements are specified for memory type.
232 It is valid, although not very useful.
233
234 \section choosing_memory_type_usage Usage
235
236 The easiest way to specify memory requirements is to fill member
237 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
238 It defines high level, common usage types.
239 For more details, see description of this enum.
240
241 For example, if you want to create a uniform buffer that will be filled using
242 transfer only once or infrequently and used for rendering every frame, you can
243 do it using following code:
244
245 \code
246 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
247 bufferInfo.size = 65536;
248 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
249
250 VmaAllocationCreateInfo allocInfo = {};
251 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
252
253 VkBuffer buffer;
254 VmaAllocation allocation;
255 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
256 \endcode
257
258 \section choosing_memory_type_required_preferred_flags Required and preferred flags
259
260 You can specify more detailed requirements by filling members
261 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
262 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
263 if you want to create a buffer that will be persistently mapped on host (so it
264 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
265 use following code:
266
267 \code
268 VmaAllocationCreateInfo allocInfo = {};
269 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
270 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
271 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
272
273 VkBuffer buffer;
274 VmaAllocation allocation;
275 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
276 \endcode
277
278 A memory type is chosen that has all the required flags and as many preferred
279 flags set as possible.
280
281 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
282 a set of required and preferred flags.
283
284 \section choosing_memory_type_explicit_memory_types Explicit memory types
285
286 If you inspected memory types available on the physical device and you have
287 a preference for memory types that you want to use, you can fill member
288 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
289 means that a memory type with that index is allowed to be used for the
290 allocation. Special value 0, just like `UINT32_MAX`, means there are no
291 restrictions to memory type index.
292
293 Please note that this member is NOT just a memory type index.
294 Still you can use it to choose just one, specific memory type.
295 For example, if you already determined that your buffer should be created in
296 memory type 2, use following code:
297
298 \code
299 uint32_t memoryTypeIndex = 2;
300
301 VmaAllocationCreateInfo allocInfo = {};
302 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
303
304 VkBuffer buffer;
305 VmaAllocation allocation;
306 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
307 \endcode
308
309 \section choosing_memory_type_custom_memory_pools Custom memory pools
310
311 If you allocate from custom memory pool, all the ways of specifying memory
312 requirements described above are not applicable and the aforementioned members
313 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
314 explicitly when creating the pool and then used to make all the allocations from
315 that pool. For further details, see \ref custom_memory_pools.
316
317
318 \page memory_mapping Memory mapping
319
320 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
321 to be able to read from it or write to it in CPU code.
322 Mapping is possible only of memory allocated from a memory type that has
323 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
324 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
325 You can use them directly with memory allocated by this library,
326 but it is not recommended because of following issue:
327 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
328 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
329 Because of this, Vulkan Memory Allocator provides following facilities:
330
331 \section memory_mapping_mapping_functions Mapping functions
332
333 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
334 They are safer and more convenient to use than standard Vulkan functions.
335 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
336 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
337 The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
338 For further details, see description of vmaMapMemory() function.
339 Example:
340
341 \code
342 // Having these objects initialized:
343
344 struct ConstantBuffer
345 {
346 ...
347 };
348 ConstantBuffer constantBufferData;
349
350 VmaAllocator allocator;
351 VkBuffer constantBuffer;
352 VmaAllocation constantBufferAllocation;
353
354 // You can map and fill your buffer using following code:
355
356 void* mappedData;
357 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
358 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
359 vmaUnmapMemory(allocator, constantBufferAllocation);
360 \endcode
361
362 When mapping, you may see a warning from Vulkan validation layer similar to this one:
363
364 <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>
365
366 It happens because the library maps entire `VkDeviceMemory` block, where different
367 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
368 You can safely ignore it if you are sure you access only memory of the intended
369 object that you wanted to map.
370
371
372 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
373
374 Kepping your memory persistently mapped is generally OK in Vulkan.
375 You don't need to unmap it before using its data on the GPU.
376 The library provides a special feature designed for that:
377 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
378 VmaAllocationCreateInfo::flags stay mapped all the time,
379 so you can just access CPU pointer to it any time
380 without a need to call any "map" or "unmap" function.
381 Example:
382
383 \code
384 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
385 bufCreateInfo.size = sizeof(ConstantBuffer);
386 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
387
388 VmaAllocationCreateInfo allocCreateInfo = {};
389 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
390 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
391
392 VkBuffer buf;
393 VmaAllocation alloc;
394 VmaAllocationInfo allocInfo;
395 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
396
397 // Buffer is already mapped. You can access its memory.
398 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
399 \endcode
400
401 There are some exceptions though, when you should consider mapping memory only for a short period of time:
402
403 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
404 device is discrete AMD GPU,
405 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
406 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
407 then whenever a memory block allocated from this memory type stays mapped
408 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
409 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
410 matter if that particular memory block is actually used by the command buffer
411 being submitted.
412 - On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
413 which requires unmapping before GPU can see updated texture.
414 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
415
416 \section memory_mapping_cache_control Cache control
417
418 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
419 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
420 you need to manually invalidate cache before reading of mapped pointer
421 and flush cache after writing to mapped pointer.
422 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
423 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
424 functions that refer to given allocation object: vmaFlushAllocation(),
425 vmaInvalidateAllocation().
426
427 Regions of memory specified for flush/invalidate must be aligned to
428 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
429 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
430 within blocks are aligned to this value, so their offsets are always multiply of
431 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
432
433 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
434
435 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
436 currently provide `HOST_COHERENT` flag on all memory types that are
437 `HOST_VISIBLE`, so on this platform you may not need to bother.
438
439 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
440
441 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
442 despite it wasn't explicitly requested.
443 For example, application may work on integrated graphics with unified memory (like Intel) or
444 allocation from video memory might have failed, so the library chose system memory as fallback.
445
446 You can detect this case and map such allocation to access its memory on CPU directly,
447 instead of launching a transfer operation.
448 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
449 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
450
451 \code
452 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
453 bufCreateInfo.size = sizeof(ConstantBuffer);
454 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
455
456 VmaAllocationCreateInfo allocCreateInfo = {};
457 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
458 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
459
460 VkBuffer buf;
461 VmaAllocation alloc;
462 VmaAllocationInfo allocInfo;
463 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
464
465 VkMemoryPropertyFlags memFlags;
466 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
467 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
468 {
469 // Allocation ended up in mappable memory. You can map it and access it directly.
470 void* mappedData;
471 vmaMapMemory(allocator, alloc, &mappedData);
472 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
473 vmaUnmapMemory(allocator, alloc);
474 }
475 else
476 {
477 // Allocation ended up in non-mappable memory.
478 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
479 }
480 \endcode
481
482 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
483 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
484 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
485 If not, the flag is just ignored.
486 Example:
487
488 \code
489 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
490 bufCreateInfo.size = sizeof(ConstantBuffer);
491 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
492
493 VmaAllocationCreateInfo allocCreateInfo = {};
494 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
495 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
496
497 VkBuffer buf;
498 VmaAllocation alloc;
499 VmaAllocationInfo allocInfo;
500 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
501
502 if(allocInfo.pUserData != nullptr)
503 {
504 // Allocation ended up in mappable memory.
505 // It's persistently mapped. You can access it directly.
506 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
507 }
508 else
509 {
510 // Allocation ended up in non-mappable memory.
511 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
512 }
513 \endcode
514
515
516 \page custom_memory_pools Custom memory pools
517
518 A memory pool contains a number of `VkDeviceMemory` blocks.
519 The library automatically creates and manages default pool for each memory type available on the device.
520 Default memory pool automatically grows in size.
521 Size of allocated blocks is also variable and managed automatically.
522
523 You can create custom pool and allocate memory out of it.
524 It can be useful if you want to:
525
526 - Keep certain kind of allocations separate from others.
527 - Enforce particular, fixed size of Vulkan memory blocks.
528 - Limit maximum amount of Vulkan memory allocated for that pool.
529 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
530
531 To use custom memory pools:
532
533 -# Fill VmaPoolCreateInfo structure.
534 -# Call vmaCreatePool() to obtain #VmaPool handle.
535 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
536 You don't need to specify any other parameters of this structure, like `usage`.
537
538 Example:
539
540 \code
541 // Create a pool that can have at most 2 blocks, 128 MiB each.
542 VmaPoolCreateInfo poolCreateInfo = {};
543 poolCreateInfo.memoryTypeIndex = ...
544 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
545 poolCreateInfo.maxBlockCount = 2;
546
547 VmaPool pool;
548 vmaCreatePool(allocator, &poolCreateInfo, &pool);
549
550 // Allocate a buffer out of it.
551 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
552 bufCreateInfo.size = 1024;
553 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
554
555 VmaAllocationCreateInfo allocCreateInfo = {};
556 allocCreateInfo.pool = pool;
557
558 VkBuffer buf;
559 VmaAllocation alloc;
560 VmaAllocationInfo allocInfo;
561 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
562 \endcode
563
564 You have to free all allocations made from this pool before destroying it.
565
566 \code
567 vmaDestroyBuffer(allocator, buf, alloc);
568 vmaDestroyPool(allocator, pool);
569 \endcode
570
571 \section custom_memory_pools_MemTypeIndex Choosing memory type index
572
573 When creating a pool, you must explicitly specify memory type index.
574 To find the one suitable for your buffers or images, you can use helper functions
575 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
576 You need to provide structures with example parameters of buffers or images
577 that you are going to create in that pool.
578
579 \code
580 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
581 exampleBufCreateInfo.size = 1024; // Whatever.
582 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
583
584 VmaAllocationCreateInfo allocCreateInfo = {};
585 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
586
587 uint32_t memTypeIndex;
588 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
589
590 VmaPoolCreateInfo poolCreateInfo = {};
591 poolCreateInfo.memoryTypeIndex = memTypeIndex;
592 // ...
593 \endcode
594
595 When creating buffers/images allocated in that pool, provide following parameters:
596
597 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
598 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
599 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
600 or the other way around.
601 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
602 Other members are ignored anyway.
603
604 \section linear_algorithm Linear allocation algorithm
605
606 Each Vulkan memory block managed by this library has accompanying metadata that
607 keeps track of used and unused regions. By default, the metadata structure and
608 algorithm tries to find best place for new allocations among free regions to
609 optimize memory usage. This way you can allocate and free objects in any order.
610
611 
612
613 Sometimes there is a need to use simpler, linear allocation algorithm. You can
614 create custom pool that uses such algorithm by adding flag
615 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
616 #VmaPool object. Then an alternative metadata management is used. It always
617 creates new allocations after last one and doesn't reuse free regions after
618 allocations freed in the middle. It results in better allocation performance and
619 less memory consumed by metadata.
620
621 
622
623 With this one flag, you can create a custom pool that can be used in many ways:
624 free-at-once, stack, double stack, and ring buffer. See below for details.
625
626 \subsection linear_algorithm_free_at_once Free-at-once
627
628 In a pool that uses linear algorithm, you still need to free all the allocations
629 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
630 them in any order. New allocations are always made after last one - free space
631 in the middle is not reused. However, when you release all the allocation and
632 the pool becomes empty, allocation starts from the beginning again. This way you
633 can use linear algorithm to speed up creation of allocations that you are going
634 to release all at once.
635
636 
637
638 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
639 value that allows multiple memory blocks.
640
641 \subsection linear_algorithm_stack Stack
642
643 When you free an allocation that was created last, its space can be reused.
644 Thanks to this, if you always release allocations in the order opposite to their
645 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
646
647 
648
649 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
650 value that allows multiple memory blocks.
651
652 \subsection linear_algorithm_double_stack Double stack
653
654 The space reserved by a custom pool with linear algorithm may be used by two
655 stacks:
656
657 - First, default one, growing up from offset 0.
658 - Second, "upper" one, growing down from the end towards lower offsets.
659
660 To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
661 to VmaAllocationCreateInfo::flags.
662
663 
664
665 Double stack is available only in pools with one memory block -
666 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
667
668 When the two stacks' ends meet so there is not enough space between them for a
669 new allocation, such allocation fails with usual
670 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
671
672 \subsection linear_algorithm_ring_buffer Ring buffer
673
674 When you free some allocations from the beginning and there is not enough free space
675 for a new one at the end of a pool, allocator's "cursor" wraps around to the
676 beginning and starts allocation there. Thanks to this, if you always release
677 allocations in the same order as you created them (FIFO - First In First Out),
678 you can achieve behavior of a ring buffer / queue.
679
680 
681
682 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
683 If there is not enough free space for a new allocation, but existing allocations
684 from the front of the queue can become lost, they become lost and the allocation
685 succeeds.
686
687 
688
689 Ring buffer is available only in pools with one memory block -
690 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
691
692 \section buddy_algorithm Buddy allocation algorithm
693
694 There is another allocation algorithm that can be used with custom pools, called
695 "buddy". Its internal data structure is based on a tree of blocks, each having
696 size that is a power of two and a half of its parent's size. When you want to
697 allocate memory of certain size, a free node in the tree is located. If it's too
698 large, it is recursively split into two halves (called "buddies"). However, if
699 requested allocation size is not a power of two, the size of a tree node is
700 aligned up to the nearest power of two and the remaining space is wasted. When
701 two buddy nodes become free, they are merged back into one larger node.
702
703 
704
705 The advantage of buddy allocation algorithm over default algorithm is faster
706 allocation and deallocation, as well as smaller external fragmentation. The
707 disadvantage is more wasted space (internal fragmentation).
708
709 For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
710 or other sources that describe this concept in general.
711
712 To use buddy allocation algorithm with a custom pool, add flag
713 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
714 #VmaPool object.
715
716 Several limitations apply to pools that use buddy algorithm:
717
718 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
719 Otherwise, only largest power of two smaller than the size is used for
720 allocations. The remaining space always stays unused.
721 - [Margins](@ref debugging_memory_usage_margins) and
722 [corruption detection](@ref debugging_memory_usage_corruption_detection)
723 don't work in such pools.
724 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
725 use them, but they never become lost. Support may be added in the future.
726 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
727 such pool.
728
729 \page defragmentation Defragmentation
730
731 Interleaved allocations and deallocations of many objects of varying size can
732 cause fragmentation over time, which can lead to a situation where the library is unable
733 to find a continuous range of free memory for a new allocation despite there is
734 enough free space, just scattered across many small free ranges between existing
735 allocations.
736
737 To mitigate this problem, you can use defragmentation feature:
738 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
739 Given set of allocations,
740 this function can move them to compact used memory, ensure more continuous free
741 space and possibly also free some `VkDeviceMemory` blocks.
742
743 What the defragmentation does is:
744
745 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
746 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
747 VmaAllocationInfo::offset changes. You must query them again using
748 vmaGetAllocationInfo() if you need them.
749 - Moves actual data in memory.
750
751 What it doesn't do, so you need to do it yourself:
752
753 - Recreate buffers and images that were bound to allocations that were defragmented and
754 bind them with their new places in memory.
755 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
756 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
757 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
758 destroy or create allocation objects!
759 - Recreate views and update descriptors that point to these buffers and images.
760
761 \section defragmentation_cpu Defragmenting CPU memory
762
763 Following example demonstrates how you can run defragmentation on CPU.
764 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
765 Others are ignored.
766
767 The way it works is:
768
769 - It temporarily maps entire memory blocks when necessary.
770 - It moves data using `memmove()` function.
771
772 \code
773 // Given following variables already initialized:
774 VkDevice device;
775 VmaAllocator allocator;
776 std::vector<VkBuffer> buffers;
777 std::vector<VmaAllocation> allocations;
778
779
780 const uint32_t allocCount = (uint32_t)allocations.size();
781 std::vector<VkBool32> allocationsChanged(allocCount);
782
783 VmaDefragmentationInfo2 defragInfo = {};
784 defragInfo.allocationCount = allocCount;
785 defragInfo.pAllocations = allocations.data();
786 defragInfo.pAllocationsChanged = allocationsChanged.data();
787 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
788 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
789
790 VmaDefragmentationContext defragCtx;
791 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
792 vmaDefragmentationEnd(allocator, defragCtx);
793
794 for(uint32_t i = 0; i < allocCount; ++i)
795 {
796 if(allocationsChanged[i])
797 {
798 // Destroy buffer that is immutably bound to memory region which is no longer valid.
799 vkDestroyBuffer(device, buffers[i], nullptr);
800
801 // Create new buffer with same parameters.
802 VkBufferCreateInfo bufferInfo = ...;
803 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
804
805 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
806
807 // Bind new buffer to new memory region. Data contained in it is already moved.
808 VmaAllocationInfo allocInfo;
809 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
810 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
811 }
812 }
813 \endcode
814
815 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
816 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
817 has been modified during defragmentation.
818 You can pass null, but you then need to query every allocation passed to defragmentation
819 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
820
821 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
822 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
823 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
824 to defragment all allocations in given pools.
825 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
826 You can also combine both methods.
827
828 \section defragmentation_gpu Defragmenting GPU memory
829
830 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
831 To do that, you need to pass a command buffer that meets requirements as described in
832 VmaDefragmentationInfo2::commandBuffer. The way it works is:
833
834 - It creates temporary buffers and binds them to entire memory blocks when necessary.
835 - It issues `vkCmdCopyBuffer()` to passed command buffer.
836
837 Example:
838
839 \code
840 // Given following variables already initialized:
841 VkDevice device;
842 VmaAllocator allocator;
843 VkCommandBuffer commandBuffer;
844 std::vector<VkBuffer> buffers;
845 std::vector<VmaAllocation> allocations;
846
847
848 const uint32_t allocCount = (uint32_t)allocations.size();
849 std::vector<VkBool32> allocationsChanged(allocCount);
850
851 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
852 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
853
854 VmaDefragmentationInfo2 defragInfo = {};
855 defragInfo.allocationCount = allocCount;
856 defragInfo.pAllocations = allocations.data();
857 defragInfo.pAllocationsChanged = allocationsChanged.data();
858 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
859 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
860 defragInfo.commandBuffer = commandBuffer;
861
862 VmaDefragmentationContext defragCtx;
863 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
864
865 vkEndCommandBuffer(commandBuffer);
866
867 // Submit commandBuffer.
868 // Wait for a fence that ensures commandBuffer execution finished.
869
870 vmaDefragmentationEnd(allocator, defragCtx);
871
872 for(uint32_t i = 0; i < allocCount; ++i)
873 {
874 if(allocationsChanged[i])
875 {
876 // Destroy buffer that is immutably bound to memory region which is no longer valid.
877 vkDestroyBuffer(device, buffers[i], nullptr);
878
879 // Create new buffer with same parameters.
880 VkBufferCreateInfo bufferInfo = ...;
881 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
882
883 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
884
885 // Bind new buffer to new memory region. Data contained in it is already moved.
886 VmaAllocationInfo allocInfo;
887 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
888 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
889 }
890 }
891 \endcode
892
893 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
894 The library automatically chooses best method to defragment each memory pool.
895
896 You may try not to block your entire program to wait until defragmentation finishes,
897 but do it in the background, as long as you carefully fullfill requirements described
898 in function vmaDefragmentationBegin().
899
900 \section defragmentation_additional_notes Additional notes
901
902 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
903 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
904
905 If you defragment allocations bound to images, these images should be created with
906 `VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
907 parameters and pointing to data copied to another memory region will interpret
908 its contents consistently. Otherwise you may experience corrupted data on some
909 implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
910
911 If you defragment allocations bound to images, new images to be bound to new
912 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
913 and then transitioned to their original layout from before defragmentation using
914 an image memory barrier.
915
916 Please don't expect memory to be fully compacted after defragmentation.
917 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
918 memory blocks to make totally empty to release them, as well as to maximimze continuous
919 empty space inside remaining blocks, while minimizing the number and size of allocations that
920 need to be moved. Some fragmentation may still remain - this is normal.
921
922 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
923
924 If you want to implement your own, custom defragmentation algorithm,
925 there is infrastructure prepared for that,
926 but it is not exposed through the library API - you need to hack its source code.
927 Here are steps needed to do this:
928
929 -# Main thing you need to do is to define your own class derived from base abstract
930 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
931 See definition and comments of this class for details.
932 -# Your code needs to interact with device memory block metadata.
933 If you need more access to its data than it's provided by its public interface,
934 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
935 -# If you want to create a flag that would enable your algorithm or pass some additional
936 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
937 VmaDefragmentationInfo2::flags.
938 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
939 of your new class whenever needed.
940
941
942 \page lost_allocations Lost allocations
943
944 If your game oversubscribes video memory, if may work OK in previous-generation
945 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
946 paged to system RAM. In Vulkan you can't do it because when you run out of
947 memory, an allocation just fails. If you have more data (e.g. textures) that can
948 fit into VRAM and you don't need it all at once, you may want to upload them to
949 GPU on demand and "push out" ones that are not used for a long time to make room
950 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
951 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
952 "lost allocations".
953
954 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
955 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
956 such allocation in every new frame, you need to query it if it's not lost.
957 To check it, call vmaTouchAllocation().
958 If the allocation is lost, you should not use it or buffer/image bound to it.
959 You mustn't forget to destroy this allocation and this buffer/image.
960 vmaGetAllocationInfo() can also be used for checking status of the allocation.
961 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
962
963 To create an allocation that can make some other allocations lost to make room
964 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
965 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
966 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
967
968 Warning! Current implementation uses quite naive, brute force algorithm,
969 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
970 flag quite slow. A new, more optimal algorithm and data structure to speed this
971 up is planned for the future.
972
973 <b>Q: When interleaving creation of new allocations with usage of existing ones,
974 how do you make sure that an allocation won't become lost while it's used in the
975 current frame?</b>
976
977 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
978 status/parameters and checks whether it's not lost, but when it's not, it also
979 atomically marks it as used in the current frame, which makes it impossible to
980 become lost in that frame. It uses lockless algorithm, so it works fast and
981 doesn't involve locking any internal mutex.
982
983 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
984 previous frame while I already submit new frame on the CPU?</b>
985
986 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
987 become lost for a number of additional frames back from the current one by
988 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
989 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
990
991 <b>Q: How do you inform the library when new frame starts?</b>
992
993 You need to call function vmaSetCurrentFrameIndex().
994
995 Example code:
996
997 \code
998 struct MyBuffer
999 {
1000 VkBuffer m_Buf = nullptr;
1001 VmaAllocation m_Alloc = nullptr;
1002
1003 // Called when the buffer is really needed in the current frame.
1004 void EnsureBuffer();
1005 };
1006
1007 void MyBuffer::EnsureBuffer()
1008 {
1009 // Buffer has been created.
1010 if(m_Buf != VK_NULL_HANDLE)
1011 {
1012 // Check if its allocation is not lost + mark it as used in current frame.
1013 if(vmaTouchAllocation(allocator, m_Alloc))
1014 {
1015 // It's all OK - safe to use m_Buf.
1016 return;
1017 }
1018 }
1019
1020 // Buffer not yet exists or lost - destroy and recreate it.
1021
1022 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1023
1024 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1025 bufCreateInfo.size = 1024;
1026 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1027
1028 VmaAllocationCreateInfo allocCreateInfo = {};
1029 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1030 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1031 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1032
1033 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1034 }
1035 \endcode
1036
1037 When using lost allocations, you may see some Vulkan validation layer warnings
1038 about overlapping regions of memory bound to different kinds of buffers and
1039 images. This is still valid as long as you implement proper handling of lost
1040 allocations (like in the example above) and don't use them.
1041
1042 You can create an allocation that is already in lost state from the beginning using function
1043 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1044
1045 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1046 in a specified custom pool to lost state.
1047 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1048 cannot become lost.
1049
1050 <b>Q: Can I touch allocation that cannot become lost?</b>
1051
1052 Yes, although it has no visible effect.
1053 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1054 also for allocations that cannot become lost, but the only way to observe it is to dump
1055 internal allocator state using vmaBuildStatsString().
1056 You can use this feature for debugging purposes to explicitly mark allocations that you use
1057 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1058
1059
1060 \page statistics Statistics
1061
1062 This library contains functions that return information about its internal state,
1063 especially the amount of memory allocated from Vulkan.
1064 Please keep in mind that these functions need to traverse all internal data structures
1065 to gather these information, so they may be quite time-consuming.
1066 Don't call them too often.
1067
1068 \section statistics_numeric_statistics Numeric statistics
1069
1070 You can query for overall statistics of the allocator using function vmaCalculateStats().
1071 Information are returned using structure #VmaStats.
1072 It contains #VmaStatInfo - number of allocated blocks, number of allocations
1073 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1074 number of bytes used and unused (but still allocated from Vulkan) and other information.
1075 They are summed across memory heaps, memory types and total for whole allocator.
1076
1077 You can query for statistics of a custom pool using function vmaGetPoolStats().
1078 Information are returned using structure #VmaPoolStats.
1079
1080 You can query for information about specific allocation using function vmaGetAllocationInfo().
1081 It fill structure #VmaAllocationInfo.
1082
1083 \section statistics_json_dump JSON dump
1084
1085 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1086 The result is guaranteed to be correct JSON.
1087 It uses ANSI encoding.
1088 Any strings provided by user (see [Allocation names](@ref allocation_names))
1089 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1090 this JSON string can be treated as using this encoding.
1091 It must be freed using function vmaFreeStatsString().
1092
1093 The format of this JSON string is not part of official documentation of the library,
1094 but it will not change in backward-incompatible way without increasing library major version number
1095 and appropriate mention in changelog.
1096
1097 The JSON string contains all the data that can be obtained using vmaCalculateStats().
1098 It can also contain detailed map of allocated memory blocks and their regions -
1099 free and occupied by allocations.
1100 This allows e.g. to visualize the memory or assess fragmentation.
1101
1102
1103 \page allocation_annotation Allocation names and user data
1104
1105 \section allocation_user_data Allocation user data
1106
1107 You can annotate allocations with your own information, e.g. for debugging purposes.
1108 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1109 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1110 some handle, index, key, ordinal number or any other value that would associate
1111 the allocation with your custom metadata.
1112
1113 \code
1114 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1115 // Fill bufferInfo...
1116
1117 MyBufferMetadata* pMetadata = CreateBufferMetadata();
1118
1119 VmaAllocationCreateInfo allocCreateInfo = {};
1120 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1121 allocCreateInfo.pUserData = pMetadata;
1122
1123 VkBuffer buffer;
1124 VmaAllocation allocation;
1125 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1126 \endcode
1127
1128 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1129
1130 \code
1131 VmaAllocationInfo allocInfo;
1132 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1133 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1134 \endcode
1135
1136 It can also be changed using function vmaSetAllocationUserData().
1137
1138 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1139 vmaBuildStatsString(), in hexadecimal form.
1140
1141 \section allocation_names Allocation names
1142
1143 There is alternative mode available where `pUserData` pointer is used to point to
1144 a null-terminated string, giving a name to the allocation. To use this mode,
1145 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1146 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1147 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1148 The library creates internal copy of the string, so the pointer you pass doesn't need
1149 to be valid for whole lifetime of the allocation. You can free it after the call.
1150
1151 \code
1152 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1153 // Fill imageInfo...
1154
1155 std::string imageName = "Texture: ";
1156 imageName += fileName;
1157
1158 VmaAllocationCreateInfo allocCreateInfo = {};
1159 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1160 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1161 allocCreateInfo.pUserData = imageName.c_str();
1162
1163 VkImage image;
1164 VmaAllocation allocation;
1165 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1166 \endcode
1167
1168 The value of `pUserData` pointer of the allocation will be different than the one
1169 you passed when setting allocation's name - pointing to a buffer managed
1170 internally that holds copy of the string.
1171
1172 \code
1173 VmaAllocationInfo allocInfo;
1174 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1175 const char* imageName = (const char*)allocInfo.pUserData;
1176 printf("Image name: %s\n", imageName);
1177 \endcode
1178
1179 That string is also printed in JSON report created by vmaBuildStatsString().
1180
1181
1182 \page debugging_memory_usage Debugging incorrect memory usage
1183
1184 If you suspect a bug with memory usage, like usage of uninitialized memory or
1185 memory being overwritten out of bounds of an allocation,
1186 you can use debug features of this library to verify this.
1187
1188 \section debugging_memory_usage_initialization Memory initialization
1189
1190 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1191 you can enable automatic memory initialization to verify this.
1192 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1193
1194 \code
1195 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1196 #include "vk_mem_alloc.h"
1197 \endcode
1198
1199 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1200 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1201 Memory is automatically mapped and unmapped if necessary.
1202
1203 If you find these values while debugging your program, good chances are that you incorrectly
1204 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1205
1206 Memory initialization works only with memory types that are `HOST_VISIBLE`.
1207 It works also with dedicated allocations.
1208 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1209 as they cannot be mapped.
1210
1211 \section debugging_memory_usage_margins Margins
1212
1213 By default, allocations are laid out in memory blocks next to each other if possible
1214 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1215
1216 
1217
1218 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1219 number of bytes as a margin before and after every allocation.
1220
1221 \code
1222 #define VMA_DEBUG_MARGIN 16
1223 #include "vk_mem_alloc.h"
1224 \endcode
1225
1226 
1227
1228 If your bug goes away after enabling margins, it means it may be caused by memory
1229 being overwritten outside of allocation boundaries. It is not 100% certain though.
1230 Change in application behavior may also be caused by different order and distribution
1231 of allocations across memory blocks after margins are applied.
1232
1233 The margin is applied also before first and after last allocation in a block.
1234 It may occur only once between two adjacent allocations.
1235
1236 Margins work with all types of memory.
1237
1238 Margin is applied only to allocations made out of memory blocks and not to dedicated
1239 allocations, which have their own memory block of specific size.
1240 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1241 or those automatically decided to put into dedicated allocations, e.g. due to its
1242 large size or recommended by VK_KHR_dedicated_allocation extension.
1243 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1244
1245 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1246
1247 Note that enabling margins increases memory usage and fragmentation.
1248
1249 \section debugging_memory_usage_corruption_detection Corruption detection
1250
1251 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1252 of contents of the margins.
1253
1254 \code
1255 #define VMA_DEBUG_MARGIN 16
1256 #define VMA_DEBUG_DETECT_CORRUPTION 1
1257 #include "vk_mem_alloc.h"
1258 \endcode
1259
1260 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1261 (it must be multiply of 4) before and after every allocation is filled with a magic number.
1262 This idea is also know as "canary".
1263 Memory is automatically mapped and unmapped if necessary.
1264
1265 This number is validated automatically when the allocation is destroyed.
1266 If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1267 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1268 which indicates a serious bug.
1269
1270 You can also explicitly request checking margins of all allocations in all memory blocks
1271 that belong to specified memory types by using function vmaCheckCorruption(),
1272 or in memory blocks that belong to specified custom pool, by using function
1273 vmaCheckPoolCorruption().
1274
1275 Margin validation (corruption detection) works only for memory types that are
1276 `HOST_VISIBLE` and `HOST_COHERENT`.
1277
1278
1279 \page record_and_replay Record and replay
1280
1281 \section record_and_replay_introduction Introduction
1282
1283 While using the library, sequence of calls to its functions together with their
1284 parameters can be recorded to a file and later replayed using standalone player
1285 application. It can be useful to:
1286
1287 - Test correctness - check if same sequence of calls will not cause crash or
1288 failures on a target platform.
1289 - Gather statistics - see number of allocations, peak memory usage, number of
1290 calls etc.
1291 - Benchmark performance - see how much time it takes to replay the whole
1292 sequence.
1293
1294 \section record_and_replay_usage Usage
1295
1296 <b>To record sequence of calls to a file:</b> Fill in
1297 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1298 object. File is opened and written during whole lifetime of the allocator.
1299
1300 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
1301 Precompiled binary can be found in "bin" directory.
1302 Its source can be found in "src/VmaReplay" directory.
1303 Its project is generated by Premake.
1304 Command line syntax is printed when the program is launched without parameters.
1305 Basic usage:
1306
1307 VmaReplay.exe MyRecording.csv
1308
1309 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1310 It's a human-readable, text file in CSV format (Comma Separated Values).
1311
1312 \section record_and_replay_additional_considerations Additional considerations
1313
1314 - Replaying file that was recorded on a different GPU (with different parameters
1315 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1316 set of memory heaps and types) may give different performance and memory usage
1317 results, as well as issue some warnings and errors.
1318 - Current implementation of recording in VMA, as well as VmaReplay application, is
1319 coded and tested only on Windows. Inclusion of recording code is driven by
1320 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1321 add. Contributions are welcomed.
1322 - Currently calls to vmaDefragment() function are not recorded.
1323
1324
1325 \page usage_patterns Recommended usage patterns
1326
1327 See also slides from talk:
1328 [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)
1329
1330
1331 \section usage_patterns_simple Simple patterns
1332
1333 \subsection usage_patterns_simple_render_targets Render targets
1334
1335 <b>When:</b>
1336 Any resources that you frequently write and read on GPU,
1337 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1338 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1339
1340 <b>What to do:</b>
1341 Create them in video memory that is fastest to access from GPU using
1342 #VMA_MEMORY_USAGE_GPU_ONLY.
1343
1344 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1345 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1346 especially if they are large or if you plan to destroy and recreate them e.g. when
1347 display resolution changes.
1348 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1349
1350 \subsection usage_patterns_simple_immutable_resources Immutable resources
1351
1352 <b>When:</b>
1353 Any resources that you fill on CPU only once (aka "immutable") or infrequently
1354 and then read frequently on GPU,
1355 e.g. textures, vertex and index buffers, constant buffers that don't change often.
1356
1357 <b>What to do:</b>
1358 Create them in video memory that is fastest to access from GPU using
1359 #VMA_MEMORY_USAGE_GPU_ONLY.
1360
1361 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1362 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1363 and submit a transfer from it to the GPU resource.
1364 You can keep the staging copy if you need it for another upload transfer in the future.
1365 If you don't, you can destroy it or reuse this buffer for uploading different resource
1366 after the transfer finishes.
1367
1368 Prefer to create just buffers in system memory rather than images, even for uploading textures.
1369 Use `vkCmdCopyBufferToImage()`.
1370 Dont use images with `VK_IMAGE_TILING_LINEAR`.
1371
1372 \subsection usage_patterns_dynamic_resources Dynamic resources
1373
1374 <b>When:</b>
1375 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1376 written on CPU, read on GPU.
1377
1378 <b>What to do:</b>
1379 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1380 You can map it and write to it directly on CPU, as well as read from it on GPU.
1381
1382 This is a more complex situation. Different solutions are possible,
1383 and the best one depends on specific GPU type, but you can use this simple approach for the start.
1384 Prefer to write to such resource sequentially (e.g. using `memcpy`).
1385 Don't perform random access or any reads from it on CPU, as it may be very slow.
1386
1387 \subsection usage_patterns_readback Readback
1388
1389 <b>When:</b>
1390 Resources that contain data written by GPU that you want to read back on CPU,
1391 e.g. results of some computations.
1392
1393 <b>What to do:</b>
1394 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1395 You can write to them directly on GPU, as well as map and read them on CPU.
1396
1397 \section usage_patterns_advanced Advanced patterns
1398
1399 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
1400
1401 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1402 by detecting it in Vulkan.
1403 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1404 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1405 When you find it, you can assume that memory is unified and all memory types are comparably fast
1406 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1407
1408 You can then sum up sizes of all available memory heaps and treat them as useful for
1409 your GPU resources, instead of only `DEVICE_LOCAL` ones.
1410 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1411 directly instead of submitting explicit transfer (see below).
1412
1413 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1414
1415 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1416
1417 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1418 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1419 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1420 read it directly on GPU.
1421 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1422 read it directly on GPU.
1423
1424 Which solution is the most efficient depends on your resource and especially on the GPU.
1425 It is best to measure it and then make the decision.
1426 Some general recommendations:
1427
1428 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1429 related to using a second copy and making transfer.
1430 - For small resources (e.g. constant buffers) use (2).
1431 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1432 Even if the resource ends up in system memory, its data may be cached on GPU after first
1433 fetch over PCIe bus.
1434 - For larger resources (e.g. textures), decide between (1) and (2).
1435 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1436 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1437
1438 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1439 solutions are possible:
1440
1441 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1442 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1443 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1444 map it and read it on CPU.
1445
1446 You should take some measurements to decide which option is faster in case of your specific
1447 resource.
1448
1449 If you don't want to specialize your code for specific types of GPUs, you can still make
1450 an simple optimization for cases when your resource ends up in mappable memory to use it
1451 directly in this case instead of creating CPU-side staging copy.
1452 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1453
1454
1455 \page configuration Configuration
1456
1457 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1458 before each include of this file or change directly in this file to provide
1459 your own implementation of basic facilities like assert, `min()` and `max()` functions,
1460 mutex, atomic etc.
1461 The library uses its own implementation of containers by default, but you can switch to using
1462 STL containers instead.
1463
1464 \section config_Vulkan_functions Pointers to Vulkan functions
1465
1466 The library uses Vulkan functions straight from the `vulkan.h` header by default.
1467 If you want to provide your own pointers to these functions, e.g. fetched using
1468 `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1469
1470 -# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1471 -# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1472
1473 \section custom_memory_allocator Custom host memory allocator
1474
1475 If you use custom allocator for CPU memory rather than default operator `new`
1476 and `delete` from C++, you can make this library using your allocator as well
1477 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1478 functions will be passed to Vulkan, as well as used by the library itself to
1479 make any CPU-side allocations.
1480
1481 \section allocation_callbacks Device memory allocation callbacks
1482
1483 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1484 You can setup callbacks to be informed about these calls, e.g. for the purpose
1485 of gathering some statistics. To do it, fill optional member
1486 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1487
1488 \section heap_memory_limit Device heap memory limit
1489
1490 If you want to test how your program behaves with limited amount of Vulkan device
1491 memory available without switching your graphics card to one that really has
1492 smaller VRAM, you can use a feature of this library intended for this purpose.
1493 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1494
1495
1496
1497 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1498
1499 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1500 performance on some GPUs. It augments Vulkan API with possibility to query
1501 driver whether it prefers particular buffer or image to have its own, dedicated
1502 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1503 to do some internal optimizations.
1504
1505 The extension is supported by this library. It will be used automatically when
1506 enabled. To enable it:
1507
1508 1 . When creating Vulkan device, check if following 2 device extensions are
1509 supported (call `vkEnumerateDeviceExtensionProperties()`).
1510 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1511
1512 - VK_KHR_get_memory_requirements2
1513 - VK_KHR_dedicated_allocation
1514
1515 If you enabled these extensions:
1516
1517 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1518 your #VmaAllocator`to inform the library that you enabled required extensions
1519 and you want the library to use them.
1520
1521 \code
1522 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1523
1524 vmaCreateAllocator(&allocatorInfo, &allocator);
1525 \endcode
1526
1527 That's all. The extension will be automatically used whenever you create a
1528 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1529
1530 When using the extension together with Vulkan Validation Layer, you will receive
1531 warnings like this:
1532
1533 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1534
1535 It is OK, you should just ignore it. It happens because you use function
1536 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1537 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1538 unaware of it.
1539
1540 To learn more about this extension, see:
1541
1542 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1543 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1544
1545
1546
1547 \page general_considerations General considerations
1548
1549 \section general_considerations_thread_safety Thread safety
1550
1551 - The library has no global state, so separate #VmaAllocator objects can be used
1552 independently.
1553 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1554 - By default, all calls to functions that take #VmaAllocator as first parameter
1555 are safe to call from multiple threads simultaneously because they are
1556 synchronized internally when needed.
1557 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1558 flag, calls to functions that take such #VmaAllocator object must be
1559 synchronized externally.
1560 - Access to a #VmaAllocation object must be externally synchronized. For example,
1561 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1562 threads at the same time if you pass the same #VmaAllocation object to these
1563 functions.
1564
1565 \section general_considerations_validation_layer_warnings Validation layer warnings
1566
1567 When using this library, you can meet following types of warnings issued by
1568 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1569 to just ignore them.
1570
1571 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1572 - It happens when VK_KHR_dedicated_allocation extension is enabled.
1573 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1574 - *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.*
1575 - It happens when you map a buffer or image, because the library maps entire
1576 `VkDeviceMemory` block, where different types of images and buffers may end
1577 up together, especially on GPUs with unified memory like Intel.
1578 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1579 - It happens when you use lost allocations, and a new image or buffer is
1580 created in place of an existing object that bacame lost.
1581 - It may happen also when you use [defragmentation](@ref defragmentation).
1582
1583 \section general_considerations_allocation_algorithm Allocation algorithm
1584
1585 The library uses following algorithm for allocation, in order:
1586
1587 -# Try to find free range of memory in existing blocks.
1588 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1589 -# If failed, try to create such block with size/2, size/4, size/8.
1590 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1591 specified, try to find space in existing blocks, possilby making some other
1592 allocations lost.
1593 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1594 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1595 -# If failed, choose other memory type that meets the requirements specified in
1596 VmaAllocationCreateInfo and go to point 1.
1597 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1598
1599 \section general_considerations_features_not_supported Features not supported
1600
1601 Features deliberately excluded from the scope of this library:
1602
1603 - Data transfer. Uploading (straming) and downloading data of buffers and images
1604 between CPU and GPU memory and related synchronization is responsibility of the user.
1605 - Allocations for imported/exported external memory. They tend to require
1606 explicit memory type index and dedicated allocation anyway, so they don't
1607 interact with main features of this library. Such special purpose allocations
1608 should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1609 - Recreation of buffers and images. Although the library has functions for
1610 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1611 recreate these objects yourself after defragmentation. That's because the big
1612 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1613 #VmaAllocation object.
1614 - Handling CPU memory allocation failures. When dynamically creating small C++
1615 objects in CPU memory (not Vulkan memory), allocation failures are not checked
1616 and handled gracefully, because that would complicate code significantly and
1617 is usually not needed in desktop PC applications anyway.
1618 - Code free of any compiler warnings. Maintaining the library to compile and
1619 work correctly on so many different platforms is hard enough. Being free of
1620 any warnings, on any version of any compiler, is simply not feasible.
1621 - This is a C++ library with C interface.
1622 Bindings or ports to any other programming languages are welcomed as external projects and
1623 are not going to be included into this repository.
1624
1625 */
1626
1627 /*
1628 Define this macro to 0/1 to disable/enable support for recording functionality,
1629 available through VmaAllocatorCreateInfo::pRecordSettings.
1630 */
1631 #ifndef VMA_RECORDING_ENABLED
1632 #ifdef _WIN32
1633 #define VMA_RECORDING_ENABLED 1
1634 #else
1635 #define VMA_RECORDING_ENABLED 0
1636 #endif
1637 #endif
1638
1639 #ifndef NOMINMAX
1640 #define NOMINMAX // For windows.h
1641 #endif
1642
1643 #ifndef VULKAN_H_
1644 #include <vulkan/vulkan.h>
1645 #endif
1646
1647 #if VMA_RECORDING_ENABLED
1648 #include <windows.h>
1649 #endif
1650
1651 #if !defined(VMA_DEDICATED_ALLOCATION)
1652 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1653 #define VMA_DEDICATED_ALLOCATION 1
1654 #else
1655 #define VMA_DEDICATED_ALLOCATION 0
1656 #endif
1657 #endif
1658
1659 /** \struct VmaAllocator
1660 \brief Represents main object of this library initialized.
1661
1662 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1663 Call function vmaDestroyAllocator() to destroy it.
1664
1665 It is recommended to create just one object of this type per `VkDevice` object,
1666 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1667 */
1668 VK_DEFINE_HANDLE(VmaAllocator)
1669
1670 /// Callback function called after successful vkAllocateMemory.
1671 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1672 VmaAllocator allocator,
1673 uint32_t memoryType,
1674 VkDeviceMemory memory,
1675 VkDeviceSize size);
1676 /// Callback function called before vkFreeMemory.
1677 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1678 VmaAllocator allocator,
1679 uint32_t memoryType,
1680 VkDeviceMemory memory,
1681 VkDeviceSize size);
1682
1683 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1684
1685 Provided for informative purpose, e.g. to gather statistics about number of
1686 allocations or total amount of memory allocated in Vulkan.
1687
1688 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1689 */
1690 typedef struct VmaDeviceMemoryCallbacks {
1691 /// Optional, can be null.
1692 PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1693 /// Optional, can be null.
1694 PFN_vmaFreeDeviceMemoryFunction pfnFree;
1695 } VmaDeviceMemoryCallbacks;
1696
1697 /// Flags for created #VmaAllocator.
1698 typedef enum VmaAllocatorCreateFlagBits {
1699 /** \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.
1700
1701 Using this flag may increase performance because internal mutexes are not used.
1702 */
1703 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1704 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1705
1706 Using this extenion will automatically allocate dedicated blocks of memory for
1707 some buffers and images instead of suballocating place for them out of bigger
1708 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1709 flag) when it is recommended by the driver. It may improve performance on some
1710 GPUs.
1711
1712 You may set this flag only if you found out that following device extensions are
1713 supported, you enabled them while creating Vulkan device passed as
1714 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1715 library:
1716
1717 - VK_KHR_get_memory_requirements2
1718 - VK_KHR_dedicated_allocation
1719
1720 When this flag is set, you can experience following warnings reported by Vulkan
1721 validation layer. You can ignore them.
1722
1723 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1724 */
1725 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1726
1727 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1728 } VmaAllocatorCreateFlagBits;
1729 typedef VkFlags VmaAllocatorCreateFlags;
1730
1731 /** \brief Pointers to some Vulkan functions - a subset used by the library.
1732
1733 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1734 */
1735 typedef struct VmaVulkanFunctions {
1736 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1737 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1738 PFN_vkAllocateMemory vkAllocateMemory;
1739 PFN_vkFreeMemory vkFreeMemory;
1740 PFN_vkMapMemory vkMapMemory;
1741 PFN_vkUnmapMemory vkUnmapMemory;
1742 PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1743 PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1744 PFN_vkBindBufferMemory vkBindBufferMemory;
1745 PFN_vkBindImageMemory vkBindImageMemory;
1746 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1747 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1748 PFN_vkCreateBuffer vkCreateBuffer;
1749 PFN_vkDestroyBuffer vkDestroyBuffer;
1750 PFN_vkCreateImage vkCreateImage;
1751 PFN_vkDestroyImage vkDestroyImage;
1752 PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1753 #if VMA_DEDICATED_ALLOCATION
1754 PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1755 PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1756 #endif
1757 } VmaVulkanFunctions;
1758
1759 /// Flags to be used in VmaRecordSettings::flags.
1760 typedef enum VmaRecordFlagBits {
1761 /** \brief Enables flush after recording every function call.
1762
1763 Enable it if you expect your application to crash, which may leave recording file truncated.
1764 It may degrade performance though.
1765 */
1766 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1767
1768 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1769 } VmaRecordFlagBits;
1770 typedef VkFlags VmaRecordFlags;
1771
1772 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1773 typedef struct VmaRecordSettings
1774 {
1775 /// Flags for recording. Use #VmaRecordFlagBits enum.
1776 VmaRecordFlags flags;
1777 /** \brief Path to the file that should be written by the recording.
1778
1779 Suggested extension: "csv".
1780 If the file already exists, it will be overwritten.
1781 It will be opened for the whole time #VmaAllocator object is alive.
1782 If opening this file fails, creation of the whole allocator object fails.
1783 */
1784 const char* pFilePath;
1785 } VmaRecordSettings;
1786
1787 /// Description of a Allocator to be created.
1788 typedef struct VmaAllocatorCreateInfo
1789 {
1790 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1791 VmaAllocatorCreateFlags flags;
1792 /// Vulkan physical device.
1793 /** It must be valid throughout whole lifetime of created allocator. */
1794 VkPhysicalDevice physicalDevice;
1795 /// Vulkan device.
1796 /** It must be valid throughout whole lifetime of created allocator. */
1797 VkDevice device;
1798 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1799 /** Set to 0 to use default, which is currently 256 MiB. */
1800 VkDeviceSize preferredLargeHeapBlockSize;
1801 /// Custom CPU memory allocation callbacks. Optional.
1802 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1803 const VkAllocationCallbacks* pAllocationCallbacks;
1804 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1805 /** Optional, can be null. */
1806 const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1807 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1808
1809 This value is used only when you make allocations with
1810 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1811 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1812
1813 For example, if you double-buffer your command buffers, so resources used for
1814 rendering in previous frame may still be in use by the GPU at the moment you
1815 allocate resources needed for the current frame, set this value to 1.
1816
1817 If you want to allow any allocations other than used in the current frame to
1818 become lost, set this value to 0.
1819 */
1820 uint32_t frameInUseCount;
1821 /** \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.
1822
1823 If not NULL, it must be a pointer to an array of
1824 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1825 maximum number of bytes that can be allocated out of particular Vulkan memory
1826 heap.
1827
1828 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1829 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1830
1831 If there is a limit defined for a heap:
1832
1833 - If user tries to allocate more memory from that heap using this allocator,
1834 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1835 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1836 value of this limit will be reported instead when using vmaGetMemoryProperties().
1837
1838 Warning! Using this feature may not be equivalent to installing a GPU with
1839 smaller amount of memory, because graphics driver doesn't necessary fail new
1840 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1841 exceeded. It may return success and just silently migrate some device memory
1842 blocks to system RAM. This driver behavior can also be controlled using
1843 VK_AMD_memory_overallocation_behavior extension.
1844 */
1845 const VkDeviceSize* pHeapSizeLimit;
1846 /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1847
1848 If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1849 you can pass null as this member, because the library will fetch pointers to
1850 Vulkan functions internally in a static way, like:
1851
1852 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1853
1854 Fill this member if you want to provide your own pointers to Vulkan functions,
1855 e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1856 */
1857 const VmaVulkanFunctions* pVulkanFunctions;
1858 /** \brief Parameters for recording of VMA calls. Can be null.
1859
1860 If not null, it enables recording of calls to VMA functions to a file.
1861 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1862 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1863 */
1864 const VmaRecordSettings* pRecordSettings;
1865 } VmaAllocatorCreateInfo;
1866
1867 /// Creates Allocator object.
1868 VkResult vmaCreateAllocator(
1869 const VmaAllocatorCreateInfo* pCreateInfo,
1870 VmaAllocator* pAllocator);
1871
1872 /// Destroys allocator object.
1873 void vmaDestroyAllocator(
1874 VmaAllocator allocator);
1875
1876 /**
1877 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1878 You can access it here, without fetching it again on your own.
1879 */
1880 void vmaGetPhysicalDeviceProperties(
1881 VmaAllocator allocator,
1882 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1883
1884 /**
1885 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1886 You can access it here, without fetching it again on your own.
1887 */
1888 void vmaGetMemoryProperties(
1889 VmaAllocator allocator,
1890 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1891
1892 /**
1893 \brief Given Memory Type Index, returns Property Flags of this memory type.
1894
1895 This is just a convenience function. Same information can be obtained using
1896 vmaGetMemoryProperties().
1897 */
1898 void vmaGetMemoryTypeProperties(
1899 VmaAllocator allocator,
1900 uint32_t memoryTypeIndex,
1901 VkMemoryPropertyFlags* pFlags);
1902
1903 /** \brief Sets index of the current frame.
1904
1905 This function must be used if you make allocations with
1906 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1907 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1908 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1909 become lost in the current frame.
1910 */
1911 void vmaSetCurrentFrameIndex(
1912 VmaAllocator allocator,
1913 uint32_t frameIndex);
1914
1915 /** \brief Calculated statistics of memory usage in entire allocator.
1916 */
1917 typedef struct VmaStatInfo
1918 {
1919 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1920 uint32_t blockCount;
1921 /// Number of #VmaAllocation allocation objects allocated.
1922 uint32_t allocationCount;
1923 /// Number of free ranges of memory between allocations.
1924 uint32_t unusedRangeCount;
1925 /// Total number of bytes occupied by all allocations.
1926 VkDeviceSize usedBytes;
1927 /// Total number of bytes occupied by unused ranges.
1928 VkDeviceSize unusedBytes;
1929 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1930 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1931 } VmaStatInfo;
1932
1933 /// General statistics from current state of Allocator.
1934 typedef struct VmaStats
1935 {
1936 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1937 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1938 VmaStatInfo total;
1939 } VmaStats;
1940
1941 /// Retrieves statistics from current state of the Allocator.
1942 void vmaCalculateStats(
1943 VmaAllocator allocator,
1944 VmaStats* pStats);
1945
1946 #define VMA_STATS_STRING_ENABLED 1
1947
1948 #if VMA_STATS_STRING_ENABLED
1949
1950 /// Builds and returns statistics as string in JSON format.
1951 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1952 */
1953 void vmaBuildStatsString(
1954 VmaAllocator allocator,
1955 char** ppStatsString,
1956 VkBool32 detailedMap);
1957
1958 void vmaFreeStatsString(
1959 VmaAllocator allocator,
1960 char* pStatsString);
1961
1962 #endif // #if VMA_STATS_STRING_ENABLED
1963
1964 /** \struct VmaPool
1965 \brief Represents custom memory pool
1966
1967 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1968 Call function vmaDestroyPool() to destroy it.
1969
1970 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1971 */
1972 VK_DEFINE_HANDLE(VmaPool)
1973
1974 typedef enum VmaMemoryUsage
1975 {
1976 /** No intended memory usage specified.
1977 Use other members of VmaAllocationCreateInfo to specify your requirements.
1978 */
1979 VMA_MEMORY_USAGE_UNKNOWN = 0,
1980 /** Memory will be used on device only, so fast access from the device is preferred.
1981 It usually means device-local GPU (video) memory.
1982 No need to be mappable on host.
1983 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1984
1985 Usage:
1986
1987 - Resources written and read by device, e.g. images used as attachments.
1988 - Resources transferred from host once (immutable) or infrequently and read by
1989 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1990 (constant) buffers, and majority of other types of resources used on GPU.
1991
1992 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1993 In such case, you are free to map it.
1994 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1995 */
1996 VMA_MEMORY_USAGE_GPU_ONLY = 1,
1997 /** Memory will be mappable on host.
1998 It usually means CPU (system) memory.
1999 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2000 CPU access is typically uncached. Writes may be write-combined.
2001 Resources created in this pool may still be accessible to the device, but access to them can be slow.
2002 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2003
2004 Usage: Staging copy of resources used as transfer source.
2005 */
2006 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2007 /**
2008 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2009 CPU access is typically uncached. Writes may be write-combined.
2010
2011 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
2012 */
2013 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2014 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2015 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2016
2017 Usage:
2018
2019 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2020 - 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.
2021 */
2022 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2023 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2024 } VmaMemoryUsage;
2025
2026 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2027 typedef enum VmaAllocationCreateFlagBits {
2028 /** \brief Set this flag if the allocation should have its own memory block.
2029
2030 Use it for special, big resources, like fullscreen images used as attachments.
2031
2032 This flag must also be used for host visible resources that you want to map
2033 simultaneously because otherwise they might end up as regions of the same
2034 `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2035 simultaneously is illegal.
2036
2037 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2038 */
2039 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2040
2041 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2042
2043 If new allocation cannot be placed in any of the existing blocks, allocation
2044 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2045
2046 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2047 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2048
2049 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2050 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2051 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2052
2053 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2054
2055 Is it valid to use this flag for allocation made from memory type that is not
2056 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2057 useful if you need an allocation that is efficient to use on GPU
2058 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2059 support it (e.g. Intel GPU).
2060
2061 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2062 */
2063 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2064 /** Allocation created with this flag can become lost as a result of another
2065 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2066 must check it before use.
2067
2068 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2069 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2070
2071 For details about supporting lost allocations, see Lost Allocations
2072 chapter of User Guide on Main Page.
2073
2074 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2075 */
2076 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2077 /** While creating allocation using this flag, other allocations that were
2078 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2079
2080 For details about supporting lost allocations, see Lost Allocations
2081 chapter of User Guide on Main Page.
2082 */
2083 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2084 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2085 null-terminated string. Instead of copying pointer value, a local copy of the
2086 string is made and stored in allocation's `pUserData`. The string is automatically
2087 freed together with the allocation. It is also used in vmaBuildStatsString().
2088 */
2089 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2090 /** Allocation will be created from upper stack in a double stack pool.
2091
2092 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2093 */
2094 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2095
2096 /** Allocation strategy that chooses smallest possible free range for the
2097 allocation.
2098 */
2099 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2100 /** Allocation strategy that chooses biggest possible free range for the
2101 allocation.
2102 */
2103 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2104 /** Allocation strategy that chooses first suitable free range for the
2105 allocation.
2106
2107 "First" doesn't necessarily means the one with smallest offset in memory,
2108 but rather the one that is easiest and fastest to find.
2109 */
2110 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2111
2112 /** Allocation strategy that tries to minimize memory usage.
2113 */
2114 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2115 /** Allocation strategy that tries to minimize allocation time.
2116 */
2117 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2118 /** Allocation strategy that tries to minimize memory fragmentation.
2119 */
2120 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2121
2122 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2123 */
2124 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2125 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2126 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2127 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2128
2129 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2130 } VmaAllocationCreateFlagBits;
2131 typedef VkFlags VmaAllocationCreateFlags;
2132
2133 typedef struct VmaAllocationCreateInfo
2134 {
2135 /// Use #VmaAllocationCreateFlagBits enum.
2136 VmaAllocationCreateFlags flags;
2137 /** \brief Intended usage of memory.
2138
2139 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2140 If `pool` is not null, this member is ignored.
2141 */
2142 VmaMemoryUsage usage;
2143 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2144
2145 Leave 0 if you specify memory requirements in other way. \n
2146 If `pool` is not null, this member is ignored.*/
2147 VkMemoryPropertyFlags requiredFlags;
2148 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2149
2150 Set to 0 if no additional flags are prefered. \n
2151 If `pool` is not null, this member is ignored. */
2152 VkMemoryPropertyFlags preferredFlags;
2153 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2154
2155 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2156 it meets other requirements specified by this structure, with no further
2157 restrictions on memory type index. \n
2158 If `pool` is not null, this member is ignored.
2159 */
2160 uint32_t memoryTypeBits;
2161 /** \brief Pool that this allocation should be created in.
2162
2163 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2164 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2165 */
2166 VmaPool pool;
2167 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2168
2169 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2170 null or pointer to a null-terminated string. The string will be then copied to
2171 internal buffer, so it doesn't need to be valid after allocation call.
2172 */
2173 void* pUserData;
2174 } VmaAllocationCreateInfo;
2175
2176 /**
2177 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2178
2179 This algorithm tries to find a memory type that:
2180
2181 - Is allowed by memoryTypeBits.
2182 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2183 - Matches intended usage.
2184 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2185
2186 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2187 from this function or any other allocating function probably means that your
2188 device doesn't support any memory type with requested features for the specific
2189 type of resource you want to use it for. Please check parameters of your
2190 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2191 */
2192 VkResult vmaFindMemoryTypeIndex(
2193 VmaAllocator allocator,
2194 uint32_t memoryTypeBits,
2195 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2196 uint32_t* pMemoryTypeIndex);
2197
2198 /**
2199 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2200
2201 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2202 It internally creates a temporary, dummy buffer that never has memory bound.
2203 It is just a convenience function, equivalent to calling:
2204
2205 - `vkCreateBuffer`
2206 - `vkGetBufferMemoryRequirements`
2207 - `vmaFindMemoryTypeIndex`
2208 - `vkDestroyBuffer`
2209 */
2210 VkResult vmaFindMemoryTypeIndexForBufferInfo(
2211 VmaAllocator allocator,
2212 const VkBufferCreateInfo* pBufferCreateInfo,
2213 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2214 uint32_t* pMemoryTypeIndex);
2215
2216 /**
2217 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2218
2219 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2220 It internally creates a temporary, dummy image that never has memory bound.
2221 It is just a convenience function, equivalent to calling:
2222
2223 - `vkCreateImage`
2224 - `vkGetImageMemoryRequirements`
2225 - `vmaFindMemoryTypeIndex`
2226 - `vkDestroyImage`
2227 */
2228 VkResult vmaFindMemoryTypeIndexForImageInfo(
2229 VmaAllocator allocator,
2230 const VkImageCreateInfo* pImageCreateInfo,
2231 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2232 uint32_t* pMemoryTypeIndex);
2233
2234 /// Flags to be passed as VmaPoolCreateInfo::flags.
2235 typedef enum VmaPoolCreateFlagBits {
2236 /** \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.
2237
2238 This is an optional optimization flag.
2239
2240 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2241 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2242 knows exact type of your allocations so it can handle Buffer-Image Granularity
2243 in the optimal way.
2244
2245 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2246 exact type of such allocations is not known, so allocator must be conservative
2247 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2248 (wasted memory). In that case, if you can make sure you always allocate only
2249 buffers and linear images or only optimal images out of this pool, use this flag
2250 to make allocator disregard Buffer-Image Granularity and so make allocations
2251 faster and more optimal.
2252 */
2253 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2254
2255 /** \brief Enables alternative, linear allocation algorithm in this pool.
2256
2257 Specify this flag to enable linear allocation algorithm, which always creates
2258 new allocations after last one and doesn't reuse space from allocations freed in
2259 between. It trades memory consumption for simplified algorithm and data
2260 structure, which has better performance and uses less memory for metadata.
2261
2262 By using this flag, you can achieve behavior of free-at-once, stack,
2263 ring buffer, and double stack. For details, see documentation chapter
2264 \ref linear_algorithm.
2265
2266 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2267
2268 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2269 */
2270 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2271
2272 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2273
2274 It operates on a tree of blocks, each having size that is a power of two and
2275 a half of its parent's size. Comparing to default algorithm, this one provides
2276 faster allocation and deallocation and decreased external fragmentation,
2277 at the expense of more memory wasted (internal fragmentation).
2278
2279 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2280 */
2281 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2282
2283 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2284 */
2285 VMA_POOL_CREATE_ALGORITHM_MASK =
2286 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2287 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2288
2289 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2290 } VmaPoolCreateFlagBits;
2291 typedef VkFlags VmaPoolCreateFlags;
2292
2293 /** \brief Describes parameter of created #VmaPool.
2294 */
2295 typedef struct VmaPoolCreateInfo {
2296 /** \brief Vulkan memory type index to allocate this pool from.
2297 */
2298 uint32_t memoryTypeIndex;
2299 /** \brief Use combination of #VmaPoolCreateFlagBits.
2300 */
2301 VmaPoolCreateFlags flags;
2302 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2303
2304 Specify nonzero to set explicit, constant size of memory blocks used by this
2305 pool.
2306
2307 Leave 0 to use default and let the library manage block sizes automatically.
2308 Sizes of particular blocks may vary.
2309 */
2310 VkDeviceSize blockSize;
2311 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2312
2313 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2314 */
2315 size_t minBlockCount;
2316 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2317
2318 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2319
2320 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2321 throughout whole lifetime of this pool.
2322 */
2323 size_t maxBlockCount;
2324 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2325
2326 This value is used only when you make allocations with
2327 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2328 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2329
2330 For example, if you double-buffer your command buffers, so resources used for
2331 rendering in previous frame may still be in use by the GPU at the moment you
2332 allocate resources needed for the current frame, set this value to 1.
2333
2334 If you want to allow any allocations other than used in the current frame to
2335 become lost, set this value to 0.
2336 */
2337 uint32_t frameInUseCount;
2338 } VmaPoolCreateInfo;
2339
2340 /** \brief Describes parameter of existing #VmaPool.
2341 */
2342 typedef struct VmaPoolStats {
2343 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2344 */
2345 VkDeviceSize size;
2346 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2347 */
2348 VkDeviceSize unusedSize;
2349 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2350 */
2351 size_t allocationCount;
2352 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2353 */
2354 size_t unusedRangeCount;
2355 /** \brief Size of the largest continuous free memory region available for new allocation.
2356
2357 Making a new allocation of that size is not guaranteed to succeed because of
2358 possible additional margin required to respect alignment and buffer/image
2359 granularity.
2360 */
2361 VkDeviceSize unusedRangeSizeMax;
2362 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2363 */
2364 size_t blockCount;
2365 } VmaPoolStats;
2366
2367 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2368
2369 @param allocator Allocator object.
2370 @param pCreateInfo Parameters of pool to create.
2371 @param[out] pPool Handle to created pool.
2372 */
2373 VkResult vmaCreatePool(
2374 VmaAllocator allocator,
2375 const VmaPoolCreateInfo* pCreateInfo,
2376 VmaPool* pPool);
2377
2378 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2379 */
2380 void vmaDestroyPool(
2381 VmaAllocator allocator,
2382 VmaPool pool);
2383
2384 /** \brief Retrieves statistics of existing #VmaPool object.
2385
2386 @param allocator Allocator object.
2387 @param pool Pool object.
2388 @param[out] pPoolStats Statistics of specified pool.
2389 */
2390 void vmaGetPoolStats(
2391 VmaAllocator allocator,
2392 VmaPool pool,
2393 VmaPoolStats* pPoolStats);
2394
2395 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2396
2397 @param allocator Allocator object.
2398 @param pool Pool.
2399 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2400 */
2401 void vmaMakePoolAllocationsLost(
2402 VmaAllocator allocator,
2403 VmaPool pool,
2404 size_t* pLostAllocationCount);
2405
2406 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2407
2408 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2409 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2410 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2411
2412 Possible return values:
2413
2414 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2415 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2416 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2417 `VMA_ASSERT` is also fired in that case.
2418 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2419 */
2420 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2421
2422 /** \struct VmaAllocation
2423 \brief Represents single memory allocation.
2424
2425 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2426 plus unique offset.
2427
2428 There are multiple ways to create such object.
2429 You need to fill structure VmaAllocationCreateInfo.
2430 For more information see [Choosing memory type](@ref choosing_memory_type).
2431
2432 Although the library provides convenience functions that create Vulkan buffer or image,
2433 allocate memory for it and bind them together,
2434 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2435 Allocation object can exist without buffer/image bound,
2436 binding can be done manually by the user, and destruction of it can be done
2437 independently of destruction of the allocation.
2438
2439 The object also remembers its size and some other information.
2440 To retrieve this information, use function vmaGetAllocationInfo() and inspect
2441 returned structure VmaAllocationInfo.
2442
2443 Some kinds allocations can be in lost state.
2444 For more information, see [Lost allocations](@ref lost_allocations).
2445 */
2446 VK_DEFINE_HANDLE(VmaAllocation)
2447
2448 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2449 */
2450 typedef struct VmaAllocationInfo {
2451 /** \brief Memory type index that this allocation was allocated from.
2452
2453 It never changes.
2454 */
2455 uint32_t memoryType;
2456 /** \brief Handle to Vulkan memory object.
2457
2458 Same memory object can be shared by multiple allocations.
2459
2460 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2461
2462 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2463 */
2464 VkDeviceMemory deviceMemory;
2465 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2466
2467 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2468 */
2469 VkDeviceSize offset;
2470 /** \brief Size of this allocation, in bytes.
2471
2472 It never changes, unless allocation is lost.
2473 */
2474 VkDeviceSize size;
2475 /** \brief Pointer to the beginning of this allocation as mapped data.
2476
2477 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2478 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2479
2480 It can change after call to vmaMapMemory(), vmaUnmapMemory().
2481 It can also change after call to vmaDefragment() if this allocation is passed to the function.
2482 */
2483 void* pMappedData;
2484 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2485
2486 It can change after call to vmaSetAllocationUserData() for this allocation.
2487 */
2488 void* pUserData;
2489 } VmaAllocationInfo;
2490
2491 /** \brief General purpose memory allocation.
2492
2493 @param[out] pAllocation Handle to allocated memory.
2494 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2495
2496 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2497
2498 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2499 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2500 */
2501 VkResult vmaAllocateMemory(
2502 VmaAllocator allocator,
2503 const VkMemoryRequirements* pVkMemoryRequirements,
2504 const VmaAllocationCreateInfo* pCreateInfo,
2505 VmaAllocation* pAllocation,
2506 VmaAllocationInfo* pAllocationInfo);
2507
2508 /** \brief General purpose memory allocation for multiple allocation objects at once.
2509
2510 @param allocator Allocator object.
2511 @param pVkMemoryRequirements Memory requirements for each allocation.
2512 @param pCreateInfo Creation parameters for each alloction.
2513 @param allocationCount Number of allocations to make.
2514 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2515 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2516
2517 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2518
2519 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2520 It is just a general purpose allocation function able to make multiple allocations at once.
2521 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2522
2523 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2524 If any allocation fails, all allocations already made within this function call are also freed, so that when
2525 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2526 */
2527 VkResult vmaAllocateMemoryPages(
2528 VmaAllocator allocator,
2529 const VkMemoryRequirements* pVkMemoryRequirements,
2530 const VmaAllocationCreateInfo* pCreateInfo,
2531 size_t allocationCount,
2532 VmaAllocation* pAllocations,
2533 VmaAllocationInfo* pAllocationInfo);
2534
2535 /**
2536 @param[out] pAllocation Handle to allocated memory.
2537 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2538
2539 You should free the memory using vmaFreeMemory().
2540 */
2541 VkResult vmaAllocateMemoryForBuffer(
2542 VmaAllocator allocator,
2543 VkBuffer buffer,
2544 const VmaAllocationCreateInfo* pCreateInfo,
2545 VmaAllocation* pAllocation,
2546 VmaAllocationInfo* pAllocationInfo);
2547
2548 /// Function similar to vmaAllocateMemoryForBuffer().
2549 VkResult vmaAllocateMemoryForImage(
2550 VmaAllocator allocator,
2551 VkImage image,
2552 const VmaAllocationCreateInfo* pCreateInfo,
2553 VmaAllocation* pAllocation,
2554 VmaAllocationInfo* pAllocationInfo);
2555
2556 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2557
2558 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2559 */
2560 void vmaFreeMemory(
2561 VmaAllocator allocator,
2562 VmaAllocation allocation);
2563
2564 /** \brief Frees memory and destroys multiple allocations.
2565
2566 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2567 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2568 vmaAllocateMemoryPages() and other functions.
2569 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2570
2571 Allocations in `pAllocations` array can come from any memory pools and types.
2572 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2573 */
2574 void vmaFreeMemoryPages(
2575 VmaAllocator allocator,
2576 size_t allocationCount,
2577 VmaAllocation* pAllocations);
2578
2579 /** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2580
2581 Tries to change allocation's size without moving or reallocating it.
2582 You can both shrink and grow allocation size.
2583 When growing, it succeeds only when the allocation belongs to a memory block with enough
2584 free space after it.
2585
2586 Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2587 Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2588
2589 After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2590 All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2591
2592 - Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2593 - Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2594 - Resizing dedicated allocations, as well as allocations created in pools that use linear
2595 or buddy algorithm, is not supported.
2596 The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2597 Support may be added in the future.
2598 */
2599 VkResult vmaResizeAllocation(
2600 VmaAllocator allocator,
2601 VmaAllocation allocation,
2602 VkDeviceSize newSize);
2603
2604 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2605
2606 Current paramters of given allocation are returned in `pAllocationInfo`.
2607
2608 This function also atomically "touches" allocation - marks it as used in current frame,
2609 just like vmaTouchAllocation().
2610 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2611
2612 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2613 you can avoid calling it too often.
2614
2615 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2616 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2617 (e.g. due to defragmentation or allocation becoming lost).
2618 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2619 */
2620 void vmaGetAllocationInfo(
2621 VmaAllocator allocator,
2622 VmaAllocation allocation,
2623 VmaAllocationInfo* pAllocationInfo);
2624
2625 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2626
2627 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2628 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2629 It then also atomically "touches" the allocation - marks it as used in current frame,
2630 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2631
2632 If the allocation is in lost state, the function returns `VK_FALSE`.
2633 Memory of such allocation, as well as buffer or image bound to it, should not be used.
2634 Lost allocation and the buffer/image still need to be destroyed.
2635
2636 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2637 this function always returns `VK_TRUE`.
2638 */
2639 VkBool32 vmaTouchAllocation(
2640 VmaAllocator allocator,
2641 VmaAllocation allocation);
2642
2643 /** \brief Sets pUserData in given allocation to new value.
2644
2645 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2646 pUserData must be either null, or pointer to a null-terminated string. The function
2647 makes local copy of the string and sets it as allocation's `pUserData`. String
2648 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2649 you can free it after this call. String previously pointed by allocation's
2650 pUserData is freed from memory.
2651
2652 If the flag was not used, the value of pointer `pUserData` is just copied to
2653 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2654 as a pointer, ordinal number or some handle to you own data.
2655 */
2656 void vmaSetAllocationUserData(
2657 VmaAllocator allocator,
2658 VmaAllocation allocation,
2659 void* pUserData);
2660
2661 /** \brief Creates new allocation that is in lost state from the beginning.
2662
2663 It can be useful if you need a dummy, non-null allocation.
2664
2665 You still need to destroy created object using vmaFreeMemory().
2666
2667 Returned allocation is not tied to any specific memory pool or memory type and
2668 not bound to any image or buffer. It has size = 0. It cannot be turned into
2669 a real, non-empty allocation.
2670 */
2671 void vmaCreateLostAllocation(
2672 VmaAllocator allocator,
2673 VmaAllocation* pAllocation);
2674
2675 /** \brief Maps memory represented by given allocation and returns pointer to it.
2676
2677 Maps memory represented by given allocation to make it accessible to CPU code.
2678 When succeeded, `*ppData` contains pointer to first byte of this memory.
2679 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2680 correctly offseted to the beginning of region assigned to this particular
2681 allocation.
2682
2683 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2684 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2685 multiple times simultaneously, it is safe to call this function on allocations
2686 assigned to the same memory block. Actual Vulkan memory will be mapped on first
2687 mapping and unmapped on last unmapping.
2688
2689 If the function succeeded, you must call vmaUnmapMemory() to unmap the
2690 allocation when mapping is no longer needed or before freeing the allocation, at
2691 the latest.
2692
2693 It also safe to call this function multiple times on the same allocation. You
2694 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2695
2696 It is also safe to call this function on allocation created with
2697 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2698 You must still call vmaUnmapMemory() same number of times as you called
2699 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2700 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2701
2702 This function fails when used on allocation made in memory type that is not
2703 `HOST_VISIBLE`.
2704
2705 This function always fails when called for allocation that was created with
2706 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2707 mapped.
2708 */
2709 VkResult vmaMapMemory(
2710 VmaAllocator allocator,
2711 VmaAllocation allocation,
2712 void** ppData);
2713
2714 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2715
2716 For details, see description of vmaMapMemory().
2717 */
2718 void vmaUnmapMemory(
2719 VmaAllocator allocator,
2720 VmaAllocation allocation);
2721
2722 /** \brief Flushes memory of given allocation.
2723
2724 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2725
2726 - `offset` must be relative to the beginning of allocation.
2727 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2728 - `offset` and `size` don't have to be aligned.
2729 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2730 - If `size` is 0, this call is ignored.
2731 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2732 this call is ignored.
2733 */
2734 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2735
2736 /** \brief Invalidates memory of given allocation.
2737
2738 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2739
2740 - `offset` must be relative to the beginning of allocation.
2741 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2742 - `offset` and `size` don't have to be aligned.
2743 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2744 - If `size` is 0, this call is ignored.
2745 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2746 this call is ignored.
2747 */
2748 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2749
2750 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2751
2752 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2753
2754 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2755 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2756 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2757
2758 Possible return values:
2759
2760 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2761 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2762 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2763 `VMA_ASSERT` is also fired in that case.
2764 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2765 */
2766 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2767
2768 /** \struct VmaDefragmentationContext
2769 \brief Represents Opaque object that represents started defragmentation process.
2770
2771 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2772 Call function vmaDefragmentationEnd() to destroy it.
2773 */
2774 VK_DEFINE_HANDLE(VmaDefragmentationContext)
2775
2776 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2777 typedef enum VmaDefragmentationFlagBits {
2778 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2779 } VmaDefragmentationFlagBits;
2780 typedef VkFlags VmaDefragmentationFlags;
2781
2782 /** \brief Parameters for defragmentation.
2783
2784 To be used with function vmaDefragmentationBegin().
2785 */
2786 typedef struct VmaDefragmentationInfo2 {
2787 /** \brief Reserved for future use. Should be 0.
2788 */
2789 VmaDefragmentationFlags flags;
2790 /** \brief Number of allocations in `pAllocations` array.
2791 */
2792 uint32_t allocationCount;
2793 /** \brief Pointer to array of allocations that can be defragmented.
2794
2795 The array should have `allocationCount` elements.
2796 The array should not contain nulls.
2797 Elements in the array should be unique - same allocation cannot occur twice.
2798 It is safe to pass allocations that are in the lost state - they are ignored.
2799 All allocations not present in this array are considered non-moveable during this defragmentation.
2800 */
2801 VmaAllocation* pAllocations;
2802 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2803
2804 The array should have `allocationCount` elements.
2805 You can pass null if you are not interested in this information.
2806 */
2807 VkBool32* pAllocationsChanged;
2808 /** \brief Numer of pools in `pPools` array.
2809 */
2810 uint32_t poolCount;
2811 /** \brief Either null or pointer to array of pools to be defragmented.
2812
2813 All the allocations in the specified pools can be moved during defragmentation
2814 and there is no way to check if they were really moved as in `pAllocationsChanged`,
2815 so you must query all the allocations in all these pools for new `VkDeviceMemory`
2816 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2817 and images bound to them.
2818
2819 The array should have `poolCount` elements.
2820 The array should not contain nulls.
2821 Elements in the array should be unique - same pool cannot occur twice.
2822
2823 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2824 It might be more efficient.
2825 */
2826 VmaPool* pPools;
2827 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2828
2829 `VK_WHOLE_SIZE` means no limit.
2830 */
2831 VkDeviceSize maxCpuBytesToMove;
2832 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2833
2834 `UINT32_MAX` means no limit.
2835 */
2836 uint32_t maxCpuAllocationsToMove;
2837 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2838
2839 `VK_WHOLE_SIZE` means no limit.
2840 */
2841 VkDeviceSize maxGpuBytesToMove;
2842 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2843
2844 `UINT32_MAX` means no limit.
2845 */
2846 uint32_t maxGpuAllocationsToMove;
2847 /** \brief Optional. Command buffer where GPU copy commands will be posted.
2848
2849 If not null, it must be a valid command buffer handle that supports Transfer queue type.
2850 It must be in the recording state and outside of a render pass instance.
2851 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2852
2853 Passing null means that only CPU defragmentation will be performed.
2854 */
2855 VkCommandBuffer commandBuffer;
2856 } VmaDefragmentationInfo2;
2857
2858 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2859
2860 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2861 */
2862 typedef struct VmaDefragmentationInfo {
2863 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2864
2865 Default is `VK_WHOLE_SIZE`, which means no limit.
2866 */
2867 VkDeviceSize maxBytesToMove;
2868 /** \brief Maximum number of allocations that can be moved to different place.
2869
2870 Default is `UINT32_MAX`, which means no limit.
2871 */
2872 uint32_t maxAllocationsToMove;
2873 } VmaDefragmentationInfo;
2874
2875 /** \brief Statistics returned by function vmaDefragment(). */
2876 typedef struct VmaDefragmentationStats {
2877 /// Total number of bytes that have been copied while moving allocations to different places.
2878 VkDeviceSize bytesMoved;
2879 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2880 VkDeviceSize bytesFreed;
2881 /// Number of allocations that have been moved to different places.
2882 uint32_t allocationsMoved;
2883 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2884 uint32_t deviceMemoryBlocksFreed;
2885 } VmaDefragmentationStats;
2886
2887 /** \brief Begins defragmentation process.
2888
2889 @param allocator Allocator object.
2890 @param pInfo Structure filled with parameters of defragmentation.
2891 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2892 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2893 @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.
2894
2895 Use this function instead of old, deprecated vmaDefragment().
2896
2897 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2898
2899 - You should not use any of allocations passed as `pInfo->pAllocations` or
2900 any allocations that belong to pools passed as `pInfo->pPools`,
2901 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2902 their data.
2903 - Some mutexes protecting internal data structures may be locked, so trying to
2904 make or free any allocations, bind buffers or images, map memory, or launch
2905 another simultaneous defragmentation in between may cause stall (when done on
2906 another thread) or deadlock (when done on the same thread), unless you are
2907 100% sure that defragmented allocations are in different pools.
2908 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2909 They become valid after call to vmaDefragmentationEnd().
2910 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
2911 and make sure it finished execution before calling vmaDefragmentationEnd().
2912 */
2913 VkResult vmaDefragmentationBegin(
2914 VmaAllocator allocator,
2915 const VmaDefragmentationInfo2* pInfo,
2916 VmaDefragmentationStats* pStats,
2917 VmaDefragmentationContext *pContext);
2918
2919 /** \brief Ends defragmentation process.
2920
2921 Use this function to finish defragmentation started by vmaDefragmentationBegin().
2922 It is safe to pass `context == null`. The function then does nothing.
2923 */
2924 VkResult vmaDefragmentationEnd(
2925 VmaAllocator allocator,
2926 VmaDefragmentationContext context);
2927
2928 /** \brief Deprecated. Compacts memory by moving allocations.
2929
2930 @param pAllocations Array of allocations that can be moved during this compation.
2931 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2932 @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.
2933 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2934 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2935 @return `VK_SUCCESS` if completed, negative error code in case of error.
2936
2937 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2938
2939 This function works by moving allocations to different places (different
2940 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2941 usage. Only allocations that are in `pAllocations` array can be moved. All other
2942 allocations are considered nonmovable in this call. Basic rules:
2943
2944 - Only allocations made in memory types that have
2945 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2946 flags can be compacted. You may pass other allocations but it makes no sense -
2947 these will never be moved.
2948 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2949 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2950 passed to this function that come from such pools are ignored.
2951 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2952 created as dedicated allocations for any other reason are also ignored.
2953 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2954 flag can be compacted. If not persistently mapped, memory will be mapped
2955 temporarily inside this function if needed.
2956 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2957
2958 The function also frees empty `VkDeviceMemory` blocks.
2959
2960 Warning: This function may be time-consuming, so you shouldn't call it too often
2961 (like after every resource creation/destruction).
2962 You can call it on special occasions (like when reloading a game level or
2963 when you just destroyed a lot of objects). Calling it every frame may be OK, but
2964 you should measure that on your platform.
2965
2966 For more information, see [Defragmentation](@ref defragmentation) chapter.
2967 */
2968 VkResult vmaDefragment(
2969 VmaAllocator allocator,
2970 VmaAllocation* pAllocations,
2971 size_t allocationCount,
2972 VkBool32* pAllocationsChanged,
2973 const VmaDefragmentationInfo *pDefragmentationInfo,
2974 VmaDefragmentationStats* pDefragmentationStats);
2975
2976 /** \brief Binds buffer to allocation.
2977
2978 Binds specified buffer to region of memory represented by specified allocation.
2979 Gets `VkDeviceMemory` handle and offset from the allocation.
2980 If you want to create a buffer, allocate memory for it and bind them together separately,
2981 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2982 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2983 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2984 (which is illegal in Vulkan).
2985
2986 It is recommended to use function vmaCreateBuffer() instead of this one.
2987 */
2988 VkResult vmaBindBufferMemory(
2989 VmaAllocator allocator,
2990 VmaAllocation allocation,
2991 VkBuffer buffer);
2992
2993 /** \brief Binds image to allocation.
2994
2995 Binds specified image to region of memory represented by specified allocation.
2996 Gets `VkDeviceMemory` handle and offset from the allocation.
2997 If you want to create an image, allocate memory for it and bind them together separately,
2998 you should use this function for binding instead of standard `vkBindImageMemory()`,
2999 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3000 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3001 (which is illegal in Vulkan).
3002
3003 It is recommended to use function vmaCreateImage() instead of this one.
3004 */
3005 VkResult vmaBindImageMemory(
3006 VmaAllocator allocator,
3007 VmaAllocation allocation,
3008 VkImage image);
3009
3010 /**
3011 @param[out] pBuffer Buffer that was created.
3012 @param[out] pAllocation Allocation that was created.
3013 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3014
3015 This function automatically:
3016
3017 -# Creates buffer.
3018 -# Allocates appropriate memory for it.
3019 -# Binds the buffer with the memory.
3020
3021 If any of these operations fail, buffer and allocation are not created,
3022 returned value is negative error code, *pBuffer and *pAllocation are null.
3023
3024 If the function succeeded, you must destroy both buffer and allocation when you
3025 no longer need them using either convenience function vmaDestroyBuffer() or
3026 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3027
3028 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3029 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3030 it requires or prefers the new buffer to have dedicated allocation. If yes,
3031 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3032 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3033 allocation for this buffer, just like when using
3034 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3035 */
3036 VkResult vmaCreateBuffer(
3037 VmaAllocator allocator,
3038 const VkBufferCreateInfo* pBufferCreateInfo,
3039 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3040 VkBuffer* pBuffer,
3041 VmaAllocation* pAllocation,
3042 VmaAllocationInfo* pAllocationInfo);
3043
3044 /** \brief Destroys Vulkan buffer and frees allocated memory.
3045
3046 This is just a convenience function equivalent to:
3047
3048 \code
3049 vkDestroyBuffer(device, buffer, allocationCallbacks);
3050 vmaFreeMemory(allocator, allocation);
3051 \endcode
3052
3053 It it safe to pass null as buffer and/or allocation.
3054 */
3055 void vmaDestroyBuffer(
3056 VmaAllocator allocator,
3057 VkBuffer buffer,
3058 VmaAllocation allocation);
3059
3060 /// Function similar to vmaCreateBuffer().
3061 VkResult vmaCreateImage(
3062 VmaAllocator allocator,
3063 const VkImageCreateInfo* pImageCreateInfo,
3064 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3065 VkImage* pImage,
3066 VmaAllocation* pAllocation,
3067 VmaAllocationInfo* pAllocationInfo);
3068
3069 /** \brief Destroys Vulkan image and frees allocated memory.
3070
3071 This is just a convenience function equivalent to:
3072
3073 \code
3074 vkDestroyImage(device, image, allocationCallbacks);
3075 vmaFreeMemory(allocator, allocation);
3076 \endcode
3077
3078 It it safe to pass null as image and/or allocation.
3079 */
3080 void vmaDestroyImage(
3081 VmaAllocator allocator,
3082 VkImage image,
3083 VmaAllocation allocation);
3084
3085 #ifdef __cplusplus
3086 }
3087 #endif
3088
3089 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3090
3091 // For Visual Studio IntelliSense.
3092 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3093 #define VMA_IMPLEMENTATION
3094 #endif
3095
3096 #ifdef VMA_IMPLEMENTATION
3097 #undef VMA_IMPLEMENTATION
3098
3099 #include <cstdint>
3100 #include <cstdlib>
3101 #include <cstring>
3102
3103 /*******************************************************************************
3104 CONFIGURATION SECTION
3105
3106 Define some of these macros before each #include of this header or change them
3107 here if you need other then default behavior depending on your environment.
3108 */
3109
3110 /*
3111 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3112 internally, like:
3113
3114 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3115
3116 Define to 0 if you are going to provide you own pointers to Vulkan functions via
3117 VmaAllocatorCreateInfo::pVulkanFunctions.
3118 */
3119 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3120 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3121 #endif
3122
3123 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3124 //#define VMA_USE_STL_CONTAINERS 1
3125
3126 /* Set this macro to 1 to make the library including and using STL containers:
3127 std::pair, std::vector, std::list, std::unordered_map.
3128
3129 Set it to 0 or undefined to make the library using its own implementation of
3130 the containers.
3131 */
3132 #if VMA_USE_STL_CONTAINERS
3133 #define VMA_USE_STL_VECTOR 1
3134 #define VMA_USE_STL_UNORDERED_MAP 1
3135 #define VMA_USE_STL_LIST 1
3136 #endif
3137
3138 #ifndef VMA_USE_STL_SHARED_MUTEX
3139 // Minimum Visual Studio 2015 Update 2
3140 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && NTDDI_VERSION > NTDDI_WIN10_RS2
3141 #define VMA_USE_STL_SHARED_MUTEX 1
3142 #endif
3143 #endif
3144
3145 #if VMA_USE_STL_VECTOR
3146 #include <vector>
3147 #endif
3148
3149 #if VMA_USE_STL_UNORDERED_MAP
3150 #include <unordered_map>
3151 #endif
3152
3153 #if VMA_USE_STL_LIST
3154 #include <list>
3155 #endif
3156
3157 /*
3158 Following headers are used in this CONFIGURATION section only, so feel free to
3159 remove them if not needed.
3160 */
3161 #include <cassert> // for assert
3162 #include <algorithm> // for min, max
3163 #include <mutex>
3164 #include <atomic> // for std::atomic
3165
3166 #ifndef VMA_NULL
3167 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3168 #define VMA_NULL nullptr
3169 #endif
3170
3171 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3172 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3173 void *aligned_alloc(size_t alignment, size_t size)
3174 {
3175 // alignment must be >= sizeof(void*)
3176 if(alignment < sizeof(void*))
3177 {
3178 alignment = sizeof(void*);
3179 }
3180
3181 return memalign(alignment, size);
3182 }
3183 #elif defined(__APPLE__) || defined(__ANDROID__)
3184 # define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3185 #elif defined(__GNU_LIBRARY__)
3186 # if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 16)
3187 // aligned_alloc() is defined in glibc only for version >= 2.16
3188 # define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3189 # endif
3190 #endif
3191
3192 #ifdef ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3193 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3194 void *aligned_alloc(size_t alignment, size_t size)
3195 {
3196 // alignment must be >= sizeof(void*)
3197 if(alignment < sizeof(void*))
3198 {
3199 alignment = sizeof(void*);
3200 }
3201
3202 void *pointer;
3203 if(posix_memalign(&pointer, alignment, size) == 0)
3204 return pointer;
3205 return VMA_NULL;
3206 }
3207 #endif
3208
3209 // If your compiler is not compatible with C++11 and definition of
3210 // aligned_alloc() function is missing, uncommeting following line may help:
3211
3212 //#include <malloc.h>
3213
3214 // Normal assert to check for programmer's errors, especially in Debug configuration.
3215 #ifndef VMA_ASSERT
3216 #ifdef _DEBUG
3217 #define VMA_ASSERT(expr) assert(expr)
3218 #else
3219 #define VMA_ASSERT(expr)
3220 #endif
3221 #endif
3222
3223 // Assert that will be called very often, like inside data structures e.g. operator[].
3224 // Making it non-empty can make program slow.
3225 #ifndef VMA_HEAVY_ASSERT
3226 #ifdef _DEBUG
3227 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3228 #else
3229 #define VMA_HEAVY_ASSERT(expr)
3230 #endif
3231 #endif
3232
3233 #ifndef VMA_ALIGN_OF
3234 #define VMA_ALIGN_OF(type) (__alignof(type))
3235 #endif
3236
3237 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3238 #if defined(_WIN32)
3239 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3240 #else
3241 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3242 #endif
3243 #endif
3244
3245 #ifndef VMA_SYSTEM_FREE
3246 #if defined(_WIN32)
3247 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
3248 #else
3249 #define VMA_SYSTEM_FREE(ptr) free(ptr)
3250 #endif
3251 #endif
3252
3253 #ifndef VMA_MIN
3254 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
3255 #endif
3256
3257 #ifndef VMA_MAX
3258 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
3259 #endif
3260
3261 #ifndef VMA_SWAP
3262 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
3263 #endif
3264
3265 #ifndef VMA_SORT
3266 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3267 #endif
3268
3269 #ifndef VMA_DEBUG_LOG
3270 #define VMA_DEBUG_LOG(format, ...)
3271 /*
3272 #define VMA_DEBUG_LOG(format, ...) do { \
3273 printf(format, __VA_ARGS__); \
3274 printf("\n"); \
3275 } while(false)
3276 */
3277 #endif
3278
3279 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3280 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)3281 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3282 {
3283 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3284 }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)3285 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3286 {
3287 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3288 }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)3289 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3290 {
3291 snprintf(outStr, strLen, "%p", ptr);
3292 }
3293 #endif
3294
3295 #ifndef VMA_MUTEX
3296 class VmaMutex
3297 {
3298 public:
Lock()3299 void Lock() { m_Mutex.lock(); }
Unlock()3300 void Unlock() { m_Mutex.unlock(); }
3301 private:
3302 std::mutex m_Mutex;
3303 };
3304 #define VMA_MUTEX VmaMutex
3305 #endif
3306
3307 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
3308 #ifndef VMA_RW_MUTEX
3309 #if VMA_USE_STL_SHARED_MUTEX
3310 // Use std::shared_mutex from C++17.
3311 #include <shared_mutex>
3312 class VmaRWMutex
3313 {
3314 public:
LockRead()3315 void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()3316 void UnlockRead() { m_Mutex.unlock_shared(); }
LockWrite()3317 void LockWrite() { m_Mutex.lock(); }
UnlockWrite()3318 void UnlockWrite() { m_Mutex.unlock(); }
3319 private:
3320 std::shared_mutex m_Mutex;
3321 };
3322 #define VMA_RW_MUTEX VmaRWMutex
3323 #elif defined(_WIN32)
3324 // Use SRWLOCK from WinAPI.
3325 class VmaRWMutex
3326 {
3327 public:
VmaRWMutex()3328 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()3329 void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()3330 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
LockWrite()3331 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()3332 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3333 private:
3334 SRWLOCK m_Lock;
3335 };
3336 #define VMA_RW_MUTEX VmaRWMutex
3337 #else
3338 // Less efficient fallback: Use normal mutex.
3339 class VmaRWMutex
3340 {
3341 public:
LockRead()3342 void LockRead() { m_Mutex.Lock(); }
UnlockRead()3343 void UnlockRead() { m_Mutex.Unlock(); }
LockWrite()3344 void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()3345 void UnlockWrite() { m_Mutex.Unlock(); }
3346 private:
3347 VMA_MUTEX m_Mutex;
3348 };
3349 #define VMA_RW_MUTEX VmaRWMutex
3350 #endif // #if VMA_USE_STL_SHARED_MUTEX
3351 #endif // #ifndef VMA_RW_MUTEX
3352
3353 /*
3354 If providing your own implementation, you need to implement a subset of std::atomic:
3355
3356 - Constructor(uint32_t desired)
3357 - uint32_t load() const
3358 - void store(uint32_t desired)
3359 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3360 */
3361 #ifndef VMA_ATOMIC_UINT32
3362 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3363 #endif
3364
3365 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3366 /**
3367 Every allocation will have its own memory block.
3368 Define to 1 for debugging purposes only.
3369 */
3370 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3371 #endif
3372
3373 #ifndef VMA_DEBUG_ALIGNMENT
3374 /**
3375 Minimum alignment of all allocations, in bytes.
3376 Set to more than 1 for debugging purposes only. Must be power of two.
3377 */
3378 #define VMA_DEBUG_ALIGNMENT (1)
3379 #endif
3380
3381 #ifndef VMA_DEBUG_MARGIN
3382 /**
3383 Minimum margin before and after every allocation, in bytes.
3384 Set nonzero for debugging purposes only.
3385 */
3386 #define VMA_DEBUG_MARGIN (0)
3387 #endif
3388
3389 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3390 /**
3391 Define this macro to 1 to automatically fill new allocations and destroyed
3392 allocations with some bit pattern.
3393 */
3394 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3395 #endif
3396
3397 #ifndef VMA_DEBUG_DETECT_CORRUPTION
3398 /**
3399 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3400 enable writing magic value to the margin before and after every allocation and
3401 validating it, so that memory corruptions (out-of-bounds writes) are detected.
3402 */
3403 #define VMA_DEBUG_DETECT_CORRUPTION (0)
3404 #endif
3405
3406 #ifndef VMA_DEBUG_GLOBAL_MUTEX
3407 /**
3408 Set this to 1 for debugging purposes only, to enable single mutex protecting all
3409 entry calls to the library. Can be useful for debugging multithreading issues.
3410 */
3411 #define VMA_DEBUG_GLOBAL_MUTEX (0)
3412 #endif
3413
3414 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3415 /**
3416 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3417 Set to more than 1 for debugging purposes only. Must be power of two.
3418 */
3419 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3420 #endif
3421
3422 #ifndef VMA_SMALL_HEAP_MAX_SIZE
3423 /// Maximum size of a memory heap in Vulkan to consider it "small".
3424 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3425 #endif
3426
3427 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3428 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3429 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3430 #endif
3431
3432 #ifndef VMA_CLASS_NO_COPY
3433 #define VMA_CLASS_NO_COPY(className) \
3434 private: \
3435 className(const className&) = delete; \
3436 className& operator=(const className&) = delete;
3437 #endif
3438
3439 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3440
3441 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3442 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3443
3444 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3445 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3446
3447 /*******************************************************************************
3448 END OF CONFIGURATION
3449 */
3450
3451 #if defined(__GNUC__)
3452 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
3453 #pragma GCC diagnostic push
3454 #pragma GCC diagnostic ignored "-Wtype-limits"
3455 #pragma GCC diagnostic ignored "-Wunused-variable"
3456 #if defined(__clang__)
3457 #pragma clang diagnostic push
3458 #pragma clang diagnostic ignored "-Wtautological-compare"
3459 #endif
3460 #if GCC_VERSION >= 80000
3461 #pragma GCC diagnostic ignored "-Wclass-memaccess"
3462 #endif
3463 #if defined(ANDROID)
3464 #pragma GCC diagnostic ignored "-Wunused-private-field"
3465 #endif
3466 #endif
3467 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3468
3469 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3470 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3471
3472 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)3473 static inline uint32_t VmaCountBitsSet(uint32_t v)
3474 {
3475 uint32_t c = v - ((v >> 1) & 0x55555555);
3476 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3477 c = ((c >> 4) + c) & 0x0F0F0F0F;
3478 c = ((c >> 8) + c) & 0x00FF00FF;
3479 c = ((c >> 16) + c) & 0x0000FFFF;
3480 return c;
3481 }
3482
3483 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3484 // Use types like uint32_t, uint64_t as T.
3485 template <typename T>
VmaAlignUp(T val,T align)3486 static inline T VmaAlignUp(T val, T align)
3487 {
3488 return (val + align - 1) / align * align;
3489 }
3490 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3491 // Use types like uint32_t, uint64_t as T.
3492 template <typename T>
VmaAlignDown(T val,T align)3493 static inline T VmaAlignDown(T val, T align)
3494 {
3495 return val / align * align;
3496 }
3497
3498 // Division with mathematical rounding to nearest number.
3499 template <typename T>
VmaRoundDiv(T x,T y)3500 static inline T VmaRoundDiv(T x, T y)
3501 {
3502 return (x + (y / (T)2)) / y;
3503 }
3504
3505 /*
3506 Returns true if given number is a power of two.
3507 T must be unsigned integer number or signed integer but always nonnegative.
3508 For 0 returns true.
3509 */
3510 template <typename T>
VmaIsPow2(T x)3511 inline bool VmaIsPow2(T x)
3512 {
3513 return (x & (x-1)) == 0;
3514 }
3515
3516 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)3517 static inline uint32_t VmaNextPow2(uint32_t v)
3518 {
3519 v--;
3520 v |= v >> 1;
3521 v |= v >> 2;
3522 v |= v >> 4;
3523 v |= v >> 8;
3524 v |= v >> 16;
3525 v++;
3526 return v;
3527 }
VmaNextPow2(uint64_t v)3528 static inline uint64_t VmaNextPow2(uint64_t v)
3529 {
3530 v--;
3531 v |= v >> 1;
3532 v |= v >> 2;
3533 v |= v >> 4;
3534 v |= v >> 8;
3535 v |= v >> 16;
3536 v |= v >> 32;
3537 v++;
3538 return v;
3539 }
3540
3541 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)3542 static inline uint32_t VmaPrevPow2(uint32_t v)
3543 {
3544 v |= v >> 1;
3545 v |= v >> 2;
3546 v |= v >> 4;
3547 v |= v >> 8;
3548 v |= v >> 16;
3549 v = v ^ (v >> 1);
3550 return v;
3551 }
VmaPrevPow2(uint64_t v)3552 static inline uint64_t VmaPrevPow2(uint64_t v)
3553 {
3554 v |= v >> 1;
3555 v |= v >> 2;
3556 v |= v >> 4;
3557 v |= v >> 8;
3558 v |= v >> 16;
3559 v |= v >> 32;
3560 v = v ^ (v >> 1);
3561 return v;
3562 }
3563
VmaStrIsEmpty(const char * pStr)3564 static inline bool VmaStrIsEmpty(const char* pStr)
3565 {
3566 return pStr == VMA_NULL || *pStr == '\0';
3567 }
3568
VmaAlgorithmToStr(uint32_t algorithm)3569 static const char* VmaAlgorithmToStr(uint32_t algorithm)
3570 {
3571 switch(algorithm)
3572 {
3573 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3574 return "Linear";
3575 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3576 return "Buddy";
3577 case 0:
3578 return "Default";
3579 default:
3580 VMA_ASSERT(0);
3581 return "";
3582 }
3583 }
3584
3585 #ifndef VMA_SORT
3586
3587 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)3588 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3589 {
3590 Iterator centerValue = end; --centerValue;
3591 Iterator insertIndex = beg;
3592 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3593 {
3594 if(cmp(*memTypeIndex, *centerValue))
3595 {
3596 if(insertIndex != memTypeIndex)
3597 {
3598 VMA_SWAP(*memTypeIndex, *insertIndex);
3599 }
3600 ++insertIndex;
3601 }
3602 }
3603 if(insertIndex != centerValue)
3604 {
3605 VMA_SWAP(*insertIndex, *centerValue);
3606 }
3607 return insertIndex;
3608 }
3609
3610 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)3611 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3612 {
3613 if(beg < end)
3614 {
3615 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3616 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3617 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3618 }
3619 }
3620
3621 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3622
3623 #endif // #ifndef VMA_SORT
3624
3625 /*
3626 Returns true if two memory blocks occupy overlapping pages.
3627 ResourceA must be in less memory offset than ResourceB.
3628
3629 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3630 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3631 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)3632 static inline bool VmaBlocksOnSamePage(
3633 VkDeviceSize resourceAOffset,
3634 VkDeviceSize resourceASize,
3635 VkDeviceSize resourceBOffset,
3636 VkDeviceSize pageSize)
3637 {
3638 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3639 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3640 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3641 VkDeviceSize resourceBStart = resourceBOffset;
3642 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3643 return resourceAEndPage == resourceBStartPage;
3644 }
3645
3646 enum VmaSuballocationType
3647 {
3648 VMA_SUBALLOCATION_TYPE_FREE = 0,
3649 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3650 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3651 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3652 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3653 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3654 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3655 };
3656
3657 /*
3658 Returns true if given suballocation types could conflict and must respect
3659 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3660 or linear image and another one is optimal image. If type is unknown, behave
3661 conservatively.
3662 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3663 static inline bool VmaIsBufferImageGranularityConflict(
3664 VmaSuballocationType suballocType1,
3665 VmaSuballocationType suballocType2)
3666 {
3667 if(suballocType1 > suballocType2)
3668 {
3669 VMA_SWAP(suballocType1, suballocType2);
3670 }
3671
3672 switch(suballocType1)
3673 {
3674 case VMA_SUBALLOCATION_TYPE_FREE:
3675 return false;
3676 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3677 return true;
3678 case VMA_SUBALLOCATION_TYPE_BUFFER:
3679 return
3680 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3681 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3682 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3683 return
3684 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3685 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3686 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3687 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3688 return
3689 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3690 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3691 return false;
3692 default:
3693 VMA_ASSERT(0);
3694 return true;
3695 }
3696 }
3697
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3698 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3699 {
3700 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3701 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3702 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3703 for(size_t i = 0; i < numberCount; ++i, ++pDst)
3704 {
3705 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3706 }
3707 #else
3708 // no-op
3709 #endif
3710 }
3711
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3712 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3713 {
3714 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3715 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3716 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3717 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3718 {
3719 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3720 {
3721 return false;
3722 }
3723 }
3724 #endif
3725 return true;
3726 }
3727
3728 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3729 struct VmaMutexLock
3730 {
VMA_CLASS_NO_COPYVmaMutexLock3731 VMA_CLASS_NO_COPY(VmaMutexLock)
3732 public:
3733 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3734 m_pMutex(useMutex ? &mutex : VMA_NULL)
3735 { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock3736 ~VmaMutexLock()
3737 { if(m_pMutex) { m_pMutex->Unlock(); } }
3738 private:
3739 VMA_MUTEX* m_pMutex;
3740 };
3741
3742 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3743 struct VmaMutexLockRead
3744 {
VMA_CLASS_NO_COPYVmaMutexLockRead3745 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3746 public:
3747 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3748 m_pMutex(useMutex ? &mutex : VMA_NULL)
3749 { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead3750 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3751 private:
3752 VMA_RW_MUTEX* m_pMutex;
3753 };
3754
3755 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3756 struct VmaMutexLockWrite
3757 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3758 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3759 public:
3760 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3761 m_pMutex(useMutex ? &mutex : VMA_NULL)
3762 { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite3763 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3764 private:
3765 VMA_RW_MUTEX* m_pMutex;
3766 };
3767
3768 #if VMA_DEBUG_GLOBAL_MUTEX
3769 static VMA_MUTEX gDebugGlobalMutex;
3770 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3771 #else
3772 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3773 #endif
3774
3775 // Minimum size of a free suballocation to register it in the free suballocation collection.
3776 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3777
3778 /*
3779 Performs binary search and returns iterator to first element that is greater or
3780 equal to (key), according to comparison (cmp).
3781
3782 Cmp should return true if first argument is less than second argument.
3783
3784 Returned value is the found element, if present in the collection or place where
3785 new element with value (key) should be inserted.
3786 */
3787 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,CmpLess cmp)3788 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3789 {
3790 size_t down = 0, up = (end - beg);
3791 while(down < up)
3792 {
3793 const size_t mid = (down + up) / 2;
3794 if(cmp(*(beg+mid), key))
3795 {
3796 down = mid + 1;
3797 }
3798 else
3799 {
3800 up = mid;
3801 }
3802 }
3803 return beg + down;
3804 }
3805
3806 /*
3807 Returns true if all pointers in the array are not-null and unique.
3808 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3809 T must be pointer type, e.g. VmaAllocation, VmaPool.
3810 */
3811 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3812 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3813 {
3814 for(uint32_t i = 0; i < count; ++i)
3815 {
3816 const T iPtr = arr[i];
3817 if(iPtr == VMA_NULL)
3818 {
3819 return false;
3820 }
3821 for(uint32_t j = i + 1; j < count; ++j)
3822 {
3823 if(iPtr == arr[j])
3824 {
3825 return false;
3826 }
3827 }
3828 }
3829 return true;
3830 }
3831
3832 ////////////////////////////////////////////////////////////////////////////////
3833 // Memory allocation
3834
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3835 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3836 {
3837 if((pAllocationCallbacks != VMA_NULL) &&
3838 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3839 {
3840 return (*pAllocationCallbacks->pfnAllocation)(
3841 pAllocationCallbacks->pUserData,
3842 size,
3843 alignment,
3844 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3845 }
3846 else
3847 {
3848 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3849 }
3850 }
3851
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3852 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3853 {
3854 if((pAllocationCallbacks != VMA_NULL) &&
3855 (pAllocationCallbacks->pfnFree != VMA_NULL))
3856 {
3857 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3858 }
3859 else
3860 {
3861 VMA_SYSTEM_FREE(ptr);
3862 }
3863 }
3864
3865 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3866 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3867 {
3868 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3869 }
3870
3871 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3872 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3873 {
3874 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3875 }
3876
3877 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3878
3879 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3880
3881 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3882 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3883 {
3884 ptr->~T();
3885 VmaFree(pAllocationCallbacks, ptr);
3886 }
3887
3888 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3889 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3890 {
3891 if(ptr != VMA_NULL)
3892 {
3893 for(size_t i = count; i--; )
3894 {
3895 ptr[i].~T();
3896 }
3897 VmaFree(pAllocationCallbacks, ptr);
3898 }
3899 }
3900
3901 // STL-compatible allocator.
3902 template<typename T>
3903 class VmaStlAllocator
3904 {
3905 public:
3906 const VkAllocationCallbacks* const m_pCallbacks;
3907 typedef T value_type;
3908
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)3909 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)3910 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3911
allocate(size_t n)3912 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)3913 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3914
3915 template<typename U>
3916 bool operator==(const VmaStlAllocator<U>& rhs) const
3917 {
3918 return m_pCallbacks == rhs.m_pCallbacks;
3919 }
3920 template<typename U>
3921 bool operator!=(const VmaStlAllocator<U>& rhs) const
3922 {
3923 return m_pCallbacks != rhs.m_pCallbacks;
3924 }
3925
3926 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3927 };
3928
3929 #if VMA_USE_STL_VECTOR
3930
3931 #define VmaVector std::vector
3932
3933 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)3934 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3935 {
3936 vec.insert(vec.begin() + index, item);
3937 }
3938
3939 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)3940 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3941 {
3942 vec.erase(vec.begin() + index);
3943 }
3944
3945 #else // #if VMA_USE_STL_VECTOR
3946
3947 /* Class with interface compatible with subset of std::vector.
3948 T must be POD because constructors and destructors are not called and memcpy is
3949 used for these objects. */
3950 template<typename T, typename AllocatorT>
3951 class VmaVector
3952 {
3953 public:
3954 typedef T value_type;
3955
VmaVector(const AllocatorT & allocator)3956 VmaVector(const AllocatorT& allocator) :
3957 m_Allocator(allocator),
3958 m_pArray(VMA_NULL),
3959 m_Count(0),
3960 m_Capacity(0)
3961 {
3962 }
3963
VmaVector(size_t count,const AllocatorT & allocator)3964 VmaVector(size_t count, const AllocatorT& allocator) :
3965 m_Allocator(allocator),
3966 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3967 m_Count(count),
3968 m_Capacity(count)
3969 {
3970 }
3971
VmaVector(const VmaVector<T,AllocatorT> & src)3972 VmaVector(const VmaVector<T, AllocatorT>& src) :
3973 m_Allocator(src.m_Allocator),
3974 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3975 m_Count(src.m_Count),
3976 m_Capacity(src.m_Count)
3977 {
3978 if(m_Count != 0)
3979 {
3980 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3981 }
3982 }
3983
~VmaVector()3984 ~VmaVector()
3985 {
3986 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3987 }
3988
3989 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3990 {
3991 if(&rhs != this)
3992 {
3993 resize(rhs.m_Count);
3994 if(m_Count != 0)
3995 {
3996 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3997 }
3998 }
3999 return *this;
4000 }
4001
empty()4002 bool empty() const { return m_Count == 0; }
size()4003 size_t size() const { return m_Count; }
data()4004 T* data() { return m_pArray; }
data()4005 const T* data() const { return m_pArray; }
4006
4007 T& operator[](size_t index)
4008 {
4009 VMA_HEAVY_ASSERT(index < m_Count);
4010 return m_pArray[index];
4011 }
4012 const T& operator[](size_t index) const
4013 {
4014 VMA_HEAVY_ASSERT(index < m_Count);
4015 return m_pArray[index];
4016 }
4017
front()4018 T& front()
4019 {
4020 VMA_HEAVY_ASSERT(m_Count > 0);
4021 return m_pArray[0];
4022 }
front()4023 const T& front() const
4024 {
4025 VMA_HEAVY_ASSERT(m_Count > 0);
4026 return m_pArray[0];
4027 }
back()4028 T& back()
4029 {
4030 VMA_HEAVY_ASSERT(m_Count > 0);
4031 return m_pArray[m_Count - 1];
4032 }
back()4033 const T& back() const
4034 {
4035 VMA_HEAVY_ASSERT(m_Count > 0);
4036 return m_pArray[m_Count - 1];
4037 }
4038
4039 void reserve(size_t newCapacity, bool freeMemory = false)
4040 {
4041 newCapacity = VMA_MAX(newCapacity, m_Count);
4042
4043 if((newCapacity < m_Capacity) && !freeMemory)
4044 {
4045 newCapacity = m_Capacity;
4046 }
4047
4048 if(newCapacity != m_Capacity)
4049 {
4050 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4051 if(m_Count != 0)
4052 {
4053 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4054 }
4055 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4056 m_Capacity = newCapacity;
4057 m_pArray = newArray;
4058 }
4059 }
4060
4061 void resize(size_t newCount, bool freeMemory = false)
4062 {
4063 size_t newCapacity = m_Capacity;
4064 if(newCount > m_Capacity)
4065 {
4066 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4067 }
4068 else if(freeMemory)
4069 {
4070 newCapacity = newCount;
4071 }
4072
4073 if(newCapacity != m_Capacity)
4074 {
4075 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4076 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4077 if(elementsToCopy != 0)
4078 {
4079 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4080 }
4081 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4082 m_Capacity = newCapacity;
4083 m_pArray = newArray;
4084 }
4085
4086 m_Count = newCount;
4087 }
4088
4089 void clear(bool freeMemory = false)
4090 {
4091 resize(0, freeMemory);
4092 }
4093
insert(size_t index,const T & src)4094 void insert(size_t index, const T& src)
4095 {
4096 VMA_HEAVY_ASSERT(index <= m_Count);
4097 const size_t oldCount = size();
4098 resize(oldCount + 1);
4099 if(index < oldCount)
4100 {
4101 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4102 }
4103 m_pArray[index] = src;
4104 }
4105
remove(size_t index)4106 void remove(size_t index)
4107 {
4108 VMA_HEAVY_ASSERT(index < m_Count);
4109 const size_t oldCount = size();
4110 if(index < oldCount - 1)
4111 {
4112 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4113 }
4114 resize(oldCount - 1);
4115 }
4116
push_back(const T & src)4117 void push_back(const T& src)
4118 {
4119 const size_t newIndex = size();
4120 resize(newIndex + 1);
4121 m_pArray[newIndex] = src;
4122 }
4123
pop_back()4124 void pop_back()
4125 {
4126 VMA_HEAVY_ASSERT(m_Count > 0);
4127 resize(size() - 1);
4128 }
4129
push_front(const T & src)4130 void push_front(const T& src)
4131 {
4132 insert(0, src);
4133 }
4134
pop_front()4135 void pop_front()
4136 {
4137 VMA_HEAVY_ASSERT(m_Count > 0);
4138 remove(0);
4139 }
4140
4141 typedef T* iterator;
4142
begin()4143 iterator begin() { return m_pArray; }
end()4144 iterator end() { return m_pArray + m_Count; }
4145
4146 private:
4147 AllocatorT m_Allocator;
4148 T* m_pArray;
4149 size_t m_Count;
4150 size_t m_Capacity;
4151 };
4152
4153 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4154 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4155 {
4156 vec.insert(index, item);
4157 }
4158
4159 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4160 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4161 {
4162 vec.remove(index);
4163 }
4164
4165 #endif // #if VMA_USE_STL_VECTOR
4166
4167 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)4168 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4169 {
4170 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4171 vector.data(),
4172 vector.data() + vector.size(),
4173 value,
4174 CmpLess()) - vector.data();
4175 VmaVectorInsert(vector, indexToInsert, value);
4176 return indexToInsert;
4177 }
4178
4179 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)4180 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4181 {
4182 CmpLess comparator;
4183 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4184 vector.begin(),
4185 vector.end(),
4186 value,
4187 comparator);
4188 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4189 {
4190 size_t indexToRemove = it - vector.begin();
4191 VmaVectorRemove(vector, indexToRemove);
4192 return true;
4193 }
4194 return false;
4195 }
4196
4197 template<typename CmpLess, typename IterT, typename KeyT>
VmaVectorFindSorted(const IterT & beg,const IterT & end,const KeyT & value)4198 IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4199 {
4200 CmpLess comparator;
4201 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4202 beg, end, value, comparator);
4203 if(it == end ||
4204 (!comparator(*it, value) && !comparator(value, *it)))
4205 {
4206 return it;
4207 }
4208 return end;
4209 }
4210
4211 ////////////////////////////////////////////////////////////////////////////////
4212 // class VmaPoolAllocator
4213
4214 /*
4215 Allocator for objects of type T using a list of arrays (pools) to speed up
4216 allocation. Number of elements that can be allocated is not bounded because
4217 allocator can create multiple blocks.
4218 */
4219 template<typename T>
4220 class VmaPoolAllocator
4221 {
4222 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4223 public:
4224 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4225 ~VmaPoolAllocator();
4226 void Clear();
4227 T* Alloc();
4228 void Free(T* ptr);
4229
4230 private:
4231 union Item
4232 {
4233 uint32_t NextFreeIndex;
4234 T Value;
4235 };
4236
4237 struct ItemBlock
4238 {
4239 Item* pItems;
4240 uint32_t FirstFreeIndex;
4241 };
4242
4243 const VkAllocationCallbacks* m_pAllocationCallbacks;
4244 size_t m_ItemsPerBlock;
4245 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4246
4247 ItemBlock& CreateNewBlock();
4248 };
4249
4250 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,size_t itemsPerBlock)4251 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4252 m_pAllocationCallbacks(pAllocationCallbacks),
4253 m_ItemsPerBlock(itemsPerBlock),
4254 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4255 {
4256 VMA_ASSERT(itemsPerBlock > 0);
4257 }
4258
4259 template<typename T>
~VmaPoolAllocator()4260 VmaPoolAllocator<T>::~VmaPoolAllocator()
4261 {
4262 Clear();
4263 }
4264
4265 template<typename T>
Clear()4266 void VmaPoolAllocator<T>::Clear()
4267 {
4268 for(size_t i = m_ItemBlocks.size(); i--; )
4269 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4270 m_ItemBlocks.clear();
4271 }
4272
4273 template<typename T>
Alloc()4274 T* VmaPoolAllocator<T>::Alloc()
4275 {
4276 for(size_t i = m_ItemBlocks.size(); i--; )
4277 {
4278 ItemBlock& block = m_ItemBlocks[i];
4279 // This block has some free items: Use first one.
4280 if(block.FirstFreeIndex != UINT32_MAX)
4281 {
4282 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4283 block.FirstFreeIndex = pItem->NextFreeIndex;
4284 return &pItem->Value;
4285 }
4286 }
4287
4288 // No block has free item: Create new one and use it.
4289 ItemBlock& newBlock = CreateNewBlock();
4290 Item* const pItem = &newBlock.pItems[0];
4291 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4292 return &pItem->Value;
4293 }
4294
4295 template<typename T>
Free(T * ptr)4296 void VmaPoolAllocator<T>::Free(T* ptr)
4297 {
4298 // Search all memory blocks to find ptr.
4299 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4300 {
4301 ItemBlock& block = m_ItemBlocks[i];
4302
4303 // Casting to union.
4304 Item* pItemPtr;
4305 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4306
4307 // Check if pItemPtr is in address range of this block.
4308 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4309 {
4310 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4311 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4312 block.FirstFreeIndex = index;
4313 return;
4314 }
4315 }
4316 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4317 }
4318
4319 template<typename T>
CreateNewBlock()4320 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4321 {
4322 ItemBlock newBlock = {
4323 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4324
4325 m_ItemBlocks.push_back(newBlock);
4326
4327 // Setup singly-linked list of all free items in this block.
4328 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4329 newBlock.pItems[i].NextFreeIndex = i + 1;
4330 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4331 return m_ItemBlocks.back();
4332 }
4333
4334 ////////////////////////////////////////////////////////////////////////////////
4335 // class VmaRawList, VmaList
4336
4337 #if VMA_USE_STL_LIST
4338
4339 #define VmaList std::list
4340
4341 #else // #if VMA_USE_STL_LIST
4342
4343 template<typename T>
4344 struct VmaListItem
4345 {
4346 VmaListItem* pPrev;
4347 VmaListItem* pNext;
4348 T Value;
4349 };
4350
4351 // Doubly linked list.
4352 template<typename T>
4353 class VmaRawList
4354 {
4355 VMA_CLASS_NO_COPY(VmaRawList)
4356 public:
4357 typedef VmaListItem<T> ItemType;
4358
4359 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4360 ~VmaRawList();
4361 void Clear();
4362
GetCount()4363 size_t GetCount() const { return m_Count; }
IsEmpty()4364 bool IsEmpty() const { return m_Count == 0; }
4365
Front()4366 ItemType* Front() { return m_pFront; }
Front()4367 const ItemType* Front() const { return m_pFront; }
Back()4368 ItemType* Back() { return m_pBack; }
Back()4369 const ItemType* Back() const { return m_pBack; }
4370
4371 ItemType* PushBack();
4372 ItemType* PushFront();
4373 ItemType* PushBack(const T& value);
4374 ItemType* PushFront(const T& value);
4375 void PopBack();
4376 void PopFront();
4377
4378 // Item can be null - it means PushBack.
4379 ItemType* InsertBefore(ItemType* pItem);
4380 // Item can be null - it means PushFront.
4381 ItemType* InsertAfter(ItemType* pItem);
4382
4383 ItemType* InsertBefore(ItemType* pItem, const T& value);
4384 ItemType* InsertAfter(ItemType* pItem, const T& value);
4385
4386 void Remove(ItemType* pItem);
4387
4388 private:
4389 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4390 VmaPoolAllocator<ItemType> m_ItemAllocator;
4391 ItemType* m_pFront;
4392 ItemType* m_pBack;
4393 size_t m_Count;
4394 };
4395
4396 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4397 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4398 m_pAllocationCallbacks(pAllocationCallbacks),
4399 m_ItemAllocator(pAllocationCallbacks, 128),
4400 m_pFront(VMA_NULL),
4401 m_pBack(VMA_NULL),
4402 m_Count(0)
4403 {
4404 }
4405
4406 template<typename T>
~VmaRawList()4407 VmaRawList<T>::~VmaRawList()
4408 {
4409 // Intentionally not calling Clear, because that would be unnecessary
4410 // computations to return all items to m_ItemAllocator as free.
4411 }
4412
4413 template<typename T>
Clear()4414 void VmaRawList<T>::Clear()
4415 {
4416 if(IsEmpty() == false)
4417 {
4418 ItemType* pItem = m_pBack;
4419 while(pItem != VMA_NULL)
4420 {
4421 ItemType* const pPrevItem = pItem->pPrev;
4422 m_ItemAllocator.Free(pItem);
4423 pItem = pPrevItem;
4424 }
4425 m_pFront = VMA_NULL;
4426 m_pBack = VMA_NULL;
4427 m_Count = 0;
4428 }
4429 }
4430
4431 template<typename T>
PushBack()4432 VmaListItem<T>* VmaRawList<T>::PushBack()
4433 {
4434 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4435 pNewItem->pNext = VMA_NULL;
4436 if(IsEmpty())
4437 {
4438 pNewItem->pPrev = VMA_NULL;
4439 m_pFront = pNewItem;
4440 m_pBack = pNewItem;
4441 m_Count = 1;
4442 }
4443 else
4444 {
4445 pNewItem->pPrev = m_pBack;
4446 m_pBack->pNext = pNewItem;
4447 m_pBack = pNewItem;
4448 ++m_Count;
4449 }
4450 return pNewItem;
4451 }
4452
4453 template<typename T>
PushFront()4454 VmaListItem<T>* VmaRawList<T>::PushFront()
4455 {
4456 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4457 pNewItem->pPrev = VMA_NULL;
4458 if(IsEmpty())
4459 {
4460 pNewItem->pNext = VMA_NULL;
4461 m_pFront = pNewItem;
4462 m_pBack = pNewItem;
4463 m_Count = 1;
4464 }
4465 else
4466 {
4467 pNewItem->pNext = m_pFront;
4468 m_pFront->pPrev = pNewItem;
4469 m_pFront = pNewItem;
4470 ++m_Count;
4471 }
4472 return pNewItem;
4473 }
4474
4475 template<typename T>
PushBack(const T & value)4476 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4477 {
4478 ItemType* const pNewItem = PushBack();
4479 pNewItem->Value = value;
4480 return pNewItem;
4481 }
4482
4483 template<typename T>
PushFront(const T & value)4484 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4485 {
4486 ItemType* const pNewItem = PushFront();
4487 pNewItem->Value = value;
4488 return pNewItem;
4489 }
4490
4491 template<typename T>
PopBack()4492 void VmaRawList<T>::PopBack()
4493 {
4494 VMA_HEAVY_ASSERT(m_Count > 0);
4495 ItemType* const pBackItem = m_pBack;
4496 ItemType* const pPrevItem = pBackItem->pPrev;
4497 if(pPrevItem != VMA_NULL)
4498 {
4499 pPrevItem->pNext = VMA_NULL;
4500 }
4501 m_pBack = pPrevItem;
4502 m_ItemAllocator.Free(pBackItem);
4503 --m_Count;
4504 }
4505
4506 template<typename T>
PopFront()4507 void VmaRawList<T>::PopFront()
4508 {
4509 VMA_HEAVY_ASSERT(m_Count > 0);
4510 ItemType* const pFrontItem = m_pFront;
4511 ItemType* const pNextItem = pFrontItem->pNext;
4512 if(pNextItem != VMA_NULL)
4513 {
4514 pNextItem->pPrev = VMA_NULL;
4515 }
4516 m_pFront = pNextItem;
4517 m_ItemAllocator.Free(pFrontItem);
4518 --m_Count;
4519 }
4520
4521 template<typename T>
Remove(ItemType * pItem)4522 void VmaRawList<T>::Remove(ItemType* pItem)
4523 {
4524 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4525 VMA_HEAVY_ASSERT(m_Count > 0);
4526
4527 if(pItem->pPrev != VMA_NULL)
4528 {
4529 pItem->pPrev->pNext = pItem->pNext;
4530 }
4531 else
4532 {
4533 VMA_HEAVY_ASSERT(m_pFront == pItem);
4534 m_pFront = pItem->pNext;
4535 }
4536
4537 if(pItem->pNext != VMA_NULL)
4538 {
4539 pItem->pNext->pPrev = pItem->pPrev;
4540 }
4541 else
4542 {
4543 VMA_HEAVY_ASSERT(m_pBack == pItem);
4544 m_pBack = pItem->pPrev;
4545 }
4546
4547 m_ItemAllocator.Free(pItem);
4548 --m_Count;
4549 }
4550
4551 template<typename T>
InsertBefore(ItemType * pItem)4552 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4553 {
4554 if(pItem != VMA_NULL)
4555 {
4556 ItemType* const prevItem = pItem->pPrev;
4557 ItemType* const newItem = m_ItemAllocator.Alloc();
4558 newItem->pPrev = prevItem;
4559 newItem->pNext = pItem;
4560 pItem->pPrev = newItem;
4561 if(prevItem != VMA_NULL)
4562 {
4563 prevItem->pNext = newItem;
4564 }
4565 else
4566 {
4567 VMA_HEAVY_ASSERT(m_pFront == pItem);
4568 m_pFront = newItem;
4569 }
4570 ++m_Count;
4571 return newItem;
4572 }
4573 else
4574 return PushBack();
4575 }
4576
4577 template<typename T>
InsertAfter(ItemType * pItem)4578 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4579 {
4580 if(pItem != VMA_NULL)
4581 {
4582 ItemType* const nextItem = pItem->pNext;
4583 ItemType* const newItem = m_ItemAllocator.Alloc();
4584 newItem->pNext = nextItem;
4585 newItem->pPrev = pItem;
4586 pItem->pNext = newItem;
4587 if(nextItem != VMA_NULL)
4588 {
4589 nextItem->pPrev = newItem;
4590 }
4591 else
4592 {
4593 VMA_HEAVY_ASSERT(m_pBack == pItem);
4594 m_pBack = newItem;
4595 }
4596 ++m_Count;
4597 return newItem;
4598 }
4599 else
4600 return PushFront();
4601 }
4602
4603 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4604 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4605 {
4606 ItemType* const newItem = InsertBefore(pItem);
4607 newItem->Value = value;
4608 return newItem;
4609 }
4610
4611 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4612 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4613 {
4614 ItemType* const newItem = InsertAfter(pItem);
4615 newItem->Value = value;
4616 return newItem;
4617 }
4618
4619 template<typename T, typename AllocatorT>
4620 class VmaList
4621 {
VMA_CLASS_NO_COPY(VmaList)4622 VMA_CLASS_NO_COPY(VmaList)
4623 public:
4624 class iterator
4625 {
4626 public:
4627 iterator() :
4628 m_pList(VMA_NULL),
4629 m_pItem(VMA_NULL)
4630 {
4631 }
4632
4633 T& operator*() const
4634 {
4635 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4636 return m_pItem->Value;
4637 }
4638 T* operator->() const
4639 {
4640 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4641 return &m_pItem->Value;
4642 }
4643
4644 iterator& operator++()
4645 {
4646 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4647 m_pItem = m_pItem->pNext;
4648 return *this;
4649 }
4650 iterator& operator--()
4651 {
4652 if(m_pItem != VMA_NULL)
4653 {
4654 m_pItem = m_pItem->pPrev;
4655 }
4656 else
4657 {
4658 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4659 m_pItem = m_pList->Back();
4660 }
4661 return *this;
4662 }
4663
4664 iterator operator++(int)
4665 {
4666 iterator result = *this;
4667 ++*this;
4668 return result;
4669 }
4670 iterator operator--(int)
4671 {
4672 iterator result = *this;
4673 --*this;
4674 return result;
4675 }
4676
4677 bool operator==(const iterator& rhs) const
4678 {
4679 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4680 return m_pItem == rhs.m_pItem;
4681 }
4682 bool operator!=(const iterator& rhs) const
4683 {
4684 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4685 return m_pItem != rhs.m_pItem;
4686 }
4687
4688 private:
4689 VmaRawList<T>* m_pList;
4690 VmaListItem<T>* m_pItem;
4691
4692 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4693 m_pList(pList),
4694 m_pItem(pItem)
4695 {
4696 }
4697
4698 friend class VmaList<T, AllocatorT>;
4699 };
4700
4701 class const_iterator
4702 {
4703 public:
const_iterator()4704 const_iterator() :
4705 m_pList(VMA_NULL),
4706 m_pItem(VMA_NULL)
4707 {
4708 }
4709
const_iterator(const iterator & src)4710 const_iterator(const iterator& src) :
4711 m_pList(src.m_pList),
4712 m_pItem(src.m_pItem)
4713 {
4714 }
4715
4716 const T& operator*() const
4717 {
4718 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4719 return m_pItem->Value;
4720 }
4721 const T* operator->() const
4722 {
4723 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4724 return &m_pItem->Value;
4725 }
4726
4727 const_iterator& operator++()
4728 {
4729 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4730 m_pItem = m_pItem->pNext;
4731 return *this;
4732 }
4733 const_iterator& operator--()
4734 {
4735 if(m_pItem != VMA_NULL)
4736 {
4737 m_pItem = m_pItem->pPrev;
4738 }
4739 else
4740 {
4741 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4742 m_pItem = m_pList->Back();
4743 }
4744 return *this;
4745 }
4746
4747 const_iterator operator++(int)
4748 {
4749 const_iterator result = *this;
4750 ++*this;
4751 return result;
4752 }
4753 const_iterator operator--(int)
4754 {
4755 const_iterator result = *this;
4756 --*this;
4757 return result;
4758 }
4759
4760 bool operator==(const const_iterator& rhs) const
4761 {
4762 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4763 return m_pItem == rhs.m_pItem;
4764 }
4765 bool operator!=(const const_iterator& rhs) const
4766 {
4767 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4768 return m_pItem != rhs.m_pItem;
4769 }
4770
4771 private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4772 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4773 m_pList(pList),
4774 m_pItem(pItem)
4775 {
4776 }
4777
4778 const VmaRawList<T>* m_pList;
4779 const VmaListItem<T>* m_pItem;
4780
4781 friend class VmaList<T, AllocatorT>;
4782 };
4783
VmaList(const AllocatorT & allocator)4784 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4785
empty()4786 bool empty() const { return m_RawList.IsEmpty(); }
size()4787 size_t size() const { return m_RawList.GetCount(); }
4788
begin()4789 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4790 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4791
cbegin()4792 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4793 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4794
clear()4795 void clear() { m_RawList.Clear(); }
push_back(const T & value)4796 void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)4797 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)4798 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4799
4800 private:
4801 VmaRawList<T> m_RawList;
4802 };
4803
4804 #endif // #if VMA_USE_STL_LIST
4805
4806 ////////////////////////////////////////////////////////////////////////////////
4807 // class VmaMap
4808
4809 // Unused in this version.
4810 #if 0
4811
4812 #if VMA_USE_STL_UNORDERED_MAP
4813
4814 #define VmaPair std::pair
4815
4816 #define VMA_MAP_TYPE(KeyT, ValueT) \
4817 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4818
4819 #else // #if VMA_USE_STL_UNORDERED_MAP
4820
4821 template<typename T1, typename T2>
4822 struct VmaPair
4823 {
4824 T1 first;
4825 T2 second;
4826
4827 VmaPair() : first(), second() { }
4828 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4829 };
4830
4831 /* Class compatible with subset of interface of std::unordered_map.
4832 KeyT, ValueT must be POD because they will be stored in VmaVector.
4833 */
4834 template<typename KeyT, typename ValueT>
4835 class VmaMap
4836 {
4837 public:
4838 typedef VmaPair<KeyT, ValueT> PairType;
4839 typedef PairType* iterator;
4840
4841 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4842
4843 iterator begin() { return m_Vector.begin(); }
4844 iterator end() { return m_Vector.end(); }
4845
4846 void insert(const PairType& pair);
4847 iterator find(const KeyT& key);
4848 void erase(iterator it);
4849
4850 private:
4851 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4852 };
4853
4854 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4855
4856 template<typename FirstT, typename SecondT>
4857 struct VmaPairFirstLess
4858 {
4859 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4860 {
4861 return lhs.first < rhs.first;
4862 }
4863 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4864 {
4865 return lhs.first < rhsFirst;
4866 }
4867 };
4868
4869 template<typename KeyT, typename ValueT>
4870 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4871 {
4872 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4873 m_Vector.data(),
4874 m_Vector.data() + m_Vector.size(),
4875 pair,
4876 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4877 VmaVectorInsert(m_Vector, indexToInsert, pair);
4878 }
4879
4880 template<typename KeyT, typename ValueT>
4881 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4882 {
4883 PairType* it = VmaBinaryFindFirstNotLess(
4884 m_Vector.data(),
4885 m_Vector.data() + m_Vector.size(),
4886 key,
4887 VmaPairFirstLess<KeyT, ValueT>());
4888 if((it != m_Vector.end()) && (it->first == key))
4889 {
4890 return it;
4891 }
4892 else
4893 {
4894 return m_Vector.end();
4895 }
4896 }
4897
4898 template<typename KeyT, typename ValueT>
4899 void VmaMap<KeyT, ValueT>::erase(iterator it)
4900 {
4901 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4902 }
4903
4904 #endif // #if VMA_USE_STL_UNORDERED_MAP
4905
4906 #endif // #if 0
4907
4908 ////////////////////////////////////////////////////////////////////////////////
4909
4910 class VmaDeviceMemoryBlock;
4911
4912 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4913
4914 struct VmaAllocation_T
4915 {
4916 VMA_CLASS_NO_COPY(VmaAllocation_T)
4917 private:
4918 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4919
4920 enum FLAGS
4921 {
4922 FLAG_USER_DATA_STRING = 0x01,
4923 };
4924
4925 public:
4926 enum ALLOCATION_TYPE
4927 {
4928 ALLOCATION_TYPE_NONE,
4929 ALLOCATION_TYPE_BLOCK,
4930 ALLOCATION_TYPE_DEDICATED,
4931 };
4932
VmaAllocation_TVmaAllocation_T4933 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4934 m_Alignment(1),
4935 m_Size(0),
4936 m_pUserData(VMA_NULL),
4937 m_LastUseFrameIndex(currentFrameIndex),
4938 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4939 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4940 m_MapCount(0),
4941 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4942 {
4943 #if VMA_STATS_STRING_ENABLED
4944 m_CreationFrameIndex = currentFrameIndex;
4945 m_BufferImageUsage = 0;
4946 #endif
4947 }
4948
~VmaAllocation_TVmaAllocation_T4949 ~VmaAllocation_T()
4950 {
4951 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4952
4953 // Check if owned string was freed.
4954 VMA_ASSERT(m_pUserData == VMA_NULL);
4955 }
4956
InitBlockAllocationVmaAllocation_T4957 void InitBlockAllocation(
4958 VmaPool hPool,
4959 VmaDeviceMemoryBlock* block,
4960 VkDeviceSize offset,
4961 VkDeviceSize alignment,
4962 VkDeviceSize size,
4963 VmaSuballocationType suballocationType,
4964 bool mapped,
4965 bool canBecomeLost)
4966 {
4967 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4968 VMA_ASSERT(block != VMA_NULL);
4969 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4970 m_Alignment = alignment;
4971 m_Size = size;
4972 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4973 m_SuballocationType = (uint8_t)suballocationType;
4974 m_BlockAllocation.m_hPool = hPool;
4975 m_BlockAllocation.m_Block = block;
4976 m_BlockAllocation.m_Offset = offset;
4977 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4978 }
4979
InitLostVmaAllocation_T4980 void InitLost()
4981 {
4982 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4983 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4984 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4985 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4986 m_BlockAllocation.m_Block = VMA_NULL;
4987 m_BlockAllocation.m_Offset = 0;
4988 m_BlockAllocation.m_CanBecomeLost = true;
4989 }
4990
4991 void ChangeBlockAllocation(
4992 VmaAllocator hAllocator,
4993 VmaDeviceMemoryBlock* block,
4994 VkDeviceSize offset);
4995
4996 void ChangeSize(VkDeviceSize newSize);
4997 void ChangeOffset(VkDeviceSize newOffset);
4998
4999 // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T5000 void InitDedicatedAllocation(
5001 uint32_t memoryTypeIndex,
5002 VkDeviceMemory hMemory,
5003 VmaSuballocationType suballocationType,
5004 void* pMappedData,
5005 VkDeviceSize size)
5006 {
5007 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5008 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
5009 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
5010 m_Alignment = 0;
5011 m_Size = size;
5012 m_SuballocationType = (uint8_t)suballocationType;
5013 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5014 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
5015 m_DedicatedAllocation.m_hMemory = hMemory;
5016 m_DedicatedAllocation.m_pMappedData = pMappedData;
5017 }
5018
GetTypeVmaAllocation_T5019 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T5020 VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T5021 VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T5022 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T5023 void* GetUserData() const { return m_pUserData; }
5024 void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T5025 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
5026
GetBlockVmaAllocation_T5027 VmaDeviceMemoryBlock* GetBlock() const
5028 {
5029 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5030 return m_BlockAllocation.m_Block;
5031 }
5032 VkDeviceSize GetOffset() const;
5033 VkDeviceMemory GetMemory() const;
5034 uint32_t GetMemoryTypeIndex() const;
IsPersistentMapVmaAllocation_T5035 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5036 void* GetMappedData() const;
5037 bool CanBecomeLost() const;
5038 VmaPool GetPool() const;
5039
GetLastUseFrameIndexVmaAllocation_T5040 uint32_t GetLastUseFrameIndex() const
5041 {
5042 return m_LastUseFrameIndex.load();
5043 }
CompareExchangeLastUseFrameIndexVmaAllocation_T5044 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5045 {
5046 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5047 }
5048 /*
5049 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5050 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5051 - Else, returns false.
5052
5053 If hAllocation is already lost, assert - you should not call it then.
5054 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5055 */
5056 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5057
DedicatedAllocCalcStatsInfoVmaAllocation_T5058 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5059 {
5060 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5061 outInfo.blockCount = 1;
5062 outInfo.allocationCount = 1;
5063 outInfo.unusedRangeCount = 0;
5064 outInfo.usedBytes = m_Size;
5065 outInfo.unusedBytes = 0;
5066 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5067 outInfo.unusedRangeSizeMin = UINT64_MAX;
5068 outInfo.unusedRangeSizeMax = 0;
5069 }
5070
5071 void BlockAllocMap();
5072 void BlockAllocUnmap();
5073 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5074 void DedicatedAllocUnmap(VmaAllocator hAllocator);
5075
5076 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T5077 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T5078 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5079
InitBufferImageUsageVmaAllocation_T5080 void InitBufferImageUsage(uint32_t bufferImageUsage)
5081 {
5082 VMA_ASSERT(m_BufferImageUsage == 0);
5083 m_BufferImageUsage = bufferImageUsage;
5084 }
5085
5086 void PrintParameters(class VmaJsonWriter& json) const;
5087 #endif
5088
5089 private:
5090 VkDeviceSize m_Alignment;
5091 VkDeviceSize m_Size;
5092 void* m_pUserData;
5093 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5094 uint8_t m_Type; // ALLOCATION_TYPE
5095 uint8_t m_SuballocationType; // VmaSuballocationType
5096 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5097 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5098 uint8_t m_MapCount;
5099 uint8_t m_Flags; // enum FLAGS
5100
5101 // Allocation out of VmaDeviceMemoryBlock.
5102 struct BlockAllocation
5103 {
5104 VmaPool m_hPool; // Null if belongs to general memory.
5105 VmaDeviceMemoryBlock* m_Block;
5106 VkDeviceSize m_Offset;
5107 bool m_CanBecomeLost;
5108 };
5109
5110 // Allocation for an object that has its own private VkDeviceMemory.
5111 struct DedicatedAllocation
5112 {
5113 uint32_t m_MemoryTypeIndex;
5114 VkDeviceMemory m_hMemory;
5115 void* m_pMappedData; // Not null means memory is mapped.
5116 };
5117
5118 union
5119 {
5120 // Allocation out of VmaDeviceMemoryBlock.
5121 BlockAllocation m_BlockAllocation;
5122 // Allocation for an object that has its own private VkDeviceMemory.
5123 DedicatedAllocation m_DedicatedAllocation;
5124 };
5125
5126 #if VMA_STATS_STRING_ENABLED
5127 uint32_t m_CreationFrameIndex;
5128 uint32_t m_BufferImageUsage; // 0 if unknown.
5129 #endif
5130
5131 void FreeUserDataString(VmaAllocator hAllocator);
5132 };
5133
5134 /*
5135 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5136 allocated memory block or free.
5137 */
5138 struct VmaSuballocation
5139 {
5140 VkDeviceSize offset;
5141 VkDeviceSize size;
5142 VmaAllocation hAllocation;
5143 VmaSuballocationType type;
5144 };
5145
5146 // Comparator for offsets.
5147 struct VmaSuballocationOffsetLess
5148 {
operatorVmaSuballocationOffsetLess5149 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5150 {
5151 return lhs.offset < rhs.offset;
5152 }
5153 };
5154 struct VmaSuballocationOffsetGreater
5155 {
operatorVmaSuballocationOffsetGreater5156 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5157 {
5158 return lhs.offset > rhs.offset;
5159 }
5160 };
5161
5162 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5163
5164 // Cost of one additional allocation lost, as equivalent in bytes.
5165 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5166
5167 /*
5168 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5169
5170 If canMakeOtherLost was false:
5171 - item points to a FREE suballocation.
5172 - itemsToMakeLostCount is 0.
5173
5174 If canMakeOtherLost was true:
5175 - item points to first of sequence of suballocations, which are either FREE,
5176 or point to VmaAllocations that can become lost.
5177 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5178 the requested allocation to succeed.
5179 */
5180 struct VmaAllocationRequest
5181 {
5182 VkDeviceSize offset;
5183 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5184 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5185 VmaSuballocationList::iterator item;
5186 size_t itemsToMakeLostCount;
5187 void* customData;
5188
CalcCostVmaAllocationRequest5189 VkDeviceSize CalcCost() const
5190 {
5191 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5192 }
5193 };
5194
5195 /*
5196 Data structure used for bookkeeping of allocations and unused ranges of memory
5197 in a single VkDeviceMemory block.
5198 */
5199 class VmaBlockMetadata
5200 {
5201 public:
5202 VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()5203 virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)5204 virtual void Init(VkDeviceSize size) { m_Size = size; }
5205
5206 // Validates all data structures inside this object. If not valid, returns false.
5207 virtual bool Validate() const = 0;
GetSize()5208 VkDeviceSize GetSize() const { return m_Size; }
5209 virtual size_t GetAllocationCount() const = 0;
5210 virtual VkDeviceSize GetSumFreeSize() const = 0;
5211 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5212 // Returns true if this block is empty - contains only single free suballocation.
5213 virtual bool IsEmpty() const = 0;
5214
5215 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5216 // Shouldn't modify blockCount.
5217 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5218
5219 #if VMA_STATS_STRING_ENABLED
5220 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5221 #endif
5222
5223 // Tries to find a place for suballocation with given parameters inside this block.
5224 // If succeeded, fills pAllocationRequest and returns true.
5225 // If failed, returns false.
5226 virtual bool CreateAllocationRequest(
5227 uint32_t currentFrameIndex,
5228 uint32_t frameInUseCount,
5229 VkDeviceSize bufferImageGranularity,
5230 VkDeviceSize allocSize,
5231 VkDeviceSize allocAlignment,
5232 bool upperAddress,
5233 VmaSuballocationType allocType,
5234 bool canMakeOtherLost,
5235 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5236 uint32_t strategy,
5237 VmaAllocationRequest* pAllocationRequest) = 0;
5238
5239 virtual bool MakeRequestedAllocationsLost(
5240 uint32_t currentFrameIndex,
5241 uint32_t frameInUseCount,
5242 VmaAllocationRequest* pAllocationRequest) = 0;
5243
5244 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5245
5246 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5247
5248 // Makes actual allocation based on request. Request must already be checked and valid.
5249 virtual void Alloc(
5250 const VmaAllocationRequest& request,
5251 VmaSuballocationType type,
5252 VkDeviceSize allocSize,
5253 bool upperAddress,
5254 VmaAllocation hAllocation) = 0;
5255
5256 // Frees suballocation assigned to given memory region.
5257 virtual void Free(const VmaAllocation allocation) = 0;
5258 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5259
5260 // Tries to resize (grow or shrink) space for given allocation, in place.
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)5261 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5262
5263 protected:
GetAllocationCallbacks()5264 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5265
5266 #if VMA_STATS_STRING_ENABLED
5267 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5268 VkDeviceSize unusedBytes,
5269 size_t allocationCount,
5270 size_t unusedRangeCount) const;
5271 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5272 VkDeviceSize offset,
5273 VmaAllocation hAllocation) const;
5274 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5275 VkDeviceSize offset,
5276 VkDeviceSize size) const;
5277 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5278 #endif
5279
5280 private:
5281 VkDeviceSize m_Size;
5282 const VkAllocationCallbacks* m_pAllocationCallbacks;
5283 };
5284
5285 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
5286 VMA_ASSERT(0 && "Validation failed: " #cond); \
5287 return false; \
5288 } } while(false)
5289
5290 class VmaBlockMetadata_Generic : public VmaBlockMetadata
5291 {
5292 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5293 public:
5294 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5295 virtual ~VmaBlockMetadata_Generic();
5296 virtual void Init(VkDeviceSize size);
5297
5298 virtual bool Validate() const;
GetAllocationCount()5299 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()5300 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5301 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5302 virtual bool IsEmpty() const;
5303
5304 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5305 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5306
5307 #if VMA_STATS_STRING_ENABLED
5308 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5309 #endif
5310
5311 virtual bool CreateAllocationRequest(
5312 uint32_t currentFrameIndex,
5313 uint32_t frameInUseCount,
5314 VkDeviceSize bufferImageGranularity,
5315 VkDeviceSize allocSize,
5316 VkDeviceSize allocAlignment,
5317 bool upperAddress,
5318 VmaSuballocationType allocType,
5319 bool canMakeOtherLost,
5320 uint32_t strategy,
5321 VmaAllocationRequest* pAllocationRequest);
5322
5323 virtual bool MakeRequestedAllocationsLost(
5324 uint32_t currentFrameIndex,
5325 uint32_t frameInUseCount,
5326 VmaAllocationRequest* pAllocationRequest);
5327
5328 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5329
5330 virtual VkResult CheckCorruption(const void* pBlockData);
5331
5332 virtual void Alloc(
5333 const VmaAllocationRequest& request,
5334 VmaSuballocationType type,
5335 VkDeviceSize allocSize,
5336 bool upperAddress,
5337 VmaAllocation hAllocation);
5338
5339 virtual void Free(const VmaAllocation allocation);
5340 virtual void FreeAtOffset(VkDeviceSize offset);
5341
5342 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5343
5344 ////////////////////////////////////////////////////////////////////////////////
5345 // For defragmentation
5346
5347 bool IsBufferImageGranularityConflictPossible(
5348 VkDeviceSize bufferImageGranularity,
5349 VmaSuballocationType& inOutPrevSuballocType) const;
5350
5351 private:
5352 friend class VmaDefragmentationAlgorithm_Generic;
5353 friend class VmaDefragmentationAlgorithm_Fast;
5354
5355 uint32_t m_FreeCount;
5356 VkDeviceSize m_SumFreeSize;
5357 VmaSuballocationList m_Suballocations;
5358 // Suballocations that are free and have size greater than certain threshold.
5359 // Sorted by size, ascending.
5360 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5361
5362 bool ValidateFreeSuballocationList() const;
5363
5364 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5365 // If yes, fills pOffset and returns true. If no, returns false.
5366 bool CheckAllocation(
5367 uint32_t currentFrameIndex,
5368 uint32_t frameInUseCount,
5369 VkDeviceSize bufferImageGranularity,
5370 VkDeviceSize allocSize,
5371 VkDeviceSize allocAlignment,
5372 VmaSuballocationType allocType,
5373 VmaSuballocationList::const_iterator suballocItem,
5374 bool canMakeOtherLost,
5375 VkDeviceSize* pOffset,
5376 size_t* itemsToMakeLostCount,
5377 VkDeviceSize* pSumFreeSize,
5378 VkDeviceSize* pSumItemSize) const;
5379 // Given free suballocation, it merges it with following one, which must also be free.
5380 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5381 // Releases given suballocation, making it free.
5382 // Merges it with adjacent free suballocations if applicable.
5383 // Returns iterator to new free suballocation at this place.
5384 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5385 // Given free suballocation, it inserts it into sorted list of
5386 // m_FreeSuballocationsBySize if it's suitable.
5387 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5388 // Given free suballocation, it removes it from sorted list of
5389 // m_FreeSuballocationsBySize if it's suitable.
5390 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5391 };
5392
5393 /*
5394 Allocations and their references in internal data structure look like this:
5395
5396 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5397
5398 0 +-------+
5399 | |
5400 | |
5401 | |
5402 +-------+
5403 | Alloc | 1st[m_1stNullItemsBeginCount]
5404 +-------+
5405 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5406 +-------+
5407 | ... |
5408 +-------+
5409 | Alloc | 1st[1st.size() - 1]
5410 +-------+
5411 | |
5412 | |
5413 | |
5414 GetSize() +-------+
5415
5416 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5417
5418 0 +-------+
5419 | Alloc | 2nd[0]
5420 +-------+
5421 | Alloc | 2nd[1]
5422 +-------+
5423 | ... |
5424 +-------+
5425 | Alloc | 2nd[2nd.size() - 1]
5426 +-------+
5427 | |
5428 | |
5429 | |
5430 +-------+
5431 | Alloc | 1st[m_1stNullItemsBeginCount]
5432 +-------+
5433 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5434 +-------+
5435 | ... |
5436 +-------+
5437 | Alloc | 1st[1st.size() - 1]
5438 +-------+
5439 | |
5440 GetSize() +-------+
5441
5442 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5443
5444 0 +-------+
5445 | |
5446 | |
5447 | |
5448 +-------+
5449 | Alloc | 1st[m_1stNullItemsBeginCount]
5450 +-------+
5451 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5452 +-------+
5453 | ... |
5454 +-------+
5455 | Alloc | 1st[1st.size() - 1]
5456 +-------+
5457 | |
5458 | |
5459 | |
5460 +-------+
5461 | Alloc | 2nd[2nd.size() - 1]
5462 +-------+
5463 | ... |
5464 +-------+
5465 | Alloc | 2nd[1]
5466 +-------+
5467 | Alloc | 2nd[0]
5468 GetSize() +-------+
5469
5470 */
5471 class VmaBlockMetadata_Linear : public VmaBlockMetadata
5472 {
5473 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5474 public:
5475 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5476 virtual ~VmaBlockMetadata_Linear();
5477 virtual void Init(VkDeviceSize size);
5478
5479 virtual bool Validate() const;
5480 virtual size_t GetAllocationCount() const;
GetSumFreeSize()5481 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5482 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5483 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5484
5485 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5486 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5487
5488 #if VMA_STATS_STRING_ENABLED
5489 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5490 #endif
5491
5492 virtual bool CreateAllocationRequest(
5493 uint32_t currentFrameIndex,
5494 uint32_t frameInUseCount,
5495 VkDeviceSize bufferImageGranularity,
5496 VkDeviceSize allocSize,
5497 VkDeviceSize allocAlignment,
5498 bool upperAddress,
5499 VmaSuballocationType allocType,
5500 bool canMakeOtherLost,
5501 uint32_t strategy,
5502 VmaAllocationRequest* pAllocationRequest);
5503
5504 virtual bool MakeRequestedAllocationsLost(
5505 uint32_t currentFrameIndex,
5506 uint32_t frameInUseCount,
5507 VmaAllocationRequest* pAllocationRequest);
5508
5509 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5510
5511 virtual VkResult CheckCorruption(const void* pBlockData);
5512
5513 virtual void Alloc(
5514 const VmaAllocationRequest& request,
5515 VmaSuballocationType type,
5516 VkDeviceSize allocSize,
5517 bool upperAddress,
5518 VmaAllocation hAllocation);
5519
5520 virtual void Free(const VmaAllocation allocation);
5521 virtual void FreeAtOffset(VkDeviceSize offset);
5522
5523 private:
5524 /*
5525 There are two suballocation vectors, used in ping-pong way.
5526 The one with index m_1stVectorIndex is called 1st.
5527 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5528 2nd can be non-empty only when 1st is not empty.
5529 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5530 */
5531 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5532
5533 enum SECOND_VECTOR_MODE
5534 {
5535 SECOND_VECTOR_EMPTY,
5536 /*
5537 Suballocations in 2nd vector are created later than the ones in 1st, but they
5538 all have smaller offset.
5539 */
5540 SECOND_VECTOR_RING_BUFFER,
5541 /*
5542 Suballocations in 2nd vector are upper side of double stack.
5543 They all have offsets higher than those in 1st vector.
5544 Top of this stack means smaller offsets, but higher indices in this vector.
5545 */
5546 SECOND_VECTOR_DOUBLE_STACK,
5547 };
5548
5549 VkDeviceSize m_SumFreeSize;
5550 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5551 uint32_t m_1stVectorIndex;
5552 SECOND_VECTOR_MODE m_2ndVectorMode;
5553
AccessSuballocations1st()5554 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5555 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()5556 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()5557 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5558
5559 // Number of items in 1st vector with hAllocation = null at the beginning.
5560 size_t m_1stNullItemsBeginCount;
5561 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5562 size_t m_1stNullItemsMiddleCount;
5563 // Number of items in 2nd vector with hAllocation = null.
5564 size_t m_2ndNullItemsCount;
5565
5566 bool ShouldCompact1st() const;
5567 void CleanupAfterFree();
5568 };
5569
5570 /*
5571 - GetSize() is the original size of allocated memory block.
5572 - m_UsableSize is this size aligned down to a power of two.
5573 All allocations and calculations happen relative to m_UsableSize.
5574 - GetUnusableSize() is the difference between them.
5575 It is repoted as separate, unused range, not available for allocations.
5576
5577 Node at level 0 has size = m_UsableSize.
5578 Each next level contains nodes with size 2 times smaller than current level.
5579 m_LevelCount is the maximum number of levels to use in the current object.
5580 */
5581 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5582 {
5583 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5584 public:
5585 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5586 virtual ~VmaBlockMetadata_Buddy();
5587 virtual void Init(VkDeviceSize size);
5588
5589 virtual bool Validate() const;
GetAllocationCount()5590 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()5591 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5592 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()5593 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5594
5595 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5596 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5597
5598 #if VMA_STATS_STRING_ENABLED
5599 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5600 #endif
5601
5602 virtual bool CreateAllocationRequest(
5603 uint32_t currentFrameIndex,
5604 uint32_t frameInUseCount,
5605 VkDeviceSize bufferImageGranularity,
5606 VkDeviceSize allocSize,
5607 VkDeviceSize allocAlignment,
5608 bool upperAddress,
5609 VmaSuballocationType allocType,
5610 bool canMakeOtherLost,
5611 uint32_t strategy,
5612 VmaAllocationRequest* pAllocationRequest);
5613
5614 virtual bool MakeRequestedAllocationsLost(
5615 uint32_t currentFrameIndex,
5616 uint32_t frameInUseCount,
5617 VmaAllocationRequest* pAllocationRequest);
5618
5619 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5620
CheckCorruption(const void * pBlockData)5621 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5622
5623 virtual void Alloc(
5624 const VmaAllocationRequest& request,
5625 VmaSuballocationType type,
5626 VkDeviceSize allocSize,
5627 bool upperAddress,
5628 VmaAllocation hAllocation);
5629
Free(const VmaAllocation allocation)5630 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)5631 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5632
5633 private:
5634 static const VkDeviceSize MIN_NODE_SIZE = 32;
5635 static const size_t MAX_LEVELS = 30;
5636
5637 struct ValidationContext
5638 {
5639 size_t calculatedAllocationCount;
5640 size_t calculatedFreeCount;
5641 VkDeviceSize calculatedSumFreeSize;
5642
ValidationContextValidationContext5643 ValidationContext() :
5644 calculatedAllocationCount(0),
5645 calculatedFreeCount(0),
5646 calculatedSumFreeSize(0) { }
5647 };
5648
5649 struct Node
5650 {
5651 VkDeviceSize offset;
5652 enum TYPE
5653 {
5654 TYPE_FREE,
5655 TYPE_ALLOCATION,
5656 TYPE_SPLIT,
5657 TYPE_COUNT
5658 } type;
5659 Node* parent;
5660 Node* buddy;
5661
5662 union
5663 {
5664 struct
5665 {
5666 Node* prev;
5667 Node* next;
5668 } free;
5669 struct
5670 {
5671 VmaAllocation alloc;
5672 } allocation;
5673 struct
5674 {
5675 Node* leftChild;
5676 } split;
5677 };
5678 };
5679
5680 // Size of the memory block aligned down to a power of two.
5681 VkDeviceSize m_UsableSize;
5682 uint32_t m_LevelCount;
5683
5684 Node* m_Root;
5685 struct {
5686 Node* front;
5687 Node* back;
5688 } m_FreeList[MAX_LEVELS];
5689 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5690 size_t m_AllocationCount;
5691 // Number of nodes in the tree with type == TYPE_FREE.
5692 size_t m_FreeCount;
5693 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5694 VkDeviceSize m_SumFreeSize;
5695
GetUnusableSize()5696 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5697 void DeleteNode(Node* node);
5698 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5699 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)5700 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5701 // Alloc passed just for validation. Can be null.
5702 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5703 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5704 // Adds node to the front of FreeList at given level.
5705 // node->type must be FREE.
5706 // node->free.prev, next can be undefined.
5707 void AddToFreeListFront(uint32_t level, Node* node);
5708 // Removes node from FreeList at given level.
5709 // node->type must be FREE.
5710 // node->free.prev, next stay untouched.
5711 void RemoveFromFreeList(uint32_t level, Node* node);
5712
5713 #if VMA_STATS_STRING_ENABLED
5714 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5715 #endif
5716 };
5717
5718 /*
5719 Represents a single block of device memory (`VkDeviceMemory`) with all the
5720 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5721
5722 Thread-safety: This class must be externally synchronized.
5723 */
5724 class VmaDeviceMemoryBlock
5725 {
5726 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5727 public:
5728 VmaBlockMetadata* m_pMetadata;
5729
5730 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5731
~VmaDeviceMemoryBlock()5732 ~VmaDeviceMemoryBlock()
5733 {
5734 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5735 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5736 }
5737
5738 // Always call after construction.
5739 void Init(
5740 VmaAllocator hAllocator,
5741 uint32_t newMemoryTypeIndex,
5742 VkDeviceMemory newMemory,
5743 VkDeviceSize newSize,
5744 uint32_t id,
5745 uint32_t algorithm);
5746 // Always call before destruction.
5747 void Destroy(VmaAllocator allocator);
5748
GetDeviceMemory()5749 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5750 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5751 uint32_t GetId() const { return m_Id; }
GetMappedData()5752 void* GetMappedData() const { return m_pMappedData; }
5753
5754 // Validates all data structures inside this object. If not valid, returns false.
5755 bool Validate() const;
5756
5757 VkResult CheckCorruption(VmaAllocator hAllocator);
5758
5759 // ppData can be null.
5760 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5761 void Unmap(VmaAllocator hAllocator, uint32_t count);
5762
5763 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5764 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5765
5766 VkResult BindBufferMemory(
5767 const VmaAllocator hAllocator,
5768 const VmaAllocation hAllocation,
5769 VkBuffer hBuffer);
5770 VkResult BindImageMemory(
5771 const VmaAllocator hAllocator,
5772 const VmaAllocation hAllocation,
5773 VkImage hImage);
5774
5775 private:
5776 uint32_t m_MemoryTypeIndex;
5777 uint32_t m_Id;
5778 VkDeviceMemory m_hMemory;
5779
5780 /*
5781 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5782 Also protects m_MapCount, m_pMappedData.
5783 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5784 */
5785 VMA_MUTEX m_Mutex;
5786 uint32_t m_MapCount;
5787 void* m_pMappedData;
5788 };
5789
5790 struct VmaPointerLess
5791 {
operatorVmaPointerLess5792 bool operator()(const void* lhs, const void* rhs) const
5793 {
5794 return lhs < rhs;
5795 }
5796 };
5797
5798 struct VmaDefragmentationMove
5799 {
5800 size_t srcBlockIndex;
5801 size_t dstBlockIndex;
5802 VkDeviceSize srcOffset;
5803 VkDeviceSize dstOffset;
5804 VkDeviceSize size;
5805 };
5806
5807 class VmaDefragmentationAlgorithm;
5808
5809 /*
5810 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5811 Vulkan memory type.
5812
5813 Synchronized internally with a mutex.
5814 */
5815 struct VmaBlockVector
5816 {
5817 VMA_CLASS_NO_COPY(VmaBlockVector)
5818 public:
5819 VmaBlockVector(
5820 VmaAllocator hAllocator,
5821 uint32_t memoryTypeIndex,
5822 VkDeviceSize preferredBlockSize,
5823 size_t minBlockCount,
5824 size_t maxBlockCount,
5825 VkDeviceSize bufferImageGranularity,
5826 uint32_t frameInUseCount,
5827 bool isCustomPool,
5828 bool explicitBlockSize,
5829 uint32_t algorithm);
5830 ~VmaBlockVector();
5831
5832 VkResult CreateMinBlocks();
5833
GetMemoryTypeIndexVmaBlockVector5834 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector5835 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector5836 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector5837 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector5838 uint32_t GetAlgorithm() const { return m_Algorithm; }
5839
5840 void GetPoolStats(VmaPoolStats* pStats);
5841
IsEmptyVmaBlockVector5842 bool IsEmpty() const { return m_Blocks.empty(); }
5843 bool IsCorruptionDetectionEnabled() const;
5844
5845 VkResult Allocate(
5846 VmaPool hCurrentPool,
5847 uint32_t currentFrameIndex,
5848 VkDeviceSize size,
5849 VkDeviceSize alignment,
5850 const VmaAllocationCreateInfo& createInfo,
5851 VmaSuballocationType suballocType,
5852 size_t allocationCount,
5853 VmaAllocation* pAllocations);
5854
5855 void Free(
5856 VmaAllocation hAllocation);
5857
5858 // Adds statistics of this BlockVector to pStats.
5859 void AddStats(VmaStats* pStats);
5860
5861 #if VMA_STATS_STRING_ENABLED
5862 void PrintDetailedMap(class VmaJsonWriter& json);
5863 #endif
5864
5865 void MakePoolAllocationsLost(
5866 uint32_t currentFrameIndex,
5867 size_t* pLostAllocationCount);
5868 VkResult CheckCorruption();
5869
5870 // Saves results in pCtx->res.
5871 void Defragment(
5872 class VmaBlockVectorDefragmentationContext* pCtx,
5873 VmaDefragmentationStats* pStats,
5874 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5875 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5876 VkCommandBuffer commandBuffer);
5877 void DefragmentationEnd(
5878 class VmaBlockVectorDefragmentationContext* pCtx,
5879 VmaDefragmentationStats* pStats);
5880
5881 ////////////////////////////////////////////////////////////////////////////////
5882 // To be used only while the m_Mutex is locked. Used during defragmentation.
5883
GetBlockCountVmaBlockVector5884 size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector5885 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5886 size_t CalcAllocationCount() const;
5887 bool IsBufferImageGranularityConflictPossible() const;
5888
5889 private:
5890 friend class VmaDefragmentationAlgorithm_Generic;
5891
5892 const VmaAllocator m_hAllocator;
5893 const uint32_t m_MemoryTypeIndex;
5894 const VkDeviceSize m_PreferredBlockSize;
5895 const size_t m_MinBlockCount;
5896 const size_t m_MaxBlockCount;
5897 const VkDeviceSize m_BufferImageGranularity;
5898 const uint32_t m_FrameInUseCount;
5899 const bool m_IsCustomPool;
5900 const bool m_ExplicitBlockSize;
5901 const uint32_t m_Algorithm;
5902 /* There can be at most one allocation that is completely empty - a
5903 hysteresis to avoid pessimistic case of alternating creation and destruction
5904 of a VkDeviceMemory. */
5905 bool m_HasEmptyBlock;
5906 VMA_RW_MUTEX m_Mutex;
5907 // Incrementally sorted by sumFreeSize, ascending.
5908 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5909 uint32_t m_NextBlockId;
5910
5911 VkDeviceSize CalcMaxBlockSize() const;
5912
5913 // Finds and removes given block from vector.
5914 void Remove(VmaDeviceMemoryBlock* pBlock);
5915
5916 // Performs single step in sorting m_Blocks. They may not be fully sorted
5917 // after this call.
5918 void IncrementallySortBlocks();
5919
5920 VkResult AllocatePage(
5921 VmaPool hCurrentPool,
5922 uint32_t currentFrameIndex,
5923 VkDeviceSize size,
5924 VkDeviceSize alignment,
5925 const VmaAllocationCreateInfo& createInfo,
5926 VmaSuballocationType suballocType,
5927 VmaAllocation* pAllocation);
5928
5929 // To be used only without CAN_MAKE_OTHER_LOST flag.
5930 VkResult AllocateFromBlock(
5931 VmaDeviceMemoryBlock* pBlock,
5932 VmaPool hCurrentPool,
5933 uint32_t currentFrameIndex,
5934 VkDeviceSize size,
5935 VkDeviceSize alignment,
5936 VmaAllocationCreateFlags allocFlags,
5937 void* pUserData,
5938 VmaSuballocationType suballocType,
5939 uint32_t strategy,
5940 VmaAllocation* pAllocation);
5941
5942 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5943
5944 // Saves result to pCtx->res.
5945 void ApplyDefragmentationMovesCpu(
5946 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5947 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5948 // Saves result to pCtx->res.
5949 void ApplyDefragmentationMovesGpu(
5950 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5951 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5952 VkCommandBuffer commandBuffer);
5953
5954 /*
5955 Used during defragmentation. pDefragmentationStats is optional. It's in/out
5956 - updated with new data.
5957 */
5958 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5959 };
5960
5961 struct VmaPool_T
5962 {
5963 VMA_CLASS_NO_COPY(VmaPool_T)
5964 public:
5965 VmaBlockVector m_BlockVector;
5966
5967 VmaPool_T(
5968 VmaAllocator hAllocator,
5969 const VmaPoolCreateInfo& createInfo,
5970 VkDeviceSize preferredBlockSize);
5971 ~VmaPool_T();
5972
GetIdVmaPool_T5973 uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T5974 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5975
5976 #if VMA_STATS_STRING_ENABLED
5977 //void PrintDetailedMap(class VmaStringBuilder& sb);
5978 #endif
5979
5980 private:
5981 uint32_t m_Id;
5982 };
5983
5984 /*
5985 Performs defragmentation:
5986
5987 - Updates `pBlockVector->m_pMetadata`.
5988 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5989 - Does not move actual data, only returns requested moves as `moves`.
5990 */
5991 class VmaDefragmentationAlgorithm
5992 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)5993 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5994 public:
5995 VmaDefragmentationAlgorithm(
5996 VmaAllocator hAllocator,
5997 VmaBlockVector* pBlockVector,
5998 uint32_t currentFrameIndex) :
5999 m_hAllocator(hAllocator),
6000 m_pBlockVector(pBlockVector),
6001 m_CurrentFrameIndex(currentFrameIndex)
6002 {
6003 }
~VmaDefragmentationAlgorithm()6004 virtual ~VmaDefragmentationAlgorithm()
6005 {
6006 }
6007
6008 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
6009 virtual void AddAll() = 0;
6010
6011 virtual VkResult Defragment(
6012 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6013 VkDeviceSize maxBytesToMove,
6014 uint32_t maxAllocationsToMove) = 0;
6015
6016 virtual VkDeviceSize GetBytesMoved() const = 0;
6017 virtual uint32_t GetAllocationsMoved() const = 0;
6018
6019 protected:
6020 VmaAllocator const m_hAllocator;
6021 VmaBlockVector* const m_pBlockVector;
6022 const uint32_t m_CurrentFrameIndex;
6023
6024 struct AllocationInfo
6025 {
6026 VmaAllocation m_hAllocation;
6027 VkBool32* m_pChanged;
6028
AllocationInfoAllocationInfo6029 AllocationInfo() :
6030 m_hAllocation(VK_NULL_HANDLE),
6031 m_pChanged(VMA_NULL)
6032 {
6033 }
AllocationInfoAllocationInfo6034 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6035 m_hAllocation(hAlloc),
6036 m_pChanged(pChanged)
6037 {
6038 }
6039 };
6040 };
6041
6042 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6043 {
6044 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6045 public:
6046 VmaDefragmentationAlgorithm_Generic(
6047 VmaAllocator hAllocator,
6048 VmaBlockVector* pBlockVector,
6049 uint32_t currentFrameIndex,
6050 bool overlappingMoveSupported);
6051 virtual ~VmaDefragmentationAlgorithm_Generic();
6052
6053 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6054 virtual void AddAll() { m_AllAllocations = true; }
6055
6056 virtual VkResult Defragment(
6057 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6058 VkDeviceSize maxBytesToMove,
6059 uint32_t maxAllocationsToMove);
6060
GetBytesMoved()6061 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6062 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6063
6064 private:
6065 uint32_t m_AllocationCount;
6066 bool m_AllAllocations;
6067
6068 VkDeviceSize m_BytesMoved;
6069 uint32_t m_AllocationsMoved;
6070
6071 struct AllocationInfoSizeGreater
6072 {
operatorAllocationInfoSizeGreater6073 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6074 {
6075 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6076 }
6077 };
6078
6079 struct AllocationInfoOffsetGreater
6080 {
operatorAllocationInfoOffsetGreater6081 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6082 {
6083 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6084 }
6085 };
6086
6087 struct BlockInfo
6088 {
6089 size_t m_OriginalBlockIndex;
6090 VmaDeviceMemoryBlock* m_pBlock;
6091 bool m_HasNonMovableAllocations;
6092 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6093
BlockInfoBlockInfo6094 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6095 m_OriginalBlockIndex(SIZE_MAX),
6096 m_pBlock(VMA_NULL),
6097 m_HasNonMovableAllocations(true),
6098 m_Allocations(pAllocationCallbacks)
6099 {
6100 }
6101
CalcHasNonMovableAllocationsBlockInfo6102 void CalcHasNonMovableAllocations()
6103 {
6104 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6105 const size_t defragmentAllocCount = m_Allocations.size();
6106 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6107 }
6108
SortAllocationsBySizeDescendingBlockInfo6109 void SortAllocationsBySizeDescending()
6110 {
6111 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6112 }
6113
SortAllocationsByOffsetDescendingBlockInfo6114 void SortAllocationsByOffsetDescending()
6115 {
6116 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6117 }
6118 };
6119
6120 struct BlockPointerLess
6121 {
operatorBlockPointerLess6122 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6123 {
6124 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6125 }
operatorBlockPointerLess6126 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6127 {
6128 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6129 }
6130 };
6131
6132 // 1. Blocks with some non-movable allocations go first.
6133 // 2. Blocks with smaller sumFreeSize go first.
6134 struct BlockInfoCompareMoveDestination
6135 {
operatorBlockInfoCompareMoveDestination6136 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6137 {
6138 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6139 {
6140 return true;
6141 }
6142 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6143 {
6144 return false;
6145 }
6146 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6147 {
6148 return true;
6149 }
6150 return false;
6151 }
6152 };
6153
6154 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6155 BlockInfoVector m_Blocks;
6156
6157 VkResult DefragmentRound(
6158 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6159 VkDeviceSize maxBytesToMove,
6160 uint32_t maxAllocationsToMove);
6161
6162 size_t CalcBlocksWithNonMovableCount() const;
6163
6164 static bool MoveMakesSense(
6165 size_t dstBlockIndex, VkDeviceSize dstOffset,
6166 size_t srcBlockIndex, VkDeviceSize srcOffset);
6167 };
6168
6169 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6170 {
6171 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6172 public:
6173 VmaDefragmentationAlgorithm_Fast(
6174 VmaAllocator hAllocator,
6175 VmaBlockVector* pBlockVector,
6176 uint32_t currentFrameIndex,
6177 bool overlappingMoveSupported);
6178 virtual ~VmaDefragmentationAlgorithm_Fast();
6179
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)6180 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()6181 virtual void AddAll() { m_AllAllocations = true; }
6182
6183 virtual VkResult Defragment(
6184 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6185 VkDeviceSize maxBytesToMove,
6186 uint32_t maxAllocationsToMove);
6187
GetBytesMoved()6188 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()6189 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6190
6191 private:
6192 struct BlockInfo
6193 {
6194 size_t origBlockIndex;
6195 };
6196
6197 class FreeSpaceDatabase
6198 {
6199 public:
FreeSpaceDatabase()6200 FreeSpaceDatabase()
6201 {
6202 FreeSpace s = {};
6203 s.blockInfoIndex = SIZE_MAX;
6204 for(size_t i = 0; i < MAX_COUNT; ++i)
6205 {
6206 m_FreeSpaces[i] = s;
6207 }
6208 }
6209
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)6210 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6211 {
6212 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6213 {
6214 return;
6215 }
6216
6217 // Find first invalid or the smallest structure.
6218 size_t bestIndex = SIZE_MAX;
6219 for(size_t i = 0; i < MAX_COUNT; ++i)
6220 {
6221 // Empty structure.
6222 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6223 {
6224 bestIndex = i;
6225 break;
6226 }
6227 if(m_FreeSpaces[i].size < size &&
6228 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6229 {
6230 bestIndex = i;
6231 }
6232 }
6233
6234 if(bestIndex != SIZE_MAX)
6235 {
6236 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6237 m_FreeSpaces[bestIndex].offset = offset;
6238 m_FreeSpaces[bestIndex].size = size;
6239 }
6240 }
6241
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)6242 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6243 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6244 {
6245 size_t bestIndex = SIZE_MAX;
6246 VkDeviceSize bestFreeSpaceAfter = 0;
6247 for(size_t i = 0; i < MAX_COUNT; ++i)
6248 {
6249 // Structure is valid.
6250 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6251 {
6252 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6253 // Allocation fits into this structure.
6254 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6255 {
6256 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6257 (dstOffset + size);
6258 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6259 {
6260 bestIndex = i;
6261 bestFreeSpaceAfter = freeSpaceAfter;
6262 }
6263 }
6264 }
6265 }
6266
6267 if(bestIndex != SIZE_MAX)
6268 {
6269 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6270 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6271
6272 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6273 {
6274 // Leave this structure for remaining empty space.
6275 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6276 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6277 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6278 }
6279 else
6280 {
6281 // This structure becomes invalid.
6282 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6283 }
6284
6285 return true;
6286 }
6287
6288 return false;
6289 }
6290
6291 private:
6292 static const size_t MAX_COUNT = 4;
6293
6294 struct FreeSpace
6295 {
6296 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6297 VkDeviceSize offset;
6298 VkDeviceSize size;
6299 } m_FreeSpaces[MAX_COUNT];
6300 };
6301
6302 const bool m_OverlappingMoveSupported;
6303
6304 uint32_t m_AllocationCount;
6305 bool m_AllAllocations;
6306
6307 VkDeviceSize m_BytesMoved;
6308 uint32_t m_AllocationsMoved;
6309
6310 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6311
6312 void PreprocessMetadata();
6313 void PostprocessMetadata();
6314 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6315 };
6316
6317 struct VmaBlockDefragmentationContext
6318 {
6319 enum BLOCK_FLAG
6320 {
6321 BLOCK_FLAG_USED = 0x00000001,
6322 };
6323 uint32_t flags;
6324 VkBuffer hBuffer;
6325
VmaBlockDefragmentationContextVmaBlockDefragmentationContext6326 VmaBlockDefragmentationContext() :
6327 flags(0),
6328 hBuffer(VK_NULL_HANDLE)
6329 {
6330 }
6331 };
6332
6333 class VmaBlockVectorDefragmentationContext
6334 {
6335 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6336 public:
6337 VkResult res;
6338 bool mutexLocked;
6339 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6340
6341 VmaBlockVectorDefragmentationContext(
6342 VmaAllocator hAllocator,
6343 VmaPool hCustomPool, // Optional.
6344 VmaBlockVector* pBlockVector,
6345 uint32_t currFrameIndex,
6346 uint32_t flags);
6347 ~VmaBlockVectorDefragmentationContext();
6348
GetCustomPool()6349 VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()6350 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()6351 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6352
6353 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()6354 void AddAll() { m_AllAllocations = true; }
6355
6356 void Begin(bool overlappingMoveSupported);
6357
6358 private:
6359 const VmaAllocator m_hAllocator;
6360 // Null if not from custom pool.
6361 const VmaPool m_hCustomPool;
6362 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6363 VmaBlockVector* const m_pBlockVector;
6364 const uint32_t m_CurrFrameIndex;
6365 //const uint32_t m_AlgorithmFlags;
6366 // Owner of this object.
6367 VmaDefragmentationAlgorithm* m_pAlgorithm;
6368
6369 struct AllocInfo
6370 {
6371 VmaAllocation hAlloc;
6372 VkBool32* pChanged;
6373 };
6374 // Used between constructor and Begin.
6375 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6376 bool m_AllAllocations;
6377 };
6378
6379 struct VmaDefragmentationContext_T
6380 {
6381 private:
6382 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6383 public:
6384 VmaDefragmentationContext_T(
6385 VmaAllocator hAllocator,
6386 uint32_t currFrameIndex,
6387 uint32_t flags,
6388 VmaDefragmentationStats* pStats);
6389 ~VmaDefragmentationContext_T();
6390
6391 void AddPools(uint32_t poolCount, VmaPool* pPools);
6392 void AddAllocations(
6393 uint32_t allocationCount,
6394 VmaAllocation* pAllocations,
6395 VkBool32* pAllocationsChanged);
6396
6397 /*
6398 Returns:
6399 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6400 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6401 - Negative value if error occured and object can be destroyed immediately.
6402 */
6403 VkResult Defragment(
6404 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6405 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6406 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6407
6408 private:
6409 const VmaAllocator m_hAllocator;
6410 const uint32_t m_CurrFrameIndex;
6411 const uint32_t m_Flags;
6412 VmaDefragmentationStats* const m_pStats;
6413 // Owner of these objects.
6414 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6415 // Owner of these objects.
6416 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6417 };
6418
6419 #if VMA_RECORDING_ENABLED
6420
6421 class VmaRecorder
6422 {
6423 public:
6424 VmaRecorder();
6425 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6426 void WriteConfiguration(
6427 const VkPhysicalDeviceProperties& devProps,
6428 const VkPhysicalDeviceMemoryProperties& memProps,
6429 bool dedicatedAllocationExtensionEnabled);
6430 ~VmaRecorder();
6431
6432 void RecordCreateAllocator(uint32_t frameIndex);
6433 void RecordDestroyAllocator(uint32_t frameIndex);
6434 void RecordCreatePool(uint32_t frameIndex,
6435 const VmaPoolCreateInfo& createInfo,
6436 VmaPool pool);
6437 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6438 void RecordAllocateMemory(uint32_t frameIndex,
6439 const VkMemoryRequirements& vkMemReq,
6440 const VmaAllocationCreateInfo& createInfo,
6441 VmaAllocation allocation);
6442 void RecordAllocateMemoryPages(uint32_t frameIndex,
6443 const VkMemoryRequirements& vkMemReq,
6444 const VmaAllocationCreateInfo& createInfo,
6445 uint64_t allocationCount,
6446 const VmaAllocation* pAllocations);
6447 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6448 const VkMemoryRequirements& vkMemReq,
6449 bool requiresDedicatedAllocation,
6450 bool prefersDedicatedAllocation,
6451 const VmaAllocationCreateInfo& createInfo,
6452 VmaAllocation allocation);
6453 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6454 const VkMemoryRequirements& vkMemReq,
6455 bool requiresDedicatedAllocation,
6456 bool prefersDedicatedAllocation,
6457 const VmaAllocationCreateInfo& createInfo,
6458 VmaAllocation allocation);
6459 void RecordFreeMemory(uint32_t frameIndex,
6460 VmaAllocation allocation);
6461 void RecordFreeMemoryPages(uint32_t frameIndex,
6462 uint64_t allocationCount,
6463 const VmaAllocation* pAllocations);
6464 void RecordResizeAllocation(
6465 uint32_t frameIndex,
6466 VmaAllocation allocation,
6467 VkDeviceSize newSize);
6468 void RecordSetAllocationUserData(uint32_t frameIndex,
6469 VmaAllocation allocation,
6470 const void* pUserData);
6471 void RecordCreateLostAllocation(uint32_t frameIndex,
6472 VmaAllocation allocation);
6473 void RecordMapMemory(uint32_t frameIndex,
6474 VmaAllocation allocation);
6475 void RecordUnmapMemory(uint32_t frameIndex,
6476 VmaAllocation allocation);
6477 void RecordFlushAllocation(uint32_t frameIndex,
6478 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6479 void RecordInvalidateAllocation(uint32_t frameIndex,
6480 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6481 void RecordCreateBuffer(uint32_t frameIndex,
6482 const VkBufferCreateInfo& bufCreateInfo,
6483 const VmaAllocationCreateInfo& allocCreateInfo,
6484 VmaAllocation allocation);
6485 void RecordCreateImage(uint32_t frameIndex,
6486 const VkImageCreateInfo& imageCreateInfo,
6487 const VmaAllocationCreateInfo& allocCreateInfo,
6488 VmaAllocation allocation);
6489 void RecordDestroyBuffer(uint32_t frameIndex,
6490 VmaAllocation allocation);
6491 void RecordDestroyImage(uint32_t frameIndex,
6492 VmaAllocation allocation);
6493 void RecordTouchAllocation(uint32_t frameIndex,
6494 VmaAllocation allocation);
6495 void RecordGetAllocationInfo(uint32_t frameIndex,
6496 VmaAllocation allocation);
6497 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6498 VmaPool pool);
6499 void RecordDefragmentationBegin(uint32_t frameIndex,
6500 const VmaDefragmentationInfo2& info,
6501 VmaDefragmentationContext ctx);
6502 void RecordDefragmentationEnd(uint32_t frameIndex,
6503 VmaDefragmentationContext ctx);
6504
6505 private:
6506 struct CallParams
6507 {
6508 uint32_t threadId;
6509 double time;
6510 };
6511
6512 class UserDataString
6513 {
6514 public:
6515 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()6516 const char* GetString() const { return m_Str; }
6517
6518 private:
6519 char m_PtrStr[17];
6520 const char* m_Str;
6521 };
6522
6523 bool m_UseMutex;
6524 VmaRecordFlags m_Flags;
6525 FILE* m_File;
6526 VMA_MUTEX m_FileMutex;
6527 int64_t m_Freq;
6528 int64_t m_StartCounter;
6529
6530 void GetBasicParams(CallParams& outParams);
6531
6532 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6533 template<typename T>
PrintPointerList(uint64_t count,const T * pItems)6534 void PrintPointerList(uint64_t count, const T* pItems)
6535 {
6536 if(count)
6537 {
6538 fprintf(m_File, "%p", pItems[0]);
6539 for(uint64_t i = 1; i < count; ++i)
6540 {
6541 fprintf(m_File, " %p", pItems[i]);
6542 }
6543 }
6544 }
6545
6546 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6547 void Flush();
6548 };
6549
6550 #endif // #if VMA_RECORDING_ENABLED
6551
6552 // Main allocator object.
6553 struct VmaAllocator_T
6554 {
6555 VMA_CLASS_NO_COPY(VmaAllocator_T)
6556 public:
6557 bool m_UseMutex;
6558 bool m_UseKhrDedicatedAllocation;
6559 VkDevice m_hDevice;
6560 bool m_AllocationCallbacksSpecified;
6561 VkAllocationCallbacks m_AllocationCallbacks;
6562 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6563
6564 // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6565 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6566 VMA_MUTEX m_HeapSizeLimitMutex;
6567
6568 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6569 VkPhysicalDeviceMemoryProperties m_MemProps;
6570
6571 // Default pools.
6572 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6573
6574 // Each vector is sorted by memory (handle value).
6575 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6576 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6577 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6578
6579 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6580 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6581 ~VmaAllocator_T();
6582
GetAllocationCallbacksVmaAllocator_T6583 const VkAllocationCallbacks* GetAllocationCallbacks() const
6584 {
6585 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6586 }
GetVulkanFunctionsVmaAllocator_T6587 const VmaVulkanFunctions& GetVulkanFunctions() const
6588 {
6589 return m_VulkanFunctions;
6590 }
6591
GetBufferImageGranularityVmaAllocator_T6592 VkDeviceSize GetBufferImageGranularity() const
6593 {
6594 return VMA_MAX(
6595 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6596 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6597 }
6598
GetMemoryHeapCountVmaAllocator_T6599 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T6600 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6601
MemoryTypeIndexToHeapIndexVmaAllocator_T6602 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6603 {
6604 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6605 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6606 }
6607 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T6608 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6609 {
6610 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6611 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6612 }
6613 // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T6614 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6615 {
6616 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6617 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6618 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6619 }
6620
IsIntegratedGpuVmaAllocator_T6621 bool IsIntegratedGpu() const
6622 {
6623 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6624 }
6625
6626 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T6627 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6628 #endif
6629
6630 void GetBufferMemoryRequirements(
6631 VkBuffer hBuffer,
6632 VkMemoryRequirements& memReq,
6633 bool& requiresDedicatedAllocation,
6634 bool& prefersDedicatedAllocation) const;
6635 void GetImageMemoryRequirements(
6636 VkImage hImage,
6637 VkMemoryRequirements& memReq,
6638 bool& requiresDedicatedAllocation,
6639 bool& prefersDedicatedAllocation) const;
6640
6641 // Main allocation function.
6642 VkResult AllocateMemory(
6643 const VkMemoryRequirements& vkMemReq,
6644 bool requiresDedicatedAllocation,
6645 bool prefersDedicatedAllocation,
6646 VkBuffer dedicatedBuffer,
6647 VkImage dedicatedImage,
6648 const VmaAllocationCreateInfo& createInfo,
6649 VmaSuballocationType suballocType,
6650 size_t allocationCount,
6651 VmaAllocation* pAllocations);
6652
6653 // Main deallocation function.
6654 void FreeMemory(
6655 size_t allocationCount,
6656 const VmaAllocation* pAllocations);
6657
6658 VkResult ResizeAllocation(
6659 const VmaAllocation alloc,
6660 VkDeviceSize newSize);
6661
6662 void CalculateStats(VmaStats* pStats);
6663
6664 #if VMA_STATS_STRING_ENABLED
6665 void PrintDetailedMap(class VmaJsonWriter& json);
6666 #endif
6667
6668 VkResult DefragmentationBegin(
6669 const VmaDefragmentationInfo2& info,
6670 VmaDefragmentationStats* pStats,
6671 VmaDefragmentationContext* pContext);
6672 VkResult DefragmentationEnd(
6673 VmaDefragmentationContext context);
6674
6675 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6676 bool TouchAllocation(VmaAllocation hAllocation);
6677
6678 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6679 void DestroyPool(VmaPool pool);
6680 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6681
6682 void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T6683 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6684
6685 void MakePoolAllocationsLost(
6686 VmaPool hPool,
6687 size_t* pLostAllocationCount);
6688 VkResult CheckPoolCorruption(VmaPool hPool);
6689 VkResult CheckCorruption(uint32_t memoryTypeBits);
6690
6691 void CreateLostAllocation(VmaAllocation* pAllocation);
6692
6693 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6694 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6695
6696 VkResult Map(VmaAllocation hAllocation, void** ppData);
6697 void Unmap(VmaAllocation hAllocation);
6698
6699 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6700 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6701
6702 void FlushOrInvalidateAllocation(
6703 VmaAllocation hAllocation,
6704 VkDeviceSize offset, VkDeviceSize size,
6705 VMA_CACHE_OPERATION op);
6706
6707 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6708
6709 private:
6710 VkDeviceSize m_PreferredLargeHeapBlockSize;
6711
6712 VkPhysicalDevice m_PhysicalDevice;
6713 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6714
6715 VMA_RW_MUTEX m_PoolsMutex;
6716 // Protected by m_PoolsMutex. Sorted by pointer value.
6717 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6718 uint32_t m_NextPoolId;
6719
6720 VmaVulkanFunctions m_VulkanFunctions;
6721
6722 #if VMA_RECORDING_ENABLED
6723 VmaRecorder* m_pRecorder;
6724 #endif
6725
6726 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6727
6728 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6729
6730 VkResult AllocateMemoryOfType(
6731 VkDeviceSize size,
6732 VkDeviceSize alignment,
6733 bool dedicatedAllocation,
6734 VkBuffer dedicatedBuffer,
6735 VkImage dedicatedImage,
6736 const VmaAllocationCreateInfo& createInfo,
6737 uint32_t memTypeIndex,
6738 VmaSuballocationType suballocType,
6739 size_t allocationCount,
6740 VmaAllocation* pAllocations);
6741
6742 // Helper function only to be used inside AllocateDedicatedMemory.
6743 VkResult AllocateDedicatedMemoryPage(
6744 VkDeviceSize size,
6745 VmaSuballocationType suballocType,
6746 uint32_t memTypeIndex,
6747 const VkMemoryAllocateInfo& allocInfo,
6748 bool map,
6749 bool isUserDataString,
6750 void* pUserData,
6751 VmaAllocation* pAllocation);
6752
6753 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6754 VkResult AllocateDedicatedMemory(
6755 VkDeviceSize size,
6756 VmaSuballocationType suballocType,
6757 uint32_t memTypeIndex,
6758 bool map,
6759 bool isUserDataString,
6760 void* pUserData,
6761 VkBuffer dedicatedBuffer,
6762 VkImage dedicatedImage,
6763 size_t allocationCount,
6764 VmaAllocation* pAllocations);
6765
6766 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6767 void FreeDedicatedMemory(VmaAllocation allocation);
6768 };
6769
6770 ////////////////////////////////////////////////////////////////////////////////
6771 // Memory allocation #2 after VmaAllocator_T definition
6772
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)6773 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6774 {
6775 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6776 }
6777
VmaFree(VmaAllocator hAllocator,void * ptr)6778 static void VmaFree(VmaAllocator hAllocator, void* ptr)
6779 {
6780 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6781 }
6782
6783 template<typename T>
VmaAllocate(VmaAllocator hAllocator)6784 static T* VmaAllocate(VmaAllocator hAllocator)
6785 {
6786 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6787 }
6788
6789 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)6790 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6791 {
6792 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6793 }
6794
6795 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)6796 static void vma_delete(VmaAllocator hAllocator, T* ptr)
6797 {
6798 if(ptr != VMA_NULL)
6799 {
6800 ptr->~T();
6801 VmaFree(hAllocator, ptr);
6802 }
6803 }
6804
6805 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)6806 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6807 {
6808 if(ptr != VMA_NULL)
6809 {
6810 for(size_t i = count; i--; )
6811 ptr[i].~T();
6812 VmaFree(hAllocator, ptr);
6813 }
6814 }
6815
6816 ////////////////////////////////////////////////////////////////////////////////
6817 // VmaStringBuilder
6818
6819 #if VMA_STATS_STRING_ENABLED
6820
6821 class VmaStringBuilder
6822 {
6823 public:
VmaStringBuilder(VmaAllocator alloc)6824 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()6825 size_t GetLength() const { return m_Data.size(); }
GetData()6826 const char* GetData() const { return m_Data.data(); }
6827
Add(char ch)6828 void Add(char ch) { m_Data.push_back(ch); }
6829 void Add(const char* pStr);
AddNewLine()6830 void AddNewLine() { Add('\n'); }
6831 void AddNumber(uint32_t num);
6832 void AddNumber(uint64_t num);
6833 void AddPointer(const void* ptr);
6834
6835 private:
6836 VmaVector< char, VmaStlAllocator<char> > m_Data;
6837 };
6838
Add(const char * pStr)6839 void VmaStringBuilder::Add(const char* pStr)
6840 {
6841 const size_t strLen = strlen(pStr);
6842 if(strLen > 0)
6843 {
6844 const size_t oldCount = m_Data.size();
6845 m_Data.resize(oldCount + strLen);
6846 memcpy(m_Data.data() + oldCount, pStr, strLen);
6847 }
6848 }
6849
AddNumber(uint32_t num)6850 void VmaStringBuilder::AddNumber(uint32_t num)
6851 {
6852 char buf[11];
6853 VmaUint32ToStr(buf, sizeof(buf), num);
6854 Add(buf);
6855 }
6856
AddNumber(uint64_t num)6857 void VmaStringBuilder::AddNumber(uint64_t num)
6858 {
6859 char buf[21];
6860 VmaUint64ToStr(buf, sizeof(buf), num);
6861 Add(buf);
6862 }
6863
AddPointer(const void * ptr)6864 void VmaStringBuilder::AddPointer(const void* ptr)
6865 {
6866 char buf[21];
6867 VmaPtrToStr(buf, sizeof(buf), ptr);
6868 Add(buf);
6869 }
6870
6871 #endif // #if VMA_STATS_STRING_ENABLED
6872
6873 ////////////////////////////////////////////////////////////////////////////////
6874 // VmaJsonWriter
6875
6876 #if VMA_STATS_STRING_ENABLED
6877
6878 class VmaJsonWriter
6879 {
6880 VMA_CLASS_NO_COPY(VmaJsonWriter)
6881 public:
6882 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6883 ~VmaJsonWriter();
6884
6885 void BeginObject(bool singleLine = false);
6886 void EndObject();
6887
6888 void BeginArray(bool singleLine = false);
6889 void EndArray();
6890
6891 void WriteString(const char* pStr);
6892 void BeginString(const char* pStr = VMA_NULL);
6893 void ContinueString(const char* pStr);
6894 void ContinueString(uint32_t n);
6895 void ContinueString(uint64_t n);
6896 void ContinueString_Pointer(const void* ptr);
6897 void EndString(const char* pStr = VMA_NULL);
6898
6899 void WriteNumber(uint32_t n);
6900 void WriteNumber(uint64_t n);
6901 void WriteBool(bool b);
6902 void WriteNull();
6903
6904 private:
6905 static const char* const INDENT;
6906
6907 enum COLLECTION_TYPE
6908 {
6909 COLLECTION_TYPE_OBJECT,
6910 COLLECTION_TYPE_ARRAY,
6911 };
6912 struct StackItem
6913 {
6914 COLLECTION_TYPE type;
6915 uint32_t valueCount;
6916 bool singleLineMode;
6917 };
6918
6919 VmaStringBuilder& m_SB;
6920 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6921 bool m_InsideString;
6922
6923 void BeginValue(bool isString);
6924 void WriteIndent(bool oneLess = false);
6925 };
6926
6927 const char* const VmaJsonWriter::INDENT = " ";
6928
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)6929 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6930 m_SB(sb),
6931 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6932 m_InsideString(false)
6933 {
6934 }
6935
~VmaJsonWriter()6936 VmaJsonWriter::~VmaJsonWriter()
6937 {
6938 VMA_ASSERT(!m_InsideString);
6939 VMA_ASSERT(m_Stack.empty());
6940 }
6941
BeginObject(bool singleLine)6942 void VmaJsonWriter::BeginObject(bool singleLine)
6943 {
6944 VMA_ASSERT(!m_InsideString);
6945
6946 BeginValue(false);
6947 m_SB.Add('{');
6948
6949 StackItem item;
6950 item.type = COLLECTION_TYPE_OBJECT;
6951 item.valueCount = 0;
6952 item.singleLineMode = singleLine;
6953 m_Stack.push_back(item);
6954 }
6955
EndObject()6956 void VmaJsonWriter::EndObject()
6957 {
6958 VMA_ASSERT(!m_InsideString);
6959
6960 WriteIndent(true);
6961 m_SB.Add('}');
6962
6963 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6964 m_Stack.pop_back();
6965 }
6966
BeginArray(bool singleLine)6967 void VmaJsonWriter::BeginArray(bool singleLine)
6968 {
6969 VMA_ASSERT(!m_InsideString);
6970
6971 BeginValue(false);
6972 m_SB.Add('[');
6973
6974 StackItem item;
6975 item.type = COLLECTION_TYPE_ARRAY;
6976 item.valueCount = 0;
6977 item.singleLineMode = singleLine;
6978 m_Stack.push_back(item);
6979 }
6980
EndArray()6981 void VmaJsonWriter::EndArray()
6982 {
6983 VMA_ASSERT(!m_InsideString);
6984
6985 WriteIndent(true);
6986 m_SB.Add(']');
6987
6988 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6989 m_Stack.pop_back();
6990 }
6991
WriteString(const char * pStr)6992 void VmaJsonWriter::WriteString(const char* pStr)
6993 {
6994 BeginString(pStr);
6995 EndString();
6996 }
6997
BeginString(const char * pStr)6998 void VmaJsonWriter::BeginString(const char* pStr)
6999 {
7000 VMA_ASSERT(!m_InsideString);
7001
7002 BeginValue(true);
7003 m_SB.Add('"');
7004 m_InsideString = true;
7005 if(pStr != VMA_NULL && pStr[0] != '\0')
7006 {
7007 ContinueString(pStr);
7008 }
7009 }
7010
ContinueString(const char * pStr)7011 void VmaJsonWriter::ContinueString(const char* pStr)
7012 {
7013 VMA_ASSERT(m_InsideString);
7014
7015 const size_t strLen = strlen(pStr);
7016 for(size_t i = 0; i < strLen; ++i)
7017 {
7018 char ch = pStr[i];
7019 if(ch == '\\')
7020 {
7021 m_SB.Add("\\\\");
7022 }
7023 else if(ch == '"')
7024 {
7025 m_SB.Add("\\\"");
7026 }
7027 else if(ch >= 32)
7028 {
7029 m_SB.Add(ch);
7030 }
7031 else switch(ch)
7032 {
7033 case '\b':
7034 m_SB.Add("\\b");
7035 break;
7036 case '\f':
7037 m_SB.Add("\\f");
7038 break;
7039 case '\n':
7040 m_SB.Add("\\n");
7041 break;
7042 case '\r':
7043 m_SB.Add("\\r");
7044 break;
7045 case '\t':
7046 m_SB.Add("\\t");
7047 break;
7048 default:
7049 VMA_ASSERT(0 && "Character not currently supported.");
7050 break;
7051 }
7052 }
7053 }
7054
ContinueString(uint32_t n)7055 void VmaJsonWriter::ContinueString(uint32_t n)
7056 {
7057 VMA_ASSERT(m_InsideString);
7058 m_SB.AddNumber(n);
7059 }
7060
ContinueString(uint64_t n)7061 void VmaJsonWriter::ContinueString(uint64_t n)
7062 {
7063 VMA_ASSERT(m_InsideString);
7064 m_SB.AddNumber(n);
7065 }
7066
ContinueString_Pointer(const void * ptr)7067 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7068 {
7069 VMA_ASSERT(m_InsideString);
7070 m_SB.AddPointer(ptr);
7071 }
7072
EndString(const char * pStr)7073 void VmaJsonWriter::EndString(const char* pStr)
7074 {
7075 VMA_ASSERT(m_InsideString);
7076 if(pStr != VMA_NULL && pStr[0] != '\0')
7077 {
7078 ContinueString(pStr);
7079 }
7080 m_SB.Add('"');
7081 m_InsideString = false;
7082 }
7083
WriteNumber(uint32_t n)7084 void VmaJsonWriter::WriteNumber(uint32_t n)
7085 {
7086 VMA_ASSERT(!m_InsideString);
7087 BeginValue(false);
7088 m_SB.AddNumber(n);
7089 }
7090
WriteNumber(uint64_t n)7091 void VmaJsonWriter::WriteNumber(uint64_t n)
7092 {
7093 VMA_ASSERT(!m_InsideString);
7094 BeginValue(false);
7095 m_SB.AddNumber(n);
7096 }
7097
WriteBool(bool b)7098 void VmaJsonWriter::WriteBool(bool b)
7099 {
7100 VMA_ASSERT(!m_InsideString);
7101 BeginValue(false);
7102 m_SB.Add(b ? "true" : "false");
7103 }
7104
WriteNull()7105 void VmaJsonWriter::WriteNull()
7106 {
7107 VMA_ASSERT(!m_InsideString);
7108 BeginValue(false);
7109 m_SB.Add("null");
7110 }
7111
BeginValue(bool isString)7112 void VmaJsonWriter::BeginValue(bool isString)
7113 {
7114 if(!m_Stack.empty())
7115 {
7116 StackItem& currItem = m_Stack.back();
7117 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7118 currItem.valueCount % 2 == 0)
7119 {
7120 VMA_ASSERT(isString);
7121 }
7122
7123 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7124 currItem.valueCount % 2 != 0)
7125 {
7126 m_SB.Add(": ");
7127 }
7128 else if(currItem.valueCount > 0)
7129 {
7130 m_SB.Add(", ");
7131 WriteIndent();
7132 }
7133 else
7134 {
7135 WriteIndent();
7136 }
7137 ++currItem.valueCount;
7138 }
7139 }
7140
WriteIndent(bool oneLess)7141 void VmaJsonWriter::WriteIndent(bool oneLess)
7142 {
7143 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7144 {
7145 m_SB.AddNewLine();
7146
7147 size_t count = m_Stack.size();
7148 if(count > 0 && oneLess)
7149 {
7150 --count;
7151 }
7152 for(size_t i = 0; i < count; ++i)
7153 {
7154 m_SB.Add(INDENT);
7155 }
7156 }
7157 }
7158
7159 #endif // #if VMA_STATS_STRING_ENABLED
7160
7161 ////////////////////////////////////////////////////////////////////////////////
7162
SetUserData(VmaAllocator hAllocator,void * pUserData)7163 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7164 {
7165 if(IsUserDataString())
7166 {
7167 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7168
7169 FreeUserDataString(hAllocator);
7170
7171 if(pUserData != VMA_NULL)
7172 {
7173 const char* const newStrSrc = (char*)pUserData;
7174 const size_t newStrLen = strlen(newStrSrc);
7175 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7176 memcpy(newStrDst, newStrSrc, newStrLen + 1);
7177 m_pUserData = newStrDst;
7178 }
7179 }
7180 else
7181 {
7182 m_pUserData = pUserData;
7183 }
7184 }
7185
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)7186 void VmaAllocation_T::ChangeBlockAllocation(
7187 VmaAllocator hAllocator,
7188 VmaDeviceMemoryBlock* block,
7189 VkDeviceSize offset)
7190 {
7191 VMA_ASSERT(block != VMA_NULL);
7192 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7193
7194 // Move mapping reference counter from old block to new block.
7195 if(block != m_BlockAllocation.m_Block)
7196 {
7197 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7198 if(IsPersistentMap())
7199 ++mapRefCount;
7200 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7201 block->Map(hAllocator, mapRefCount, VMA_NULL);
7202 }
7203
7204 m_BlockAllocation.m_Block = block;
7205 m_BlockAllocation.m_Offset = offset;
7206 }
7207
ChangeSize(VkDeviceSize newSize)7208 void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7209 {
7210 VMA_ASSERT(newSize > 0);
7211 m_Size = newSize;
7212 }
7213
ChangeOffset(VkDeviceSize newOffset)7214 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7215 {
7216 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7217 m_BlockAllocation.m_Offset = newOffset;
7218 }
7219
GetOffset()7220 VkDeviceSize VmaAllocation_T::GetOffset() const
7221 {
7222 switch(m_Type)
7223 {
7224 case ALLOCATION_TYPE_BLOCK:
7225 return m_BlockAllocation.m_Offset;
7226 case ALLOCATION_TYPE_DEDICATED:
7227 return 0;
7228 default:
7229 VMA_ASSERT(0);
7230 return 0;
7231 }
7232 }
7233
GetMemory()7234 VkDeviceMemory VmaAllocation_T::GetMemory() const
7235 {
7236 switch(m_Type)
7237 {
7238 case ALLOCATION_TYPE_BLOCK:
7239 return m_BlockAllocation.m_Block->GetDeviceMemory();
7240 case ALLOCATION_TYPE_DEDICATED:
7241 return m_DedicatedAllocation.m_hMemory;
7242 default:
7243 VMA_ASSERT(0);
7244 return VK_NULL_HANDLE;
7245 }
7246 }
7247
GetMemoryTypeIndex()7248 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7249 {
7250 switch(m_Type)
7251 {
7252 case ALLOCATION_TYPE_BLOCK:
7253 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7254 case ALLOCATION_TYPE_DEDICATED:
7255 return m_DedicatedAllocation.m_MemoryTypeIndex;
7256 default:
7257 VMA_ASSERT(0);
7258 return UINT32_MAX;
7259 }
7260 }
7261
GetMappedData()7262 void* VmaAllocation_T::GetMappedData() const
7263 {
7264 switch(m_Type)
7265 {
7266 case ALLOCATION_TYPE_BLOCK:
7267 if(m_MapCount != 0)
7268 {
7269 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7270 VMA_ASSERT(pBlockData != VMA_NULL);
7271 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7272 }
7273 else
7274 {
7275 return VMA_NULL;
7276 }
7277 break;
7278 case ALLOCATION_TYPE_DEDICATED:
7279 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7280 return m_DedicatedAllocation.m_pMappedData;
7281 default:
7282 VMA_ASSERT(0);
7283 return VMA_NULL;
7284 }
7285 }
7286
CanBecomeLost()7287 bool VmaAllocation_T::CanBecomeLost() const
7288 {
7289 switch(m_Type)
7290 {
7291 case ALLOCATION_TYPE_BLOCK:
7292 return m_BlockAllocation.m_CanBecomeLost;
7293 case ALLOCATION_TYPE_DEDICATED:
7294 return false;
7295 default:
7296 VMA_ASSERT(0);
7297 return false;
7298 }
7299 }
7300
GetPool()7301 VmaPool VmaAllocation_T::GetPool() const
7302 {
7303 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7304 return m_BlockAllocation.m_hPool;
7305 }
7306
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)7307 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7308 {
7309 VMA_ASSERT(CanBecomeLost());
7310
7311 /*
7312 Warning: This is a carefully designed algorithm.
7313 Do not modify unless you really know what you're doing :)
7314 */
7315 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7316 for(;;)
7317 {
7318 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7319 {
7320 VMA_ASSERT(0);
7321 return false;
7322 }
7323 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7324 {
7325 return false;
7326 }
7327 else // Last use time earlier than current time.
7328 {
7329 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7330 {
7331 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7332 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7333 return true;
7334 }
7335 }
7336 }
7337 }
7338
7339 #if VMA_STATS_STRING_ENABLED
7340
7341 // Correspond to values of enum VmaSuballocationType.
7342 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7343 "FREE",
7344 "UNKNOWN",
7345 "BUFFER",
7346 "IMAGE_UNKNOWN",
7347 "IMAGE_LINEAR",
7348 "IMAGE_OPTIMAL",
7349 };
7350
PrintParameters(class VmaJsonWriter & json)7351 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7352 {
7353 json.WriteString("Type");
7354 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7355
7356 json.WriteString("Size");
7357 json.WriteNumber(m_Size);
7358
7359 if(m_pUserData != VMA_NULL)
7360 {
7361 json.WriteString("UserData");
7362 if(IsUserDataString())
7363 {
7364 json.WriteString((const char*)m_pUserData);
7365 }
7366 else
7367 {
7368 json.BeginString();
7369 json.ContinueString_Pointer(m_pUserData);
7370 json.EndString();
7371 }
7372 }
7373
7374 json.WriteString("CreationFrameIndex");
7375 json.WriteNumber(m_CreationFrameIndex);
7376
7377 json.WriteString("LastUseFrameIndex");
7378 json.WriteNumber(GetLastUseFrameIndex());
7379
7380 if(m_BufferImageUsage != 0)
7381 {
7382 json.WriteString("Usage");
7383 json.WriteNumber(m_BufferImageUsage);
7384 }
7385 }
7386
7387 #endif
7388
FreeUserDataString(VmaAllocator hAllocator)7389 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7390 {
7391 VMA_ASSERT(IsUserDataString());
7392 if(m_pUserData != VMA_NULL)
7393 {
7394 char* const oldStr = (char*)m_pUserData;
7395 const size_t oldStrLen = strlen(oldStr);
7396 vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7397 m_pUserData = VMA_NULL;
7398 }
7399 }
7400
BlockAllocMap()7401 void VmaAllocation_T::BlockAllocMap()
7402 {
7403 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7404
7405 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7406 {
7407 ++m_MapCount;
7408 }
7409 else
7410 {
7411 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7412 }
7413 }
7414
BlockAllocUnmap()7415 void VmaAllocation_T::BlockAllocUnmap()
7416 {
7417 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7418
7419 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7420 {
7421 --m_MapCount;
7422 }
7423 else
7424 {
7425 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7426 }
7427 }
7428
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)7429 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7430 {
7431 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7432
7433 if(m_MapCount != 0)
7434 {
7435 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7436 {
7437 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7438 *ppData = m_DedicatedAllocation.m_pMappedData;
7439 ++m_MapCount;
7440 return VK_SUCCESS;
7441 }
7442 else
7443 {
7444 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7445 return VK_ERROR_MEMORY_MAP_FAILED;
7446 }
7447 }
7448 else
7449 {
7450 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7451 hAllocator->m_hDevice,
7452 m_DedicatedAllocation.m_hMemory,
7453 0, // offset
7454 VK_WHOLE_SIZE,
7455 0, // flags
7456 ppData);
7457 if(result == VK_SUCCESS)
7458 {
7459 m_DedicatedAllocation.m_pMappedData = *ppData;
7460 m_MapCount = 1;
7461 }
7462 return result;
7463 }
7464 }
7465
DedicatedAllocUnmap(VmaAllocator hAllocator)7466 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7467 {
7468 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7469
7470 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7471 {
7472 --m_MapCount;
7473 if(m_MapCount == 0)
7474 {
7475 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7476 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7477 hAllocator->m_hDevice,
7478 m_DedicatedAllocation.m_hMemory);
7479 }
7480 }
7481 else
7482 {
7483 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7484 }
7485 }
7486
7487 #if VMA_STATS_STRING_ENABLED
7488
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)7489 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7490 {
7491 json.BeginObject();
7492
7493 json.WriteString("Blocks");
7494 json.WriteNumber(stat.blockCount);
7495
7496 json.WriteString("Allocations");
7497 json.WriteNumber(stat.allocationCount);
7498
7499 json.WriteString("UnusedRanges");
7500 json.WriteNumber(stat.unusedRangeCount);
7501
7502 json.WriteString("UsedBytes");
7503 json.WriteNumber(stat.usedBytes);
7504
7505 json.WriteString("UnusedBytes");
7506 json.WriteNumber(stat.unusedBytes);
7507
7508 if(stat.allocationCount > 1)
7509 {
7510 json.WriteString("AllocationSize");
7511 json.BeginObject(true);
7512 json.WriteString("Min");
7513 json.WriteNumber(stat.allocationSizeMin);
7514 json.WriteString("Avg");
7515 json.WriteNumber(stat.allocationSizeAvg);
7516 json.WriteString("Max");
7517 json.WriteNumber(stat.allocationSizeMax);
7518 json.EndObject();
7519 }
7520
7521 if(stat.unusedRangeCount > 1)
7522 {
7523 json.WriteString("UnusedRangeSize");
7524 json.BeginObject(true);
7525 json.WriteString("Min");
7526 json.WriteNumber(stat.unusedRangeSizeMin);
7527 json.WriteString("Avg");
7528 json.WriteNumber(stat.unusedRangeSizeAvg);
7529 json.WriteString("Max");
7530 json.WriteNumber(stat.unusedRangeSizeMax);
7531 json.EndObject();
7532 }
7533
7534 json.EndObject();
7535 }
7536
7537 #endif // #if VMA_STATS_STRING_ENABLED
7538
7539 struct VmaSuballocationItemSizeLess
7540 {
operatorVmaSuballocationItemSizeLess7541 bool operator()(
7542 const VmaSuballocationList::iterator lhs,
7543 const VmaSuballocationList::iterator rhs) const
7544 {
7545 return lhs->size < rhs->size;
7546 }
operatorVmaSuballocationItemSizeLess7547 bool operator()(
7548 const VmaSuballocationList::iterator lhs,
7549 VkDeviceSize rhsSize) const
7550 {
7551 return lhs->size < rhsSize;
7552 }
7553 };
7554
7555
7556 ////////////////////////////////////////////////////////////////////////////////
7557 // class VmaBlockMetadata
7558
VmaBlockMetadata(VmaAllocator hAllocator)7559 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7560 m_Size(0),
7561 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7562 {
7563 }
7564
7565 #if VMA_STATS_STRING_ENABLED
7566
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)7567 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7568 VkDeviceSize unusedBytes,
7569 size_t allocationCount,
7570 size_t unusedRangeCount) const
7571 {
7572 json.BeginObject();
7573
7574 json.WriteString("TotalBytes");
7575 json.WriteNumber(GetSize());
7576
7577 json.WriteString("UnusedBytes");
7578 json.WriteNumber(unusedBytes);
7579
7580 json.WriteString("Allocations");
7581 json.WriteNumber((uint64_t)allocationCount);
7582
7583 json.WriteString("UnusedRanges");
7584 json.WriteNumber((uint64_t)unusedRangeCount);
7585
7586 json.WriteString("Suballocations");
7587 json.BeginArray();
7588 }
7589
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)7590 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7591 VkDeviceSize offset,
7592 VmaAllocation hAllocation) const
7593 {
7594 json.BeginObject(true);
7595
7596 json.WriteString("Offset");
7597 json.WriteNumber(offset);
7598
7599 hAllocation->PrintParameters(json);
7600
7601 json.EndObject();
7602 }
7603
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)7604 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7605 VkDeviceSize offset,
7606 VkDeviceSize size) const
7607 {
7608 json.BeginObject(true);
7609
7610 json.WriteString("Offset");
7611 json.WriteNumber(offset);
7612
7613 json.WriteString("Type");
7614 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7615
7616 json.WriteString("Size");
7617 json.WriteNumber(size);
7618
7619 json.EndObject();
7620 }
7621
PrintDetailedMap_End(class VmaJsonWriter & json)7622 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7623 {
7624 json.EndArray();
7625 json.EndObject();
7626 }
7627
7628 #endif // #if VMA_STATS_STRING_ENABLED
7629
7630 ////////////////////////////////////////////////////////////////////////////////
7631 // class VmaBlockMetadata_Generic
7632
VmaBlockMetadata_Generic(VmaAllocator hAllocator)7633 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7634 VmaBlockMetadata(hAllocator),
7635 m_FreeCount(0),
7636 m_SumFreeSize(0),
7637 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7638 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7639 {
7640 }
7641
~VmaBlockMetadata_Generic()7642 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7643 {
7644 }
7645
Init(VkDeviceSize size)7646 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7647 {
7648 VmaBlockMetadata::Init(size);
7649
7650 m_FreeCount = 1;
7651 m_SumFreeSize = size;
7652
7653 VmaSuballocation suballoc = {};
7654 suballoc.offset = 0;
7655 suballoc.size = size;
7656 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7657 suballoc.hAllocation = VK_NULL_HANDLE;
7658
7659 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7660 m_Suballocations.push_back(suballoc);
7661 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7662 --suballocItem;
7663 m_FreeSuballocationsBySize.push_back(suballocItem);
7664 }
7665
Validate()7666 bool VmaBlockMetadata_Generic::Validate() const
7667 {
7668 VMA_VALIDATE(!m_Suballocations.empty());
7669
7670 // Expected offset of new suballocation as calculated from previous ones.
7671 VkDeviceSize calculatedOffset = 0;
7672 // Expected number of free suballocations as calculated from traversing their list.
7673 uint32_t calculatedFreeCount = 0;
7674 // Expected sum size of free suballocations as calculated from traversing their list.
7675 VkDeviceSize calculatedSumFreeSize = 0;
7676 // Expected number of free suballocations that should be registered in
7677 // m_FreeSuballocationsBySize calculated from traversing their list.
7678 size_t freeSuballocationsToRegister = 0;
7679 // True if previous visited suballocation was free.
7680 bool prevFree = false;
7681
7682 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7683 suballocItem != m_Suballocations.cend();
7684 ++suballocItem)
7685 {
7686 const VmaSuballocation& subAlloc = *suballocItem;
7687
7688 // Actual offset of this suballocation doesn't match expected one.
7689 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7690
7691 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7692 // Two adjacent free suballocations are invalid. They should be merged.
7693 VMA_VALIDATE(!prevFree || !currFree);
7694
7695 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7696
7697 if(currFree)
7698 {
7699 calculatedSumFreeSize += subAlloc.size;
7700 ++calculatedFreeCount;
7701 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7702 {
7703 ++freeSuballocationsToRegister;
7704 }
7705
7706 // Margin required between allocations - every free space must be at least that large.
7707 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7708 }
7709 else
7710 {
7711 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7712 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7713
7714 // Margin required between allocations - previous allocation must be free.
7715 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7716 }
7717
7718 calculatedOffset += subAlloc.size;
7719 prevFree = currFree;
7720 }
7721
7722 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7723 // match expected one.
7724 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7725
7726 VkDeviceSize lastSize = 0;
7727 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7728 {
7729 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7730
7731 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7732 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7733 // They must be sorted by size ascending.
7734 VMA_VALIDATE(suballocItem->size >= lastSize);
7735
7736 lastSize = suballocItem->size;
7737 }
7738
7739 // Check if totals match calculacted values.
7740 VMA_VALIDATE(ValidateFreeSuballocationList());
7741 VMA_VALIDATE(calculatedOffset == GetSize());
7742 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7743 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7744
7745 return true;
7746 }
7747
GetUnusedRangeSizeMax()7748 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7749 {
7750 if(!m_FreeSuballocationsBySize.empty())
7751 {
7752 return m_FreeSuballocationsBySize.back()->size;
7753 }
7754 else
7755 {
7756 return 0;
7757 }
7758 }
7759
IsEmpty()7760 bool VmaBlockMetadata_Generic::IsEmpty() const
7761 {
7762 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7763 }
7764
CalcAllocationStatInfo(VmaStatInfo & outInfo)7765 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7766 {
7767 outInfo.blockCount = 1;
7768
7769 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7770 outInfo.allocationCount = rangeCount - m_FreeCount;
7771 outInfo.unusedRangeCount = m_FreeCount;
7772
7773 outInfo.unusedBytes = m_SumFreeSize;
7774 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7775
7776 outInfo.allocationSizeMin = UINT64_MAX;
7777 outInfo.allocationSizeMax = 0;
7778 outInfo.unusedRangeSizeMin = UINT64_MAX;
7779 outInfo.unusedRangeSizeMax = 0;
7780
7781 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7782 suballocItem != m_Suballocations.cend();
7783 ++suballocItem)
7784 {
7785 const VmaSuballocation& suballoc = *suballocItem;
7786 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7787 {
7788 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7789 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7790 }
7791 else
7792 {
7793 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7794 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7795 }
7796 }
7797 }
7798
AddPoolStats(VmaPoolStats & inoutStats)7799 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7800 {
7801 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7802
7803 inoutStats.size += GetSize();
7804 inoutStats.unusedSize += m_SumFreeSize;
7805 inoutStats.allocationCount += rangeCount - m_FreeCount;
7806 inoutStats.unusedRangeCount += m_FreeCount;
7807 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7808 }
7809
7810 #if VMA_STATS_STRING_ENABLED
7811
PrintDetailedMap(class VmaJsonWriter & json)7812 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7813 {
7814 PrintDetailedMap_Begin(json,
7815 m_SumFreeSize, // unusedBytes
7816 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7817 m_FreeCount); // unusedRangeCount
7818
7819 size_t i = 0;
7820 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7821 suballocItem != m_Suballocations.cend();
7822 ++suballocItem, ++i)
7823 {
7824 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7825 {
7826 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7827 }
7828 else
7829 {
7830 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7831 }
7832 }
7833
7834 PrintDetailedMap_End(json);
7835 }
7836
7837 #endif // #if VMA_STATS_STRING_ENABLED
7838
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)7839 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7840 uint32_t currentFrameIndex,
7841 uint32_t frameInUseCount,
7842 VkDeviceSize bufferImageGranularity,
7843 VkDeviceSize allocSize,
7844 VkDeviceSize allocAlignment,
7845 bool upperAddress,
7846 VmaSuballocationType allocType,
7847 bool canMakeOtherLost,
7848 uint32_t strategy,
7849 VmaAllocationRequest* pAllocationRequest)
7850 {
7851 VMA_ASSERT(allocSize > 0);
7852 VMA_ASSERT(!upperAddress);
7853 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7854 VMA_ASSERT(pAllocationRequest != VMA_NULL);
7855 VMA_HEAVY_ASSERT(Validate());
7856
7857 // There is not enough total free space in this block to fullfill the request: Early return.
7858 if(canMakeOtherLost == false &&
7859 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7860 {
7861 return false;
7862 }
7863
7864 // New algorithm, efficiently searching freeSuballocationsBySize.
7865 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7866 if(freeSuballocCount > 0)
7867 {
7868 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7869 {
7870 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7871 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7872 m_FreeSuballocationsBySize.data(),
7873 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7874 allocSize + 2 * VMA_DEBUG_MARGIN,
7875 VmaSuballocationItemSizeLess());
7876 size_t index = it - m_FreeSuballocationsBySize.data();
7877 for(; index < freeSuballocCount; ++index)
7878 {
7879 if(CheckAllocation(
7880 currentFrameIndex,
7881 frameInUseCount,
7882 bufferImageGranularity,
7883 allocSize,
7884 allocAlignment,
7885 allocType,
7886 m_FreeSuballocationsBySize[index],
7887 false, // canMakeOtherLost
7888 &pAllocationRequest->offset,
7889 &pAllocationRequest->itemsToMakeLostCount,
7890 &pAllocationRequest->sumFreeSize,
7891 &pAllocationRequest->sumItemSize))
7892 {
7893 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7894 return true;
7895 }
7896 }
7897 }
7898 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7899 {
7900 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7901 it != m_Suballocations.end();
7902 ++it)
7903 {
7904 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7905 currentFrameIndex,
7906 frameInUseCount,
7907 bufferImageGranularity,
7908 allocSize,
7909 allocAlignment,
7910 allocType,
7911 it,
7912 false, // canMakeOtherLost
7913 &pAllocationRequest->offset,
7914 &pAllocationRequest->itemsToMakeLostCount,
7915 &pAllocationRequest->sumFreeSize,
7916 &pAllocationRequest->sumItemSize))
7917 {
7918 pAllocationRequest->item = it;
7919 return true;
7920 }
7921 }
7922 }
7923 else // WORST_FIT, FIRST_FIT
7924 {
7925 // Search staring from biggest suballocations.
7926 for(size_t index = freeSuballocCount; index--; )
7927 {
7928 if(CheckAllocation(
7929 currentFrameIndex,
7930 frameInUseCount,
7931 bufferImageGranularity,
7932 allocSize,
7933 allocAlignment,
7934 allocType,
7935 m_FreeSuballocationsBySize[index],
7936 false, // canMakeOtherLost
7937 &pAllocationRequest->offset,
7938 &pAllocationRequest->itemsToMakeLostCount,
7939 &pAllocationRequest->sumFreeSize,
7940 &pAllocationRequest->sumItemSize))
7941 {
7942 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7943 return true;
7944 }
7945 }
7946 }
7947 }
7948
7949 if(canMakeOtherLost)
7950 {
7951 // Brute-force algorithm. TODO: Come up with something better.
7952
7953 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7954 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7955
7956 VmaAllocationRequest tmpAllocRequest = {};
7957 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7958 suballocIt != m_Suballocations.end();
7959 ++suballocIt)
7960 {
7961 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7962 suballocIt->hAllocation->CanBecomeLost())
7963 {
7964 if(CheckAllocation(
7965 currentFrameIndex,
7966 frameInUseCount,
7967 bufferImageGranularity,
7968 allocSize,
7969 allocAlignment,
7970 allocType,
7971 suballocIt,
7972 canMakeOtherLost,
7973 &tmpAllocRequest.offset,
7974 &tmpAllocRequest.itemsToMakeLostCount,
7975 &tmpAllocRequest.sumFreeSize,
7976 &tmpAllocRequest.sumItemSize))
7977 {
7978 tmpAllocRequest.item = suballocIt;
7979
7980 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7981 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7982 {
7983 *pAllocationRequest = tmpAllocRequest;
7984 }
7985 }
7986 }
7987 }
7988
7989 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7990 {
7991 return true;
7992 }
7993 }
7994
7995 return false;
7996 }
7997
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)7998 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7999 uint32_t currentFrameIndex,
8000 uint32_t frameInUseCount,
8001 VmaAllocationRequest* pAllocationRequest)
8002 {
8003 while(pAllocationRequest->itemsToMakeLostCount > 0)
8004 {
8005 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
8006 {
8007 ++pAllocationRequest->item;
8008 }
8009 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8010 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
8011 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
8012 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8013 {
8014 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
8015 --pAllocationRequest->itemsToMakeLostCount;
8016 }
8017 else
8018 {
8019 return false;
8020 }
8021 }
8022
8023 VMA_HEAVY_ASSERT(Validate());
8024 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8025 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8026
8027 return true;
8028 }
8029
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8030 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8031 {
8032 uint32_t lostAllocationCount = 0;
8033 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8034 it != m_Suballocations.end();
8035 ++it)
8036 {
8037 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8038 it->hAllocation->CanBecomeLost() &&
8039 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8040 {
8041 it = FreeSuballocation(it);
8042 ++lostAllocationCount;
8043 }
8044 }
8045 return lostAllocationCount;
8046 }
8047
CheckCorruption(const void * pBlockData)8048 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8049 {
8050 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8051 it != m_Suballocations.end();
8052 ++it)
8053 {
8054 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8055 {
8056 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8057 {
8058 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8059 return VK_ERROR_VALIDATION_FAILED_EXT;
8060 }
8061 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8062 {
8063 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8064 return VK_ERROR_VALIDATION_FAILED_EXT;
8065 }
8066 }
8067 }
8068
8069 return VK_SUCCESS;
8070 }
8071
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)8072 void VmaBlockMetadata_Generic::Alloc(
8073 const VmaAllocationRequest& request,
8074 VmaSuballocationType type,
8075 VkDeviceSize allocSize,
8076 bool upperAddress,
8077 VmaAllocation hAllocation)
8078 {
8079 VMA_ASSERT(!upperAddress);
8080 VMA_ASSERT(request.item != m_Suballocations.end());
8081 VmaSuballocation& suballoc = *request.item;
8082 // Given suballocation is a free block.
8083 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8084 // Given offset is inside this suballocation.
8085 VMA_ASSERT(request.offset >= suballoc.offset);
8086 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8087 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8088 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8089
8090 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8091 // it to become used.
8092 UnregisterFreeSuballocation(request.item);
8093
8094 suballoc.offset = request.offset;
8095 suballoc.size = allocSize;
8096 suballoc.type = type;
8097 suballoc.hAllocation = hAllocation;
8098
8099 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8100 if(paddingEnd)
8101 {
8102 VmaSuballocation paddingSuballoc = {};
8103 paddingSuballoc.offset = request.offset + allocSize;
8104 paddingSuballoc.size = paddingEnd;
8105 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8106 VmaSuballocationList::iterator next = request.item;
8107 ++next;
8108 const VmaSuballocationList::iterator paddingEndItem =
8109 m_Suballocations.insert(next, paddingSuballoc);
8110 RegisterFreeSuballocation(paddingEndItem);
8111 }
8112
8113 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8114 if(paddingBegin)
8115 {
8116 VmaSuballocation paddingSuballoc = {};
8117 paddingSuballoc.offset = request.offset - paddingBegin;
8118 paddingSuballoc.size = paddingBegin;
8119 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8120 const VmaSuballocationList::iterator paddingBeginItem =
8121 m_Suballocations.insert(request.item, paddingSuballoc);
8122 RegisterFreeSuballocation(paddingBeginItem);
8123 }
8124
8125 // Update totals.
8126 m_FreeCount = m_FreeCount - 1;
8127 if(paddingBegin > 0)
8128 {
8129 ++m_FreeCount;
8130 }
8131 if(paddingEnd > 0)
8132 {
8133 ++m_FreeCount;
8134 }
8135 m_SumFreeSize -= allocSize;
8136 }
8137
Free(const VmaAllocation allocation)8138 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8139 {
8140 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8141 suballocItem != m_Suballocations.end();
8142 ++suballocItem)
8143 {
8144 VmaSuballocation& suballoc = *suballocItem;
8145 if(suballoc.hAllocation == allocation)
8146 {
8147 FreeSuballocation(suballocItem);
8148 VMA_HEAVY_ASSERT(Validate());
8149 return;
8150 }
8151 }
8152 VMA_ASSERT(0 && "Not found!");
8153 }
8154
FreeAtOffset(VkDeviceSize offset)8155 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8156 {
8157 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8158 suballocItem != m_Suballocations.end();
8159 ++suballocItem)
8160 {
8161 VmaSuballocation& suballoc = *suballocItem;
8162 if(suballoc.offset == offset)
8163 {
8164 FreeSuballocation(suballocItem);
8165 return;
8166 }
8167 }
8168 VMA_ASSERT(0 && "Not found!");
8169 }
8170
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)8171 bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8172 {
8173 typedef VmaSuballocationList::iterator iter_type;
8174 for(iter_type suballocItem = m_Suballocations.begin();
8175 suballocItem != m_Suballocations.end();
8176 ++suballocItem)
8177 {
8178 VmaSuballocation& suballoc = *suballocItem;
8179 if(suballoc.hAllocation == alloc)
8180 {
8181 iter_type nextItem = suballocItem;
8182 ++nextItem;
8183
8184 // Should have been ensured on higher level.
8185 VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8186
8187 // Shrinking.
8188 if(newSize < alloc->GetSize())
8189 {
8190 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8191
8192 // There is next item.
8193 if(nextItem != m_Suballocations.end())
8194 {
8195 // Next item is free.
8196 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8197 {
8198 // Grow this next item backward.
8199 UnregisterFreeSuballocation(nextItem);
8200 nextItem->offset -= sizeDiff;
8201 nextItem->size += sizeDiff;
8202 RegisterFreeSuballocation(nextItem);
8203 }
8204 // Next item is not free.
8205 else
8206 {
8207 // Create free item after current one.
8208 VmaSuballocation newFreeSuballoc;
8209 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8210 newFreeSuballoc.offset = suballoc.offset + newSize;
8211 newFreeSuballoc.size = sizeDiff;
8212 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8213 iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8214 RegisterFreeSuballocation(newFreeSuballocIt);
8215
8216 ++m_FreeCount;
8217 }
8218 }
8219 // This is the last item.
8220 else
8221 {
8222 // Create free item at the end.
8223 VmaSuballocation newFreeSuballoc;
8224 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8225 newFreeSuballoc.offset = suballoc.offset + newSize;
8226 newFreeSuballoc.size = sizeDiff;
8227 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8228 m_Suballocations.push_back(newFreeSuballoc);
8229
8230 iter_type newFreeSuballocIt = m_Suballocations.end();
8231 RegisterFreeSuballocation(--newFreeSuballocIt);
8232
8233 ++m_FreeCount;
8234 }
8235
8236 suballoc.size = newSize;
8237 m_SumFreeSize += sizeDiff;
8238 }
8239 // Growing.
8240 else
8241 {
8242 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8243
8244 // There is next item.
8245 if(nextItem != m_Suballocations.end())
8246 {
8247 // Next item is free.
8248 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8249 {
8250 // There is not enough free space, including margin.
8251 if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8252 {
8253 return false;
8254 }
8255
8256 // There is more free space than required.
8257 if(nextItem->size > sizeDiff)
8258 {
8259 // Move and shrink this next item.
8260 UnregisterFreeSuballocation(nextItem);
8261 nextItem->offset += sizeDiff;
8262 nextItem->size -= sizeDiff;
8263 RegisterFreeSuballocation(nextItem);
8264 }
8265 // There is exactly the amount of free space required.
8266 else
8267 {
8268 // Remove this next free item.
8269 UnregisterFreeSuballocation(nextItem);
8270 m_Suballocations.erase(nextItem);
8271 --m_FreeCount;
8272 }
8273 }
8274 // Next item is not free - there is no space to grow.
8275 else
8276 {
8277 return false;
8278 }
8279 }
8280 // This is the last item - there is no space to grow.
8281 else
8282 {
8283 return false;
8284 }
8285
8286 suballoc.size = newSize;
8287 m_SumFreeSize -= sizeDiff;
8288 }
8289
8290 // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8291 return true;
8292 }
8293 }
8294 VMA_ASSERT(0 && "Not found!");
8295 return false;
8296 }
8297
ValidateFreeSuballocationList()8298 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8299 {
8300 VkDeviceSize lastSize = 0;
8301 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8302 {
8303 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8304
8305 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8306 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8307 VMA_VALIDATE(it->size >= lastSize);
8308 lastSize = it->size;
8309 }
8310 return true;
8311 }
8312
CheckAllocation(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaSuballocationList::const_iterator suballocItem,bool canMakeOtherLost,VkDeviceSize * pOffset,size_t * itemsToMakeLostCount,VkDeviceSize * pSumFreeSize,VkDeviceSize * pSumItemSize)8313 bool VmaBlockMetadata_Generic::CheckAllocation(
8314 uint32_t currentFrameIndex,
8315 uint32_t frameInUseCount,
8316 VkDeviceSize bufferImageGranularity,
8317 VkDeviceSize allocSize,
8318 VkDeviceSize allocAlignment,
8319 VmaSuballocationType allocType,
8320 VmaSuballocationList::const_iterator suballocItem,
8321 bool canMakeOtherLost,
8322 VkDeviceSize* pOffset,
8323 size_t* itemsToMakeLostCount,
8324 VkDeviceSize* pSumFreeSize,
8325 VkDeviceSize* pSumItemSize) const
8326 {
8327 VMA_ASSERT(allocSize > 0);
8328 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8329 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8330 VMA_ASSERT(pOffset != VMA_NULL);
8331
8332 *itemsToMakeLostCount = 0;
8333 *pSumFreeSize = 0;
8334 *pSumItemSize = 0;
8335
8336 if(canMakeOtherLost)
8337 {
8338 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8339 {
8340 *pSumFreeSize = suballocItem->size;
8341 }
8342 else
8343 {
8344 if(suballocItem->hAllocation->CanBecomeLost() &&
8345 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8346 {
8347 ++*itemsToMakeLostCount;
8348 *pSumItemSize = suballocItem->size;
8349 }
8350 else
8351 {
8352 return false;
8353 }
8354 }
8355
8356 // Remaining size is too small for this request: Early return.
8357 if(GetSize() - suballocItem->offset < allocSize)
8358 {
8359 return false;
8360 }
8361
8362 // Start from offset equal to beginning of this suballocation.
8363 *pOffset = suballocItem->offset;
8364
8365 // Apply VMA_DEBUG_MARGIN at the beginning.
8366 if(VMA_DEBUG_MARGIN > 0)
8367 {
8368 *pOffset += VMA_DEBUG_MARGIN;
8369 }
8370
8371 // Apply alignment.
8372 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8373
8374 // Check previous suballocations for BufferImageGranularity conflicts.
8375 // Make bigger alignment if necessary.
8376 if(bufferImageGranularity > 1)
8377 {
8378 bool bufferImageGranularityConflict = false;
8379 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8380 while(prevSuballocItem != m_Suballocations.cbegin())
8381 {
8382 --prevSuballocItem;
8383 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8384 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8385 {
8386 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8387 {
8388 bufferImageGranularityConflict = true;
8389 break;
8390 }
8391 }
8392 else
8393 // Already on previous page.
8394 break;
8395 }
8396 if(bufferImageGranularityConflict)
8397 {
8398 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8399 }
8400 }
8401
8402 // Now that we have final *pOffset, check if we are past suballocItem.
8403 // If yes, return false - this function should be called for another suballocItem as starting point.
8404 if(*pOffset >= suballocItem->offset + suballocItem->size)
8405 {
8406 return false;
8407 }
8408
8409 // Calculate padding at the beginning based on current offset.
8410 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8411
8412 // Calculate required margin at the end.
8413 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8414
8415 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8416 // Another early return check.
8417 if(suballocItem->offset + totalSize > GetSize())
8418 {
8419 return false;
8420 }
8421
8422 // Advance lastSuballocItem until desired size is reached.
8423 // Update itemsToMakeLostCount.
8424 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8425 if(totalSize > suballocItem->size)
8426 {
8427 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8428 while(remainingSize > 0)
8429 {
8430 ++lastSuballocItem;
8431 if(lastSuballocItem == m_Suballocations.cend())
8432 {
8433 return false;
8434 }
8435 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8436 {
8437 *pSumFreeSize += lastSuballocItem->size;
8438 }
8439 else
8440 {
8441 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8442 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8443 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8444 {
8445 ++*itemsToMakeLostCount;
8446 *pSumItemSize += lastSuballocItem->size;
8447 }
8448 else
8449 {
8450 return false;
8451 }
8452 }
8453 remainingSize = (lastSuballocItem->size < remainingSize) ?
8454 remainingSize - lastSuballocItem->size : 0;
8455 }
8456 }
8457
8458 // Check next suballocations for BufferImageGranularity conflicts.
8459 // If conflict exists, we must mark more allocations lost or fail.
8460 if(bufferImageGranularity > 1)
8461 {
8462 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8463 ++nextSuballocItem;
8464 while(nextSuballocItem != m_Suballocations.cend())
8465 {
8466 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8467 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8468 {
8469 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8470 {
8471 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8472 if(nextSuballoc.hAllocation->CanBecomeLost() &&
8473 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8474 {
8475 ++*itemsToMakeLostCount;
8476 }
8477 else
8478 {
8479 return false;
8480 }
8481 }
8482 }
8483 else
8484 {
8485 // Already on next page.
8486 break;
8487 }
8488 ++nextSuballocItem;
8489 }
8490 }
8491 }
8492 else
8493 {
8494 const VmaSuballocation& suballoc = *suballocItem;
8495 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8496
8497 *pSumFreeSize = suballoc.size;
8498
8499 // Size of this suballocation is too small for this request: Early return.
8500 if(suballoc.size < allocSize)
8501 {
8502 return false;
8503 }
8504
8505 // Start from offset equal to beginning of this suballocation.
8506 *pOffset = suballoc.offset;
8507
8508 // Apply VMA_DEBUG_MARGIN at the beginning.
8509 if(VMA_DEBUG_MARGIN > 0)
8510 {
8511 *pOffset += VMA_DEBUG_MARGIN;
8512 }
8513
8514 // Apply alignment.
8515 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8516
8517 // Check previous suballocations for BufferImageGranularity conflicts.
8518 // Make bigger alignment if necessary.
8519 if(bufferImageGranularity > 1)
8520 {
8521 bool bufferImageGranularityConflict = false;
8522 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8523 while(prevSuballocItem != m_Suballocations.cbegin())
8524 {
8525 --prevSuballocItem;
8526 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8527 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8528 {
8529 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8530 {
8531 bufferImageGranularityConflict = true;
8532 break;
8533 }
8534 }
8535 else
8536 // Already on previous page.
8537 break;
8538 }
8539 if(bufferImageGranularityConflict)
8540 {
8541 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8542 }
8543 }
8544
8545 // Calculate padding at the beginning based on current offset.
8546 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8547
8548 // Calculate required margin at the end.
8549 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8550
8551 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8552 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8553 {
8554 return false;
8555 }
8556
8557 // Check next suballocations for BufferImageGranularity conflicts.
8558 // If conflict exists, allocation cannot be made here.
8559 if(bufferImageGranularity > 1)
8560 {
8561 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8562 ++nextSuballocItem;
8563 while(nextSuballocItem != m_Suballocations.cend())
8564 {
8565 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8566 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8567 {
8568 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8569 {
8570 return false;
8571 }
8572 }
8573 else
8574 {
8575 // Already on next page.
8576 break;
8577 }
8578 ++nextSuballocItem;
8579 }
8580 }
8581 }
8582
8583 // All tests passed: Success. pOffset is already filled.
8584 return true;
8585 }
8586
MergeFreeWithNext(VmaSuballocationList::iterator item)8587 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8588 {
8589 VMA_ASSERT(item != m_Suballocations.end());
8590 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8591
8592 VmaSuballocationList::iterator nextItem = item;
8593 ++nextItem;
8594 VMA_ASSERT(nextItem != m_Suballocations.end());
8595 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8596
8597 item->size += nextItem->size;
8598 --m_FreeCount;
8599 m_Suballocations.erase(nextItem);
8600 }
8601
FreeSuballocation(VmaSuballocationList::iterator suballocItem)8602 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8603 {
8604 // Change this suballocation to be marked as free.
8605 VmaSuballocation& suballoc = *suballocItem;
8606 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8607 suballoc.hAllocation = VK_NULL_HANDLE;
8608
8609 // Update totals.
8610 ++m_FreeCount;
8611 m_SumFreeSize += suballoc.size;
8612
8613 // Merge with previous and/or next suballocation if it's also free.
8614 bool mergeWithNext = false;
8615 bool mergeWithPrev = false;
8616
8617 VmaSuballocationList::iterator nextItem = suballocItem;
8618 ++nextItem;
8619 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8620 {
8621 mergeWithNext = true;
8622 }
8623
8624 VmaSuballocationList::iterator prevItem = suballocItem;
8625 if(suballocItem != m_Suballocations.begin())
8626 {
8627 --prevItem;
8628 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8629 {
8630 mergeWithPrev = true;
8631 }
8632 }
8633
8634 if(mergeWithNext)
8635 {
8636 UnregisterFreeSuballocation(nextItem);
8637 MergeFreeWithNext(suballocItem);
8638 }
8639
8640 if(mergeWithPrev)
8641 {
8642 UnregisterFreeSuballocation(prevItem);
8643 MergeFreeWithNext(prevItem);
8644 RegisterFreeSuballocation(prevItem);
8645 return prevItem;
8646 }
8647 else
8648 {
8649 RegisterFreeSuballocation(suballocItem);
8650 return suballocItem;
8651 }
8652 }
8653
RegisterFreeSuballocation(VmaSuballocationList::iterator item)8654 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8655 {
8656 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8657 VMA_ASSERT(item->size > 0);
8658
8659 // You may want to enable this validation at the beginning or at the end of
8660 // this function, depending on what do you want to check.
8661 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8662
8663 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8664 {
8665 if(m_FreeSuballocationsBySize.empty())
8666 {
8667 m_FreeSuballocationsBySize.push_back(item);
8668 }
8669 else
8670 {
8671 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8672 }
8673 }
8674
8675 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8676 }
8677
8678
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)8679 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8680 {
8681 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8682 VMA_ASSERT(item->size > 0);
8683
8684 // You may want to enable this validation at the beginning or at the end of
8685 // this function, depending on what do you want to check.
8686 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8687
8688 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8689 {
8690 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8691 m_FreeSuballocationsBySize.data(),
8692 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8693 item,
8694 VmaSuballocationItemSizeLess());
8695 for(size_t index = it - m_FreeSuballocationsBySize.data();
8696 index < m_FreeSuballocationsBySize.size();
8697 ++index)
8698 {
8699 if(m_FreeSuballocationsBySize[index] == item)
8700 {
8701 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8702 return;
8703 }
8704 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8705 }
8706 VMA_ASSERT(0 && "Not found.");
8707 }
8708
8709 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8710 }
8711
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)8712 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8713 VkDeviceSize bufferImageGranularity,
8714 VmaSuballocationType& inOutPrevSuballocType) const
8715 {
8716 if(bufferImageGranularity == 1 || IsEmpty())
8717 {
8718 return false;
8719 }
8720
8721 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8722 bool typeConflictFound = false;
8723 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8724 it != m_Suballocations.cend();
8725 ++it)
8726 {
8727 const VmaSuballocationType suballocType = it->type;
8728 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8729 {
8730 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8731 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8732 {
8733 typeConflictFound = true;
8734 }
8735 inOutPrevSuballocType = suballocType;
8736 }
8737 }
8738
8739 return typeConflictFound || minAlignment >= bufferImageGranularity;
8740 }
8741
8742 ////////////////////////////////////////////////////////////////////////////////
8743 // class VmaBlockMetadata_Linear
8744
VmaBlockMetadata_Linear(VmaAllocator hAllocator)8745 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8746 VmaBlockMetadata(hAllocator),
8747 m_SumFreeSize(0),
8748 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8749 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8750 m_1stVectorIndex(0),
8751 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8752 m_1stNullItemsBeginCount(0),
8753 m_1stNullItemsMiddleCount(0),
8754 m_2ndNullItemsCount(0)
8755 {
8756 }
8757
~VmaBlockMetadata_Linear()8758 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8759 {
8760 }
8761
Init(VkDeviceSize size)8762 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8763 {
8764 VmaBlockMetadata::Init(size);
8765 m_SumFreeSize = size;
8766 }
8767
Validate()8768 bool VmaBlockMetadata_Linear::Validate() const
8769 {
8770 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8771 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8772
8773 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8774 VMA_VALIDATE(!suballocations1st.empty() ||
8775 suballocations2nd.empty() ||
8776 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8777
8778 if(!suballocations1st.empty())
8779 {
8780 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8781 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8782 // Null item at the end should be just pop_back().
8783 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8784 }
8785 if(!suballocations2nd.empty())
8786 {
8787 // Null item at the end should be just pop_back().
8788 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8789 }
8790
8791 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8792 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8793
8794 VkDeviceSize sumUsedSize = 0;
8795 const size_t suballoc1stCount = suballocations1st.size();
8796 VkDeviceSize offset = VMA_DEBUG_MARGIN;
8797
8798 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8799 {
8800 const size_t suballoc2ndCount = suballocations2nd.size();
8801 size_t nullItem2ndCount = 0;
8802 for(size_t i = 0; i < suballoc2ndCount; ++i)
8803 {
8804 const VmaSuballocation& suballoc = suballocations2nd[i];
8805 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8806
8807 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8808 VMA_VALIDATE(suballoc.offset >= offset);
8809
8810 if(!currFree)
8811 {
8812 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8813 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8814 sumUsedSize += suballoc.size;
8815 }
8816 else
8817 {
8818 ++nullItem2ndCount;
8819 }
8820
8821 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8822 }
8823
8824 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8825 }
8826
8827 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8828 {
8829 const VmaSuballocation& suballoc = suballocations1st[i];
8830 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8831 suballoc.hAllocation == VK_NULL_HANDLE);
8832 }
8833
8834 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8835
8836 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8837 {
8838 const VmaSuballocation& suballoc = suballocations1st[i];
8839 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8840
8841 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8842 VMA_VALIDATE(suballoc.offset >= offset);
8843 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8844
8845 if(!currFree)
8846 {
8847 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8848 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8849 sumUsedSize += suballoc.size;
8850 }
8851 else
8852 {
8853 ++nullItem1stCount;
8854 }
8855
8856 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8857 }
8858 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8859
8860 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8861 {
8862 const size_t suballoc2ndCount = suballocations2nd.size();
8863 size_t nullItem2ndCount = 0;
8864 for(size_t i = suballoc2ndCount; i--; )
8865 {
8866 const VmaSuballocation& suballoc = suballocations2nd[i];
8867 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8868
8869 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8870 VMA_VALIDATE(suballoc.offset >= offset);
8871
8872 if(!currFree)
8873 {
8874 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8875 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8876 sumUsedSize += suballoc.size;
8877 }
8878 else
8879 {
8880 ++nullItem2ndCount;
8881 }
8882
8883 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8884 }
8885
8886 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8887 }
8888
8889 VMA_VALIDATE(offset <= GetSize());
8890 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8891
8892 return true;
8893 }
8894
GetAllocationCount()8895 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8896 {
8897 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8898 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8899 }
8900
GetUnusedRangeSizeMax()8901 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8902 {
8903 const VkDeviceSize size = GetSize();
8904
8905 /*
8906 We don't consider gaps inside allocation vectors with freed allocations because
8907 they are not suitable for reuse in linear allocator. We consider only space that
8908 is available for new allocations.
8909 */
8910 if(IsEmpty())
8911 {
8912 return size;
8913 }
8914
8915 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8916
8917 switch(m_2ndVectorMode)
8918 {
8919 case SECOND_VECTOR_EMPTY:
8920 /*
8921 Available space is after end of 1st, as well as before beginning of 1st (which
8922 whould make it a ring buffer).
8923 */
8924 {
8925 const size_t suballocations1stCount = suballocations1st.size();
8926 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8927 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8928 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8929 return VMA_MAX(
8930 firstSuballoc.offset,
8931 size - (lastSuballoc.offset + lastSuballoc.size));
8932 }
8933 break;
8934
8935 case SECOND_VECTOR_RING_BUFFER:
8936 /*
8937 Available space is only between end of 2nd and beginning of 1st.
8938 */
8939 {
8940 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8941 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8942 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8943 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8944 }
8945 break;
8946
8947 case SECOND_VECTOR_DOUBLE_STACK:
8948 /*
8949 Available space is only between end of 1st and top of 2nd.
8950 */
8951 {
8952 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8953 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8954 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8955 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8956 }
8957 break;
8958
8959 default:
8960 VMA_ASSERT(0);
8961 return 0;
8962 }
8963 }
8964
CalcAllocationStatInfo(VmaStatInfo & outInfo)8965 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8966 {
8967 const VkDeviceSize size = GetSize();
8968 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8969 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8970 const size_t suballoc1stCount = suballocations1st.size();
8971 const size_t suballoc2ndCount = suballocations2nd.size();
8972
8973 outInfo.blockCount = 1;
8974 outInfo.allocationCount = (uint32_t)GetAllocationCount();
8975 outInfo.unusedRangeCount = 0;
8976 outInfo.usedBytes = 0;
8977 outInfo.allocationSizeMin = UINT64_MAX;
8978 outInfo.allocationSizeMax = 0;
8979 outInfo.unusedRangeSizeMin = UINT64_MAX;
8980 outInfo.unusedRangeSizeMax = 0;
8981
8982 VkDeviceSize lastOffset = 0;
8983
8984 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8985 {
8986 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8987 size_t nextAlloc2ndIndex = 0;
8988 while(lastOffset < freeSpace2ndTo1stEnd)
8989 {
8990 // Find next non-null allocation or move nextAllocIndex to the end.
8991 while(nextAlloc2ndIndex < suballoc2ndCount &&
8992 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8993 {
8994 ++nextAlloc2ndIndex;
8995 }
8996
8997 // Found non-null allocation.
8998 if(nextAlloc2ndIndex < suballoc2ndCount)
8999 {
9000 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9001
9002 // 1. Process free space before this allocation.
9003 if(lastOffset < suballoc.offset)
9004 {
9005 // There is free space from lastOffset to suballoc.offset.
9006 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9007 ++outInfo.unusedRangeCount;
9008 outInfo.unusedBytes += unusedRangeSize;
9009 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9010 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9011 }
9012
9013 // 2. Process this allocation.
9014 // There is allocation with suballoc.offset, suballoc.size.
9015 outInfo.usedBytes += suballoc.size;
9016 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9017 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9018
9019 // 3. Prepare for next iteration.
9020 lastOffset = suballoc.offset + suballoc.size;
9021 ++nextAlloc2ndIndex;
9022 }
9023 // We are at the end.
9024 else
9025 {
9026 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9027 if(lastOffset < freeSpace2ndTo1stEnd)
9028 {
9029 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9030 ++outInfo.unusedRangeCount;
9031 outInfo.unusedBytes += unusedRangeSize;
9032 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9033 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9034 }
9035
9036 // End of loop.
9037 lastOffset = freeSpace2ndTo1stEnd;
9038 }
9039 }
9040 }
9041
9042 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9043 const VkDeviceSize freeSpace1stTo2ndEnd =
9044 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9045 while(lastOffset < freeSpace1stTo2ndEnd)
9046 {
9047 // Find next non-null allocation or move nextAllocIndex to the end.
9048 while(nextAlloc1stIndex < suballoc1stCount &&
9049 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9050 {
9051 ++nextAlloc1stIndex;
9052 }
9053
9054 // Found non-null allocation.
9055 if(nextAlloc1stIndex < suballoc1stCount)
9056 {
9057 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9058
9059 // 1. Process free space before this allocation.
9060 if(lastOffset < suballoc.offset)
9061 {
9062 // There is free space from lastOffset to suballoc.offset.
9063 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9064 ++outInfo.unusedRangeCount;
9065 outInfo.unusedBytes += unusedRangeSize;
9066 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9067 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9068 }
9069
9070 // 2. Process this allocation.
9071 // There is allocation with suballoc.offset, suballoc.size.
9072 outInfo.usedBytes += suballoc.size;
9073 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9074 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9075
9076 // 3. Prepare for next iteration.
9077 lastOffset = suballoc.offset + suballoc.size;
9078 ++nextAlloc1stIndex;
9079 }
9080 // We are at the end.
9081 else
9082 {
9083 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9084 if(lastOffset < freeSpace1stTo2ndEnd)
9085 {
9086 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9087 ++outInfo.unusedRangeCount;
9088 outInfo.unusedBytes += unusedRangeSize;
9089 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9090 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9091 }
9092
9093 // End of loop.
9094 lastOffset = freeSpace1stTo2ndEnd;
9095 }
9096 }
9097
9098 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9099 {
9100 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9101 while(lastOffset < size)
9102 {
9103 // Find next non-null allocation or move nextAllocIndex to the end.
9104 while(nextAlloc2ndIndex != SIZE_MAX &&
9105 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9106 {
9107 --nextAlloc2ndIndex;
9108 }
9109
9110 // Found non-null allocation.
9111 if(nextAlloc2ndIndex != SIZE_MAX)
9112 {
9113 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9114
9115 // 1. Process free space before this allocation.
9116 if(lastOffset < suballoc.offset)
9117 {
9118 // There is free space from lastOffset to suballoc.offset.
9119 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9120 ++outInfo.unusedRangeCount;
9121 outInfo.unusedBytes += unusedRangeSize;
9122 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9123 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9124 }
9125
9126 // 2. Process this allocation.
9127 // There is allocation with suballoc.offset, suballoc.size.
9128 outInfo.usedBytes += suballoc.size;
9129 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9130 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9131
9132 // 3. Prepare for next iteration.
9133 lastOffset = suballoc.offset + suballoc.size;
9134 --nextAlloc2ndIndex;
9135 }
9136 // We are at the end.
9137 else
9138 {
9139 // There is free space from lastOffset to size.
9140 if(lastOffset < size)
9141 {
9142 const VkDeviceSize unusedRangeSize = size - lastOffset;
9143 ++outInfo.unusedRangeCount;
9144 outInfo.unusedBytes += unusedRangeSize;
9145 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9146 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9147 }
9148
9149 // End of loop.
9150 lastOffset = size;
9151 }
9152 }
9153 }
9154
9155 outInfo.unusedBytes = size - outInfo.usedBytes;
9156 }
9157
AddPoolStats(VmaPoolStats & inoutStats)9158 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9159 {
9160 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9161 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9162 const VkDeviceSize size = GetSize();
9163 const size_t suballoc1stCount = suballocations1st.size();
9164 const size_t suballoc2ndCount = suballocations2nd.size();
9165
9166 inoutStats.size += size;
9167
9168 VkDeviceSize lastOffset = 0;
9169
9170 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9171 {
9172 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9173 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9174 while(lastOffset < freeSpace2ndTo1stEnd)
9175 {
9176 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9177 while(nextAlloc2ndIndex < suballoc2ndCount &&
9178 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9179 {
9180 ++nextAlloc2ndIndex;
9181 }
9182
9183 // Found non-null allocation.
9184 if(nextAlloc2ndIndex < suballoc2ndCount)
9185 {
9186 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9187
9188 // 1. Process free space before this allocation.
9189 if(lastOffset < suballoc.offset)
9190 {
9191 // There is free space from lastOffset to suballoc.offset.
9192 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9193 inoutStats.unusedSize += unusedRangeSize;
9194 ++inoutStats.unusedRangeCount;
9195 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9196 }
9197
9198 // 2. Process this allocation.
9199 // There is allocation with suballoc.offset, suballoc.size.
9200 ++inoutStats.allocationCount;
9201
9202 // 3. Prepare for next iteration.
9203 lastOffset = suballoc.offset + suballoc.size;
9204 ++nextAlloc2ndIndex;
9205 }
9206 // We are at the end.
9207 else
9208 {
9209 if(lastOffset < freeSpace2ndTo1stEnd)
9210 {
9211 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9212 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9213 inoutStats.unusedSize += unusedRangeSize;
9214 ++inoutStats.unusedRangeCount;
9215 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9216 }
9217
9218 // End of loop.
9219 lastOffset = freeSpace2ndTo1stEnd;
9220 }
9221 }
9222 }
9223
9224 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9225 const VkDeviceSize freeSpace1stTo2ndEnd =
9226 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9227 while(lastOffset < freeSpace1stTo2ndEnd)
9228 {
9229 // Find next non-null allocation or move nextAllocIndex to the end.
9230 while(nextAlloc1stIndex < suballoc1stCount &&
9231 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9232 {
9233 ++nextAlloc1stIndex;
9234 }
9235
9236 // Found non-null allocation.
9237 if(nextAlloc1stIndex < suballoc1stCount)
9238 {
9239 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9240
9241 // 1. Process free space before this allocation.
9242 if(lastOffset < suballoc.offset)
9243 {
9244 // There is free space from lastOffset to suballoc.offset.
9245 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9246 inoutStats.unusedSize += unusedRangeSize;
9247 ++inoutStats.unusedRangeCount;
9248 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9249 }
9250
9251 // 2. Process this allocation.
9252 // There is allocation with suballoc.offset, suballoc.size.
9253 ++inoutStats.allocationCount;
9254
9255 // 3. Prepare for next iteration.
9256 lastOffset = suballoc.offset + suballoc.size;
9257 ++nextAlloc1stIndex;
9258 }
9259 // We are at the end.
9260 else
9261 {
9262 if(lastOffset < freeSpace1stTo2ndEnd)
9263 {
9264 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9265 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9266 inoutStats.unusedSize += unusedRangeSize;
9267 ++inoutStats.unusedRangeCount;
9268 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9269 }
9270
9271 // End of loop.
9272 lastOffset = freeSpace1stTo2ndEnd;
9273 }
9274 }
9275
9276 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9277 {
9278 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9279 while(lastOffset < size)
9280 {
9281 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9282 while(nextAlloc2ndIndex != SIZE_MAX &&
9283 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9284 {
9285 --nextAlloc2ndIndex;
9286 }
9287
9288 // Found non-null allocation.
9289 if(nextAlloc2ndIndex != SIZE_MAX)
9290 {
9291 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9292
9293 // 1. Process free space before this allocation.
9294 if(lastOffset < suballoc.offset)
9295 {
9296 // There is free space from lastOffset to suballoc.offset.
9297 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9298 inoutStats.unusedSize += unusedRangeSize;
9299 ++inoutStats.unusedRangeCount;
9300 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9301 }
9302
9303 // 2. Process this allocation.
9304 // There is allocation with suballoc.offset, suballoc.size.
9305 ++inoutStats.allocationCount;
9306
9307 // 3. Prepare for next iteration.
9308 lastOffset = suballoc.offset + suballoc.size;
9309 --nextAlloc2ndIndex;
9310 }
9311 // We are at the end.
9312 else
9313 {
9314 if(lastOffset < size)
9315 {
9316 // There is free space from lastOffset to size.
9317 const VkDeviceSize unusedRangeSize = size - lastOffset;
9318 inoutStats.unusedSize += unusedRangeSize;
9319 ++inoutStats.unusedRangeCount;
9320 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9321 }
9322
9323 // End of loop.
9324 lastOffset = size;
9325 }
9326 }
9327 }
9328 }
9329
9330 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)9331 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9332 {
9333 const VkDeviceSize size = GetSize();
9334 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9335 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9336 const size_t suballoc1stCount = suballocations1st.size();
9337 const size_t suballoc2ndCount = suballocations2nd.size();
9338
9339 // FIRST PASS
9340
9341 size_t unusedRangeCount = 0;
9342 VkDeviceSize usedBytes = 0;
9343
9344 VkDeviceSize lastOffset = 0;
9345
9346 size_t alloc2ndCount = 0;
9347 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9348 {
9349 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9350 size_t nextAlloc2ndIndex = 0;
9351 while(lastOffset < freeSpace2ndTo1stEnd)
9352 {
9353 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9354 while(nextAlloc2ndIndex < suballoc2ndCount &&
9355 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9356 {
9357 ++nextAlloc2ndIndex;
9358 }
9359
9360 // Found non-null allocation.
9361 if(nextAlloc2ndIndex < suballoc2ndCount)
9362 {
9363 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9364
9365 // 1. Process free space before this allocation.
9366 if(lastOffset < suballoc.offset)
9367 {
9368 // There is free space from lastOffset to suballoc.offset.
9369 ++unusedRangeCount;
9370 }
9371
9372 // 2. Process this allocation.
9373 // There is allocation with suballoc.offset, suballoc.size.
9374 ++alloc2ndCount;
9375 usedBytes += suballoc.size;
9376
9377 // 3. Prepare for next iteration.
9378 lastOffset = suballoc.offset + suballoc.size;
9379 ++nextAlloc2ndIndex;
9380 }
9381 // We are at the end.
9382 else
9383 {
9384 if(lastOffset < freeSpace2ndTo1stEnd)
9385 {
9386 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9387 ++unusedRangeCount;
9388 }
9389
9390 // End of loop.
9391 lastOffset = freeSpace2ndTo1stEnd;
9392 }
9393 }
9394 }
9395
9396 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9397 size_t alloc1stCount = 0;
9398 const VkDeviceSize freeSpace1stTo2ndEnd =
9399 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9400 while(lastOffset < freeSpace1stTo2ndEnd)
9401 {
9402 // Find next non-null allocation or move nextAllocIndex to the end.
9403 while(nextAlloc1stIndex < suballoc1stCount &&
9404 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9405 {
9406 ++nextAlloc1stIndex;
9407 }
9408
9409 // Found non-null allocation.
9410 if(nextAlloc1stIndex < suballoc1stCount)
9411 {
9412 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9413
9414 // 1. Process free space before this allocation.
9415 if(lastOffset < suballoc.offset)
9416 {
9417 // There is free space from lastOffset to suballoc.offset.
9418 ++unusedRangeCount;
9419 }
9420
9421 // 2. Process this allocation.
9422 // There is allocation with suballoc.offset, suballoc.size.
9423 ++alloc1stCount;
9424 usedBytes += suballoc.size;
9425
9426 // 3. Prepare for next iteration.
9427 lastOffset = suballoc.offset + suballoc.size;
9428 ++nextAlloc1stIndex;
9429 }
9430 // We are at the end.
9431 else
9432 {
9433 if(lastOffset < size)
9434 {
9435 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9436 ++unusedRangeCount;
9437 }
9438
9439 // End of loop.
9440 lastOffset = freeSpace1stTo2ndEnd;
9441 }
9442 }
9443
9444 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9445 {
9446 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9447 while(lastOffset < size)
9448 {
9449 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9450 while(nextAlloc2ndIndex != SIZE_MAX &&
9451 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9452 {
9453 --nextAlloc2ndIndex;
9454 }
9455
9456 // Found non-null allocation.
9457 if(nextAlloc2ndIndex != SIZE_MAX)
9458 {
9459 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9460
9461 // 1. Process free space before this allocation.
9462 if(lastOffset < suballoc.offset)
9463 {
9464 // There is free space from lastOffset to suballoc.offset.
9465 ++unusedRangeCount;
9466 }
9467
9468 // 2. Process this allocation.
9469 // There is allocation with suballoc.offset, suballoc.size.
9470 ++alloc2ndCount;
9471 usedBytes += suballoc.size;
9472
9473 // 3. Prepare for next iteration.
9474 lastOffset = suballoc.offset + suballoc.size;
9475 --nextAlloc2ndIndex;
9476 }
9477 // We are at the end.
9478 else
9479 {
9480 if(lastOffset < size)
9481 {
9482 // There is free space from lastOffset to size.
9483 ++unusedRangeCount;
9484 }
9485
9486 // End of loop.
9487 lastOffset = size;
9488 }
9489 }
9490 }
9491
9492 const VkDeviceSize unusedBytes = size - usedBytes;
9493 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9494
9495 // SECOND PASS
9496 lastOffset = 0;
9497
9498 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9499 {
9500 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9501 size_t nextAlloc2ndIndex = 0;
9502 while(lastOffset < freeSpace2ndTo1stEnd)
9503 {
9504 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9505 while(nextAlloc2ndIndex < suballoc2ndCount &&
9506 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9507 {
9508 ++nextAlloc2ndIndex;
9509 }
9510
9511 // Found non-null allocation.
9512 if(nextAlloc2ndIndex < suballoc2ndCount)
9513 {
9514 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9515
9516 // 1. Process free space before this allocation.
9517 if(lastOffset < suballoc.offset)
9518 {
9519 // There is free space from lastOffset to suballoc.offset.
9520 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9521 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9522 }
9523
9524 // 2. Process this allocation.
9525 // There is allocation with suballoc.offset, suballoc.size.
9526 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9527
9528 // 3. Prepare for next iteration.
9529 lastOffset = suballoc.offset + suballoc.size;
9530 ++nextAlloc2ndIndex;
9531 }
9532 // We are at the end.
9533 else
9534 {
9535 if(lastOffset < freeSpace2ndTo1stEnd)
9536 {
9537 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9538 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9539 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9540 }
9541
9542 // End of loop.
9543 lastOffset = freeSpace2ndTo1stEnd;
9544 }
9545 }
9546 }
9547
9548 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9549 while(lastOffset < freeSpace1stTo2ndEnd)
9550 {
9551 // Find next non-null allocation or move nextAllocIndex to the end.
9552 while(nextAlloc1stIndex < suballoc1stCount &&
9553 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9554 {
9555 ++nextAlloc1stIndex;
9556 }
9557
9558 // Found non-null allocation.
9559 if(nextAlloc1stIndex < suballoc1stCount)
9560 {
9561 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9562
9563 // 1. Process free space before this allocation.
9564 if(lastOffset < suballoc.offset)
9565 {
9566 // There is free space from lastOffset to suballoc.offset.
9567 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9568 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9569 }
9570
9571 // 2. Process this allocation.
9572 // There is allocation with suballoc.offset, suballoc.size.
9573 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9574
9575 // 3. Prepare for next iteration.
9576 lastOffset = suballoc.offset + suballoc.size;
9577 ++nextAlloc1stIndex;
9578 }
9579 // We are at the end.
9580 else
9581 {
9582 if(lastOffset < freeSpace1stTo2ndEnd)
9583 {
9584 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9585 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9586 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9587 }
9588
9589 // End of loop.
9590 lastOffset = freeSpace1stTo2ndEnd;
9591 }
9592 }
9593
9594 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9595 {
9596 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9597 while(lastOffset < size)
9598 {
9599 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9600 while(nextAlloc2ndIndex != SIZE_MAX &&
9601 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9602 {
9603 --nextAlloc2ndIndex;
9604 }
9605
9606 // Found non-null allocation.
9607 if(nextAlloc2ndIndex != SIZE_MAX)
9608 {
9609 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9610
9611 // 1. Process free space before this allocation.
9612 if(lastOffset < suballoc.offset)
9613 {
9614 // There is free space from lastOffset to suballoc.offset.
9615 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9616 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9617 }
9618
9619 // 2. Process this allocation.
9620 // There is allocation with suballoc.offset, suballoc.size.
9621 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9622
9623 // 3. Prepare for next iteration.
9624 lastOffset = suballoc.offset + suballoc.size;
9625 --nextAlloc2ndIndex;
9626 }
9627 // We are at the end.
9628 else
9629 {
9630 if(lastOffset < size)
9631 {
9632 // There is free space from lastOffset to size.
9633 const VkDeviceSize unusedRangeSize = size - lastOffset;
9634 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9635 }
9636
9637 // End of loop.
9638 lastOffset = size;
9639 }
9640 }
9641 }
9642
9643 PrintDetailedMap_End(json);
9644 }
9645 #endif // #if VMA_STATS_STRING_ENABLED
9646
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9647 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9648 uint32_t currentFrameIndex,
9649 uint32_t frameInUseCount,
9650 VkDeviceSize bufferImageGranularity,
9651 VkDeviceSize allocSize,
9652 VkDeviceSize allocAlignment,
9653 bool upperAddress,
9654 VmaSuballocationType allocType,
9655 bool canMakeOtherLost,
9656 uint32_t strategy,
9657 VmaAllocationRequest* pAllocationRequest)
9658 {
9659 VMA_ASSERT(allocSize > 0);
9660 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9661 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9662 VMA_HEAVY_ASSERT(Validate());
9663
9664 const VkDeviceSize size = GetSize();
9665 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9666 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9667
9668 if(upperAddress)
9669 {
9670 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9671 {
9672 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9673 return false;
9674 }
9675
9676 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9677 if(allocSize > size)
9678 {
9679 return false;
9680 }
9681 VkDeviceSize resultBaseOffset = size - allocSize;
9682 if(!suballocations2nd.empty())
9683 {
9684 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9685 resultBaseOffset = lastSuballoc.offset - allocSize;
9686 if(allocSize > lastSuballoc.offset)
9687 {
9688 return false;
9689 }
9690 }
9691
9692 // Start from offset equal to end of free space.
9693 VkDeviceSize resultOffset = resultBaseOffset;
9694
9695 // Apply VMA_DEBUG_MARGIN at the end.
9696 if(VMA_DEBUG_MARGIN > 0)
9697 {
9698 if(resultOffset < VMA_DEBUG_MARGIN)
9699 {
9700 return false;
9701 }
9702 resultOffset -= VMA_DEBUG_MARGIN;
9703 }
9704
9705 // Apply alignment.
9706 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9707
9708 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9709 // Make bigger alignment if necessary.
9710 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9711 {
9712 bool bufferImageGranularityConflict = false;
9713 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9714 {
9715 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9716 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9717 {
9718 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9719 {
9720 bufferImageGranularityConflict = true;
9721 break;
9722 }
9723 }
9724 else
9725 // Already on previous page.
9726 break;
9727 }
9728 if(bufferImageGranularityConflict)
9729 {
9730 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9731 }
9732 }
9733
9734 // There is enough free space.
9735 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9736 suballocations1st.back().offset + suballocations1st.back().size :
9737 0;
9738 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9739 {
9740 // Check previous suballocations for BufferImageGranularity conflicts.
9741 // If conflict exists, allocation cannot be made here.
9742 if(bufferImageGranularity > 1)
9743 {
9744 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9745 {
9746 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9747 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9748 {
9749 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9750 {
9751 return false;
9752 }
9753 }
9754 else
9755 {
9756 // Already on next page.
9757 break;
9758 }
9759 }
9760 }
9761
9762 // All tests passed: Success.
9763 pAllocationRequest->offset = resultOffset;
9764 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9765 pAllocationRequest->sumItemSize = 0;
9766 // pAllocationRequest->item unused.
9767 pAllocationRequest->itemsToMakeLostCount = 0;
9768 return true;
9769 }
9770 }
9771 else // !upperAddress
9772 {
9773 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9774 {
9775 // Try to allocate at the end of 1st vector.
9776
9777 VkDeviceSize resultBaseOffset = 0;
9778 if(!suballocations1st.empty())
9779 {
9780 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9781 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9782 }
9783
9784 // Start from offset equal to beginning of free space.
9785 VkDeviceSize resultOffset = resultBaseOffset;
9786
9787 // Apply VMA_DEBUG_MARGIN at the beginning.
9788 if(VMA_DEBUG_MARGIN > 0)
9789 {
9790 resultOffset += VMA_DEBUG_MARGIN;
9791 }
9792
9793 // Apply alignment.
9794 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9795
9796 // Check previous suballocations for BufferImageGranularity conflicts.
9797 // Make bigger alignment if necessary.
9798 if(bufferImageGranularity > 1 && !suballocations1st.empty())
9799 {
9800 bool bufferImageGranularityConflict = false;
9801 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9802 {
9803 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9804 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9805 {
9806 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9807 {
9808 bufferImageGranularityConflict = true;
9809 break;
9810 }
9811 }
9812 else
9813 // Already on previous page.
9814 break;
9815 }
9816 if(bufferImageGranularityConflict)
9817 {
9818 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9819 }
9820 }
9821
9822 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9823 suballocations2nd.back().offset : size;
9824
9825 // There is enough free space at the end after alignment.
9826 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9827 {
9828 // Check next suballocations for BufferImageGranularity conflicts.
9829 // If conflict exists, allocation cannot be made here.
9830 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9831 {
9832 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9833 {
9834 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9835 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9836 {
9837 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9838 {
9839 return false;
9840 }
9841 }
9842 else
9843 {
9844 // Already on previous page.
9845 break;
9846 }
9847 }
9848 }
9849
9850 // All tests passed: Success.
9851 pAllocationRequest->offset = resultOffset;
9852 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9853 pAllocationRequest->sumItemSize = 0;
9854 // pAllocationRequest->item unused.
9855 pAllocationRequest->itemsToMakeLostCount = 0;
9856 return true;
9857 }
9858 }
9859
9860 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9861 // beginning of 1st vector as the end of free space.
9862 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9863 {
9864 VMA_ASSERT(!suballocations1st.empty());
9865
9866 VkDeviceSize resultBaseOffset = 0;
9867 if(!suballocations2nd.empty())
9868 {
9869 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9870 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9871 }
9872
9873 // Start from offset equal to beginning of free space.
9874 VkDeviceSize resultOffset = resultBaseOffset;
9875
9876 // Apply VMA_DEBUG_MARGIN at the beginning.
9877 if(VMA_DEBUG_MARGIN > 0)
9878 {
9879 resultOffset += VMA_DEBUG_MARGIN;
9880 }
9881
9882 // Apply alignment.
9883 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9884
9885 // Check previous suballocations for BufferImageGranularity conflicts.
9886 // Make bigger alignment if necessary.
9887 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9888 {
9889 bool bufferImageGranularityConflict = false;
9890 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9891 {
9892 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9893 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9894 {
9895 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9896 {
9897 bufferImageGranularityConflict = true;
9898 break;
9899 }
9900 }
9901 else
9902 // Already on previous page.
9903 break;
9904 }
9905 if(bufferImageGranularityConflict)
9906 {
9907 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9908 }
9909 }
9910
9911 pAllocationRequest->itemsToMakeLostCount = 0;
9912 pAllocationRequest->sumItemSize = 0;
9913 size_t index1st = m_1stNullItemsBeginCount;
9914
9915 if(canMakeOtherLost)
9916 {
9917 while(index1st < suballocations1st.size() &&
9918 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9919 {
9920 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9921 const VmaSuballocation& suballoc = suballocations1st[index1st];
9922 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9923 {
9924 // No problem.
9925 }
9926 else
9927 {
9928 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9929 if(suballoc.hAllocation->CanBecomeLost() &&
9930 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9931 {
9932 ++pAllocationRequest->itemsToMakeLostCount;
9933 pAllocationRequest->sumItemSize += suballoc.size;
9934 }
9935 else
9936 {
9937 return false;
9938 }
9939 }
9940 ++index1st;
9941 }
9942
9943 // Check next suballocations for BufferImageGranularity conflicts.
9944 // If conflict exists, we must mark more allocations lost or fail.
9945 if(bufferImageGranularity > 1)
9946 {
9947 while(index1st < suballocations1st.size())
9948 {
9949 const VmaSuballocation& suballoc = suballocations1st[index1st];
9950 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9951 {
9952 if(suballoc.hAllocation != VK_NULL_HANDLE)
9953 {
9954 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9955 if(suballoc.hAllocation->CanBecomeLost() &&
9956 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9957 {
9958 ++pAllocationRequest->itemsToMakeLostCount;
9959 pAllocationRequest->sumItemSize += suballoc.size;
9960 }
9961 else
9962 {
9963 return false;
9964 }
9965 }
9966 }
9967 else
9968 {
9969 // Already on next page.
9970 break;
9971 }
9972 ++index1st;
9973 }
9974 }
9975 }
9976
9977 // There is enough free space at the end after alignment.
9978 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9979 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9980 {
9981 // Check next suballocations for BufferImageGranularity conflicts.
9982 // If conflict exists, allocation cannot be made here.
9983 if(bufferImageGranularity > 1)
9984 {
9985 for(size_t nextSuballocIndex = index1st;
9986 nextSuballocIndex < suballocations1st.size();
9987 nextSuballocIndex++)
9988 {
9989 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9990 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9991 {
9992 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9993 {
9994 return false;
9995 }
9996 }
9997 else
9998 {
9999 // Already on next page.
10000 break;
10001 }
10002 }
10003 }
10004
10005 // All tests passed: Success.
10006 pAllocationRequest->offset = resultOffset;
10007 pAllocationRequest->sumFreeSize =
10008 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
10009 - resultBaseOffset
10010 - pAllocationRequest->sumItemSize;
10011 // pAllocationRequest->item unused.
10012 return true;
10013 }
10014 }
10015 }
10016
10017 return false;
10018 }
10019
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10020 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10021 uint32_t currentFrameIndex,
10022 uint32_t frameInUseCount,
10023 VmaAllocationRequest* pAllocationRequest)
10024 {
10025 if(pAllocationRequest->itemsToMakeLostCount == 0)
10026 {
10027 return true;
10028 }
10029
10030 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10031
10032 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10033 size_t index1st = m_1stNullItemsBeginCount;
10034 size_t madeLostCount = 0;
10035 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10036 {
10037 VMA_ASSERT(index1st < suballocations1st.size());
10038 VmaSuballocation& suballoc = suballocations1st[index1st];
10039 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10040 {
10041 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10042 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10043 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10044 {
10045 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10046 suballoc.hAllocation = VK_NULL_HANDLE;
10047 m_SumFreeSize += suballoc.size;
10048 ++m_1stNullItemsMiddleCount;
10049 ++madeLostCount;
10050 }
10051 else
10052 {
10053 return false;
10054 }
10055 }
10056 ++index1st;
10057 }
10058
10059 CleanupAfterFree();
10060 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10061
10062 return true;
10063 }
10064
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10065 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10066 {
10067 uint32_t lostAllocationCount = 0;
10068
10069 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10070 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10071 {
10072 VmaSuballocation& suballoc = suballocations1st[i];
10073 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10074 suballoc.hAllocation->CanBecomeLost() &&
10075 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10076 {
10077 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10078 suballoc.hAllocation = VK_NULL_HANDLE;
10079 ++m_1stNullItemsMiddleCount;
10080 m_SumFreeSize += suballoc.size;
10081 ++lostAllocationCount;
10082 }
10083 }
10084
10085 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10086 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10087 {
10088 VmaSuballocation& suballoc = suballocations2nd[i];
10089 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10090 suballoc.hAllocation->CanBecomeLost() &&
10091 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10092 {
10093 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10094 suballoc.hAllocation = VK_NULL_HANDLE;
10095 ++m_2ndNullItemsCount;
10096 ++lostAllocationCount;
10097 }
10098 }
10099
10100 if(lostAllocationCount)
10101 {
10102 CleanupAfterFree();
10103 }
10104
10105 return lostAllocationCount;
10106 }
10107
CheckCorruption(const void * pBlockData)10108 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10109 {
10110 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10111 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10112 {
10113 const VmaSuballocation& suballoc = suballocations1st[i];
10114 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10115 {
10116 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10117 {
10118 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10119 return VK_ERROR_VALIDATION_FAILED_EXT;
10120 }
10121 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10122 {
10123 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10124 return VK_ERROR_VALIDATION_FAILED_EXT;
10125 }
10126 }
10127 }
10128
10129 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10130 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10131 {
10132 const VmaSuballocation& suballoc = suballocations2nd[i];
10133 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10134 {
10135 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10136 {
10137 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10138 return VK_ERROR_VALIDATION_FAILED_EXT;
10139 }
10140 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10141 {
10142 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10143 return VK_ERROR_VALIDATION_FAILED_EXT;
10144 }
10145 }
10146 }
10147
10148 return VK_SUCCESS;
10149 }
10150
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)10151 void VmaBlockMetadata_Linear::Alloc(
10152 const VmaAllocationRequest& request,
10153 VmaSuballocationType type,
10154 VkDeviceSize allocSize,
10155 bool upperAddress,
10156 VmaAllocation hAllocation)
10157 {
10158 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10159
10160 if(upperAddress)
10161 {
10162 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10163 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10164 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10165 suballocations2nd.push_back(newSuballoc);
10166 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10167 }
10168 else
10169 {
10170 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10171
10172 // First allocation.
10173 if(suballocations1st.empty())
10174 {
10175 suballocations1st.push_back(newSuballoc);
10176 }
10177 else
10178 {
10179 // New allocation at the end of 1st vector.
10180 if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10181 {
10182 // Check if it fits before the end of the block.
10183 VMA_ASSERT(request.offset + allocSize <= GetSize());
10184 suballocations1st.push_back(newSuballoc);
10185 }
10186 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10187 else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10188 {
10189 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10190
10191 switch(m_2ndVectorMode)
10192 {
10193 case SECOND_VECTOR_EMPTY:
10194 // First allocation from second part ring buffer.
10195 VMA_ASSERT(suballocations2nd.empty());
10196 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10197 break;
10198 case SECOND_VECTOR_RING_BUFFER:
10199 // 2-part ring buffer is already started.
10200 VMA_ASSERT(!suballocations2nd.empty());
10201 break;
10202 case SECOND_VECTOR_DOUBLE_STACK:
10203 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10204 break;
10205 default:
10206 VMA_ASSERT(0);
10207 }
10208
10209 suballocations2nd.push_back(newSuballoc);
10210 }
10211 else
10212 {
10213 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10214 }
10215 }
10216 }
10217
10218 m_SumFreeSize -= newSuballoc.size;
10219 }
10220
Free(const VmaAllocation allocation)10221 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10222 {
10223 FreeAtOffset(allocation->GetOffset());
10224 }
10225
FreeAtOffset(VkDeviceSize offset)10226 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10227 {
10228 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10229 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10230
10231 if(!suballocations1st.empty())
10232 {
10233 // First allocation: Mark it as next empty at the beginning.
10234 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10235 if(firstSuballoc.offset == offset)
10236 {
10237 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10238 firstSuballoc.hAllocation = VK_NULL_HANDLE;
10239 m_SumFreeSize += firstSuballoc.size;
10240 ++m_1stNullItemsBeginCount;
10241 CleanupAfterFree();
10242 return;
10243 }
10244 }
10245
10246 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10247 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10248 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10249 {
10250 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10251 if(lastSuballoc.offset == offset)
10252 {
10253 m_SumFreeSize += lastSuballoc.size;
10254 suballocations2nd.pop_back();
10255 CleanupAfterFree();
10256 return;
10257 }
10258 }
10259 // Last allocation in 1st vector.
10260 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10261 {
10262 VmaSuballocation& lastSuballoc = suballocations1st.back();
10263 if(lastSuballoc.offset == offset)
10264 {
10265 m_SumFreeSize += lastSuballoc.size;
10266 suballocations1st.pop_back();
10267 CleanupAfterFree();
10268 return;
10269 }
10270 }
10271
10272 // Item from the middle of 1st vector.
10273 {
10274 VmaSuballocation refSuballoc;
10275 refSuballoc.offset = offset;
10276 // Rest of members stays uninitialized intentionally for better performance.
10277 SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10278 suballocations1st.begin() + m_1stNullItemsBeginCount,
10279 suballocations1st.end(),
10280 refSuballoc);
10281 if(it != suballocations1st.end())
10282 {
10283 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10284 it->hAllocation = VK_NULL_HANDLE;
10285 ++m_1stNullItemsMiddleCount;
10286 m_SumFreeSize += it->size;
10287 CleanupAfterFree();
10288 return;
10289 }
10290 }
10291
10292 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10293 {
10294 // Item from the middle of 2nd vector.
10295 VmaSuballocation refSuballoc;
10296 refSuballoc.offset = offset;
10297 // Rest of members stays uninitialized intentionally for better performance.
10298 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10299 VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10300 VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10301 if(it != suballocations2nd.end())
10302 {
10303 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10304 it->hAllocation = VK_NULL_HANDLE;
10305 ++m_2ndNullItemsCount;
10306 m_SumFreeSize += it->size;
10307 CleanupAfterFree();
10308 return;
10309 }
10310 }
10311
10312 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10313 }
10314
ShouldCompact1st()10315 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10316 {
10317 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10318 const size_t suballocCount = AccessSuballocations1st().size();
10319 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10320 }
10321
CleanupAfterFree()10322 void VmaBlockMetadata_Linear::CleanupAfterFree()
10323 {
10324 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10325 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10326
10327 if(IsEmpty())
10328 {
10329 suballocations1st.clear();
10330 suballocations2nd.clear();
10331 m_1stNullItemsBeginCount = 0;
10332 m_1stNullItemsMiddleCount = 0;
10333 m_2ndNullItemsCount = 0;
10334 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10335 }
10336 else
10337 {
10338 const size_t suballoc1stCount = suballocations1st.size();
10339 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10340 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10341
10342 // Find more null items at the beginning of 1st vector.
10343 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10344 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10345 {
10346 ++m_1stNullItemsBeginCount;
10347 --m_1stNullItemsMiddleCount;
10348 }
10349
10350 // Find more null items at the end of 1st vector.
10351 while(m_1stNullItemsMiddleCount > 0 &&
10352 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10353 {
10354 --m_1stNullItemsMiddleCount;
10355 suballocations1st.pop_back();
10356 }
10357
10358 // Find more null items at the end of 2nd vector.
10359 while(m_2ndNullItemsCount > 0 &&
10360 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10361 {
10362 --m_2ndNullItemsCount;
10363 suballocations2nd.pop_back();
10364 }
10365
10366 if(ShouldCompact1st())
10367 {
10368 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10369 size_t srcIndex = m_1stNullItemsBeginCount;
10370 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10371 {
10372 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10373 {
10374 ++srcIndex;
10375 }
10376 if(dstIndex != srcIndex)
10377 {
10378 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10379 }
10380 ++srcIndex;
10381 }
10382 suballocations1st.resize(nonNullItemCount);
10383 m_1stNullItemsBeginCount = 0;
10384 m_1stNullItemsMiddleCount = 0;
10385 }
10386
10387 // 2nd vector became empty.
10388 if(suballocations2nd.empty())
10389 {
10390 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10391 }
10392
10393 // 1st vector became empty.
10394 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10395 {
10396 suballocations1st.clear();
10397 m_1stNullItemsBeginCount = 0;
10398
10399 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10400 {
10401 // Swap 1st with 2nd. Now 2nd is empty.
10402 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10403 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10404 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10405 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10406 {
10407 ++m_1stNullItemsBeginCount;
10408 --m_1stNullItemsMiddleCount;
10409 }
10410 m_2ndNullItemsCount = 0;
10411 m_1stVectorIndex ^= 1;
10412 }
10413 }
10414 }
10415
10416 VMA_HEAVY_ASSERT(Validate());
10417 }
10418
10419
10420 ////////////////////////////////////////////////////////////////////////////////
10421 // class VmaBlockMetadata_Buddy
10422
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)10423 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10424 VmaBlockMetadata(hAllocator),
10425 m_Root(VMA_NULL),
10426 m_AllocationCount(0),
10427 m_FreeCount(1),
10428 m_SumFreeSize(0)
10429 {
10430 memset(m_FreeList, 0, sizeof(m_FreeList));
10431 }
10432
~VmaBlockMetadata_Buddy()10433 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10434 {
10435 DeleteNode(m_Root);
10436 }
10437
Init(VkDeviceSize size)10438 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10439 {
10440 VmaBlockMetadata::Init(size);
10441
10442 m_UsableSize = VmaPrevPow2(size);
10443 m_SumFreeSize = m_UsableSize;
10444
10445 // Calculate m_LevelCount.
10446 m_LevelCount = 1;
10447 while(m_LevelCount < MAX_LEVELS &&
10448 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10449 {
10450 ++m_LevelCount;
10451 }
10452
10453 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10454 rootNode->offset = 0;
10455 rootNode->type = Node::TYPE_FREE;
10456 rootNode->parent = VMA_NULL;
10457 rootNode->buddy = VMA_NULL;
10458
10459 m_Root = rootNode;
10460 AddToFreeListFront(0, rootNode);
10461 }
10462
Validate()10463 bool VmaBlockMetadata_Buddy::Validate() const
10464 {
10465 // Validate tree.
10466 ValidationContext ctx;
10467 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10468 {
10469 VMA_VALIDATE(false && "ValidateNode failed.");
10470 }
10471 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10472 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10473
10474 // Validate free node lists.
10475 for(uint32_t level = 0; level < m_LevelCount; ++level)
10476 {
10477 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10478 m_FreeList[level].front->free.prev == VMA_NULL);
10479
10480 for(Node* node = m_FreeList[level].front;
10481 node != VMA_NULL;
10482 node = node->free.next)
10483 {
10484 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10485
10486 if(node->free.next == VMA_NULL)
10487 {
10488 VMA_VALIDATE(m_FreeList[level].back == node);
10489 }
10490 else
10491 {
10492 VMA_VALIDATE(node->free.next->free.prev == node);
10493 }
10494 }
10495 }
10496
10497 // Validate that free lists ar higher levels are empty.
10498 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10499 {
10500 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10501 }
10502
10503 return true;
10504 }
10505
GetUnusedRangeSizeMax()10506 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10507 {
10508 for(uint32_t level = 0; level < m_LevelCount; ++level)
10509 {
10510 if(m_FreeList[level].front != VMA_NULL)
10511 {
10512 return LevelToNodeSize(level);
10513 }
10514 }
10515 return 0;
10516 }
10517
CalcAllocationStatInfo(VmaStatInfo & outInfo)10518 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10519 {
10520 const VkDeviceSize unusableSize = GetUnusableSize();
10521
10522 outInfo.blockCount = 1;
10523
10524 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10525 outInfo.usedBytes = outInfo.unusedBytes = 0;
10526
10527 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10528 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10529 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10530
10531 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10532
10533 if(unusableSize > 0)
10534 {
10535 ++outInfo.unusedRangeCount;
10536 outInfo.unusedBytes += unusableSize;
10537 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10538 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10539 }
10540 }
10541
AddPoolStats(VmaPoolStats & inoutStats)10542 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10543 {
10544 const VkDeviceSize unusableSize = GetUnusableSize();
10545
10546 inoutStats.size += GetSize();
10547 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10548 inoutStats.allocationCount += m_AllocationCount;
10549 inoutStats.unusedRangeCount += m_FreeCount;
10550 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10551
10552 if(unusableSize > 0)
10553 {
10554 ++inoutStats.unusedRangeCount;
10555 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10556 }
10557 }
10558
10559 #if VMA_STATS_STRING_ENABLED
10560
PrintDetailedMap(class VmaJsonWriter & json)10561 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10562 {
10563 // TODO optimize
10564 VmaStatInfo stat;
10565 CalcAllocationStatInfo(stat);
10566
10567 PrintDetailedMap_Begin(
10568 json,
10569 stat.unusedBytes,
10570 stat.allocationCount,
10571 stat.unusedRangeCount);
10572
10573 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10574
10575 const VkDeviceSize unusableSize = GetUnusableSize();
10576 if(unusableSize > 0)
10577 {
10578 PrintDetailedMap_UnusedRange(json,
10579 m_UsableSize, // offset
10580 unusableSize); // size
10581 }
10582
10583 PrintDetailedMap_End(json);
10584 }
10585
10586 #endif // #if VMA_STATS_STRING_ENABLED
10587
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10588 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10589 uint32_t currentFrameIndex,
10590 uint32_t frameInUseCount,
10591 VkDeviceSize bufferImageGranularity,
10592 VkDeviceSize allocSize,
10593 VkDeviceSize allocAlignment,
10594 bool upperAddress,
10595 VmaSuballocationType allocType,
10596 bool canMakeOtherLost,
10597 uint32_t strategy,
10598 VmaAllocationRequest* pAllocationRequest)
10599 {
10600 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10601
10602 // Simple way to respect bufferImageGranularity. May be optimized some day.
10603 // Whenever it might be an OPTIMAL image...
10604 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10605 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10606 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10607 {
10608 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10609 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10610 }
10611
10612 if(allocSize > m_UsableSize)
10613 {
10614 return false;
10615 }
10616
10617 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10618 for(uint32_t level = targetLevel + 1; level--; )
10619 {
10620 for(Node* freeNode = m_FreeList[level].front;
10621 freeNode != VMA_NULL;
10622 freeNode = freeNode->free.next)
10623 {
10624 if(freeNode->offset % allocAlignment == 0)
10625 {
10626 pAllocationRequest->offset = freeNode->offset;
10627 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10628 pAllocationRequest->sumItemSize = 0;
10629 pAllocationRequest->itemsToMakeLostCount = 0;
10630 pAllocationRequest->customData = (void*)(uintptr_t)level;
10631 return true;
10632 }
10633 }
10634 }
10635
10636 return false;
10637 }
10638
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)10639 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10640 uint32_t currentFrameIndex,
10641 uint32_t frameInUseCount,
10642 VmaAllocationRequest* pAllocationRequest)
10643 {
10644 /*
10645 Lost allocations are not supported in buddy allocator at the moment.
10646 Support might be added in the future.
10647 */
10648 return pAllocationRequest->itemsToMakeLostCount == 0;
10649 }
10650
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)10651 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10652 {
10653 /*
10654 Lost allocations are not supported in buddy allocator at the moment.
10655 Support might be added in the future.
10656 */
10657 return 0;
10658 }
10659
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,bool upperAddress,VmaAllocation hAllocation)10660 void VmaBlockMetadata_Buddy::Alloc(
10661 const VmaAllocationRequest& request,
10662 VmaSuballocationType type,
10663 VkDeviceSize allocSize,
10664 bool upperAddress,
10665 VmaAllocation hAllocation)
10666 {
10667 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10668 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10669
10670 Node* currNode = m_FreeList[currLevel].front;
10671 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10672 while(currNode->offset != request.offset)
10673 {
10674 currNode = currNode->free.next;
10675 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10676 }
10677
10678 // Go down, splitting free nodes.
10679 while(currLevel < targetLevel)
10680 {
10681 // currNode is already first free node at currLevel.
10682 // Remove it from list of free nodes at this currLevel.
10683 RemoveFromFreeList(currLevel, currNode);
10684
10685 const uint32_t childrenLevel = currLevel + 1;
10686
10687 // Create two free sub-nodes.
10688 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10689 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10690
10691 leftChild->offset = currNode->offset;
10692 leftChild->type = Node::TYPE_FREE;
10693 leftChild->parent = currNode;
10694 leftChild->buddy = rightChild;
10695
10696 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10697 rightChild->type = Node::TYPE_FREE;
10698 rightChild->parent = currNode;
10699 rightChild->buddy = leftChild;
10700
10701 // Convert current currNode to split type.
10702 currNode->type = Node::TYPE_SPLIT;
10703 currNode->split.leftChild = leftChild;
10704
10705 // Add child nodes to free list. Order is important!
10706 AddToFreeListFront(childrenLevel, rightChild);
10707 AddToFreeListFront(childrenLevel, leftChild);
10708
10709 ++m_FreeCount;
10710 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10711 ++currLevel;
10712 currNode = m_FreeList[currLevel].front;
10713
10714 /*
10715 We can be sure that currNode, as left child of node previously split,
10716 also fullfills the alignment requirement.
10717 */
10718 }
10719
10720 // Remove from free list.
10721 VMA_ASSERT(currLevel == targetLevel &&
10722 currNode != VMA_NULL &&
10723 currNode->type == Node::TYPE_FREE);
10724 RemoveFromFreeList(currLevel, currNode);
10725
10726 // Convert to allocation node.
10727 currNode->type = Node::TYPE_ALLOCATION;
10728 currNode->allocation.alloc = hAllocation;
10729
10730 ++m_AllocationCount;
10731 --m_FreeCount;
10732 m_SumFreeSize -= allocSize;
10733 }
10734
DeleteNode(Node * node)10735 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10736 {
10737 if(node->type == Node::TYPE_SPLIT)
10738 {
10739 DeleteNode(node->split.leftChild->buddy);
10740 DeleteNode(node->split.leftChild);
10741 }
10742
10743 vma_delete(GetAllocationCallbacks(), node);
10744 }
10745
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)10746 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10747 {
10748 VMA_VALIDATE(level < m_LevelCount);
10749 VMA_VALIDATE(curr->parent == parent);
10750 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10751 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10752 switch(curr->type)
10753 {
10754 case Node::TYPE_FREE:
10755 // curr->free.prev, next are validated separately.
10756 ctx.calculatedSumFreeSize += levelNodeSize;
10757 ++ctx.calculatedFreeCount;
10758 break;
10759 case Node::TYPE_ALLOCATION:
10760 ++ctx.calculatedAllocationCount;
10761 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10762 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10763 break;
10764 case Node::TYPE_SPLIT:
10765 {
10766 const uint32_t childrenLevel = level + 1;
10767 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10768 const Node* const leftChild = curr->split.leftChild;
10769 VMA_VALIDATE(leftChild != VMA_NULL);
10770 VMA_VALIDATE(leftChild->offset == curr->offset);
10771 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10772 {
10773 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10774 }
10775 const Node* const rightChild = leftChild->buddy;
10776 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10777 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10778 {
10779 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10780 }
10781 }
10782 break;
10783 default:
10784 return false;
10785 }
10786
10787 return true;
10788 }
10789
AllocSizeToLevel(VkDeviceSize allocSize)10790 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10791 {
10792 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10793 uint32_t level = 0;
10794 VkDeviceSize currLevelNodeSize = m_UsableSize;
10795 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10796 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10797 {
10798 ++level;
10799 currLevelNodeSize = nextLevelNodeSize;
10800 nextLevelNodeSize = currLevelNodeSize >> 1;
10801 }
10802 return level;
10803 }
10804
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)10805 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10806 {
10807 // Find node and level.
10808 Node* node = m_Root;
10809 VkDeviceSize nodeOffset = 0;
10810 uint32_t level = 0;
10811 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10812 while(node->type == Node::TYPE_SPLIT)
10813 {
10814 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10815 if(offset < nodeOffset + nextLevelSize)
10816 {
10817 node = node->split.leftChild;
10818 }
10819 else
10820 {
10821 node = node->split.leftChild->buddy;
10822 nodeOffset += nextLevelSize;
10823 }
10824 ++level;
10825 levelNodeSize = nextLevelSize;
10826 }
10827
10828 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10829 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10830
10831 ++m_FreeCount;
10832 --m_AllocationCount;
10833 m_SumFreeSize += alloc->GetSize();
10834
10835 node->type = Node::TYPE_FREE;
10836
10837 // Join free nodes if possible.
10838 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10839 {
10840 RemoveFromFreeList(level, node->buddy);
10841 Node* const parent = node->parent;
10842
10843 vma_delete(GetAllocationCallbacks(), node->buddy);
10844 vma_delete(GetAllocationCallbacks(), node);
10845 parent->type = Node::TYPE_FREE;
10846
10847 node = parent;
10848 --level;
10849 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10850 --m_FreeCount;
10851 }
10852
10853 AddToFreeListFront(level, node);
10854 }
10855
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)10856 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10857 {
10858 switch(node->type)
10859 {
10860 case Node::TYPE_FREE:
10861 ++outInfo.unusedRangeCount;
10862 outInfo.unusedBytes += levelNodeSize;
10863 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10864 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10865 break;
10866 case Node::TYPE_ALLOCATION:
10867 {
10868 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10869 ++outInfo.allocationCount;
10870 outInfo.usedBytes += allocSize;
10871 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10872 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10873
10874 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10875 if(unusedRangeSize > 0)
10876 {
10877 ++outInfo.unusedRangeCount;
10878 outInfo.unusedBytes += unusedRangeSize;
10879 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10880 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10881 }
10882 }
10883 break;
10884 case Node::TYPE_SPLIT:
10885 {
10886 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10887 const Node* const leftChild = node->split.leftChild;
10888 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10889 const Node* const rightChild = leftChild->buddy;
10890 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10891 }
10892 break;
10893 default:
10894 VMA_ASSERT(0);
10895 }
10896 }
10897
AddToFreeListFront(uint32_t level,Node * node)10898 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10899 {
10900 VMA_ASSERT(node->type == Node::TYPE_FREE);
10901
10902 // List is empty.
10903 Node* const frontNode = m_FreeList[level].front;
10904 if(frontNode == VMA_NULL)
10905 {
10906 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10907 node->free.prev = node->free.next = VMA_NULL;
10908 m_FreeList[level].front = m_FreeList[level].back = node;
10909 }
10910 else
10911 {
10912 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10913 node->free.prev = VMA_NULL;
10914 node->free.next = frontNode;
10915 frontNode->free.prev = node;
10916 m_FreeList[level].front = node;
10917 }
10918 }
10919
RemoveFromFreeList(uint32_t level,Node * node)10920 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10921 {
10922 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10923
10924 // It is at the front.
10925 if(node->free.prev == VMA_NULL)
10926 {
10927 VMA_ASSERT(m_FreeList[level].front == node);
10928 m_FreeList[level].front = node->free.next;
10929 }
10930 else
10931 {
10932 Node* const prevFreeNode = node->free.prev;
10933 VMA_ASSERT(prevFreeNode->free.next == node);
10934 prevFreeNode->free.next = node->free.next;
10935 }
10936
10937 // It is at the back.
10938 if(node->free.next == VMA_NULL)
10939 {
10940 VMA_ASSERT(m_FreeList[level].back == node);
10941 m_FreeList[level].back = node->free.prev;
10942 }
10943 else
10944 {
10945 Node* const nextFreeNode = node->free.next;
10946 VMA_ASSERT(nextFreeNode->free.prev == node);
10947 nextFreeNode->free.prev = node->free.prev;
10948 }
10949 }
10950
10951 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)10952 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10953 {
10954 switch(node->type)
10955 {
10956 case Node::TYPE_FREE:
10957 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10958 break;
10959 case Node::TYPE_ALLOCATION:
10960 {
10961 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10962 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10963 if(allocSize < levelNodeSize)
10964 {
10965 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10966 }
10967 }
10968 break;
10969 case Node::TYPE_SPLIT:
10970 {
10971 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10972 const Node* const leftChild = node->split.leftChild;
10973 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10974 const Node* const rightChild = leftChild->buddy;
10975 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10976 }
10977 break;
10978 default:
10979 VMA_ASSERT(0);
10980 }
10981 }
10982 #endif // #if VMA_STATS_STRING_ENABLED
10983
10984
10985 ////////////////////////////////////////////////////////////////////////////////
10986 // class VmaDeviceMemoryBlock
10987
VmaDeviceMemoryBlock(VmaAllocator hAllocator)10988 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
10989 m_pMetadata(VMA_NULL),
10990 m_MemoryTypeIndex(UINT32_MAX),
10991 m_Id(0),
10992 m_hMemory(VK_NULL_HANDLE),
10993 m_MapCount(0),
10994 m_pMappedData(VMA_NULL)
10995 {
10996 }
10997
Init(VmaAllocator hAllocator,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)10998 void VmaDeviceMemoryBlock::Init(
10999 VmaAllocator hAllocator,
11000 uint32_t newMemoryTypeIndex,
11001 VkDeviceMemory newMemory,
11002 VkDeviceSize newSize,
11003 uint32_t id,
11004 uint32_t algorithm)
11005 {
11006 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11007
11008 m_MemoryTypeIndex = newMemoryTypeIndex;
11009 m_Id = id;
11010 m_hMemory = newMemory;
11011
11012 switch(algorithm)
11013 {
11014 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11015 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
11016 break;
11017 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11018 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
11019 break;
11020 default:
11021 VMA_ASSERT(0);
11022 // Fall-through.
11023 case 0:
11024 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
11025 }
11026 m_pMetadata->Init(newSize);
11027 }
11028
Destroy(VmaAllocator allocator)11029 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11030 {
11031 // This is the most important assert in the entire library.
11032 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11033 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11034
11035 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11036 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11037 m_hMemory = VK_NULL_HANDLE;
11038
11039 vma_delete(allocator, m_pMetadata);
11040 m_pMetadata = VMA_NULL;
11041 }
11042
Validate()11043 bool VmaDeviceMemoryBlock::Validate() const
11044 {
11045 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11046 (m_pMetadata->GetSize() != 0));
11047
11048 return m_pMetadata->Validate();
11049 }
11050
CheckCorruption(VmaAllocator hAllocator)11051 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11052 {
11053 void* pData = nullptr;
11054 VkResult res = Map(hAllocator, 1, &pData);
11055 if(res != VK_SUCCESS)
11056 {
11057 return res;
11058 }
11059
11060 res = m_pMetadata->CheckCorruption(pData);
11061
11062 Unmap(hAllocator, 1);
11063
11064 return res;
11065 }
11066
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11067 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11068 {
11069 if(count == 0)
11070 {
11071 return VK_SUCCESS;
11072 }
11073
11074 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11075 if(m_MapCount != 0)
11076 {
11077 m_MapCount += count;
11078 VMA_ASSERT(m_pMappedData != VMA_NULL);
11079 if(ppData != VMA_NULL)
11080 {
11081 *ppData = m_pMappedData;
11082 }
11083 return VK_SUCCESS;
11084 }
11085 else
11086 {
11087 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11088 hAllocator->m_hDevice,
11089 m_hMemory,
11090 0, // offset
11091 VK_WHOLE_SIZE,
11092 0, // flags
11093 &m_pMappedData);
11094 if(result == VK_SUCCESS)
11095 {
11096 if(ppData != VMA_NULL)
11097 {
11098 *ppData = m_pMappedData;
11099 }
11100 m_MapCount = count;
11101 }
11102 return result;
11103 }
11104 }
11105
Unmap(VmaAllocator hAllocator,uint32_t count)11106 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11107 {
11108 if(count == 0)
11109 {
11110 return;
11111 }
11112
11113 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11114 if(m_MapCount >= count)
11115 {
11116 m_MapCount -= count;
11117 if(m_MapCount == 0)
11118 {
11119 m_pMappedData = VMA_NULL;
11120 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11121 }
11122 }
11123 else
11124 {
11125 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11126 }
11127 }
11128
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11129 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11130 {
11131 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11132 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11133
11134 void* pData;
11135 VkResult res = Map(hAllocator, 1, &pData);
11136 if(res != VK_SUCCESS)
11137 {
11138 return res;
11139 }
11140
11141 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11142 VmaWriteMagicValue(pData, allocOffset + allocSize);
11143
11144 Unmap(hAllocator, 1);
11145
11146 return VK_SUCCESS;
11147 }
11148
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11149 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11150 {
11151 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11152 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11153
11154 void* pData;
11155 VkResult res = Map(hAllocator, 1, &pData);
11156 if(res != VK_SUCCESS)
11157 {
11158 return res;
11159 }
11160
11161 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11162 {
11163 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11164 }
11165 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11166 {
11167 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11168 }
11169
11170 Unmap(hAllocator, 1);
11171
11172 return VK_SUCCESS;
11173 }
11174
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkBuffer hBuffer)11175 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11176 const VmaAllocator hAllocator,
11177 const VmaAllocation hAllocation,
11178 VkBuffer hBuffer)
11179 {
11180 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11181 hAllocation->GetBlock() == this);
11182 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11183 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11184 return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11185 hAllocator->m_hDevice,
11186 hBuffer,
11187 m_hMemory,
11188 hAllocation->GetOffset());
11189 }
11190
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkImage hImage)11191 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11192 const VmaAllocator hAllocator,
11193 const VmaAllocation hAllocation,
11194 VkImage hImage)
11195 {
11196 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11197 hAllocation->GetBlock() == this);
11198 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11199 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11200 return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11201 hAllocator->m_hDevice,
11202 hImage,
11203 m_hMemory,
11204 hAllocation->GetOffset());
11205 }
11206
InitStatInfo(VmaStatInfo & outInfo)11207 static void InitStatInfo(VmaStatInfo& outInfo)
11208 {
11209 memset(&outInfo, 0, sizeof(outInfo));
11210 outInfo.allocationSizeMin = UINT64_MAX;
11211 outInfo.unusedRangeSizeMin = UINT64_MAX;
11212 }
11213
11214 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)11215 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11216 {
11217 inoutInfo.blockCount += srcInfo.blockCount;
11218 inoutInfo.allocationCount += srcInfo.allocationCount;
11219 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11220 inoutInfo.usedBytes += srcInfo.usedBytes;
11221 inoutInfo.unusedBytes += srcInfo.unusedBytes;
11222 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11223 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11224 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11225 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11226 }
11227
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)11228 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11229 {
11230 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11231 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11232 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11233 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11234 }
11235
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)11236 VmaPool_T::VmaPool_T(
11237 VmaAllocator hAllocator,
11238 const VmaPoolCreateInfo& createInfo,
11239 VkDeviceSize preferredBlockSize) :
11240 m_BlockVector(
11241 hAllocator,
11242 createInfo.memoryTypeIndex,
11243 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11244 createInfo.minBlockCount,
11245 createInfo.maxBlockCount,
11246 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11247 createInfo.frameInUseCount,
11248 true, // isCustomPool
11249 createInfo.blockSize != 0, // explicitBlockSize
11250 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11251 m_Id(0)
11252 {
11253 }
11254
~VmaPool_T()11255 VmaPool_T::~VmaPool_T()
11256 {
11257 }
11258
11259 #if VMA_STATS_STRING_ENABLED
11260
11261 #endif // #if VMA_STATS_STRING_ENABLED
11262
VmaBlockVector(VmaAllocator hAllocator,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool isCustomPool,bool explicitBlockSize,uint32_t algorithm)11263 VmaBlockVector::VmaBlockVector(
11264 VmaAllocator hAllocator,
11265 uint32_t memoryTypeIndex,
11266 VkDeviceSize preferredBlockSize,
11267 size_t minBlockCount,
11268 size_t maxBlockCount,
11269 VkDeviceSize bufferImageGranularity,
11270 uint32_t frameInUseCount,
11271 bool isCustomPool,
11272 bool explicitBlockSize,
11273 uint32_t algorithm) :
11274 m_hAllocator(hAllocator),
11275 m_MemoryTypeIndex(memoryTypeIndex),
11276 m_PreferredBlockSize(preferredBlockSize),
11277 m_MinBlockCount(minBlockCount),
11278 m_MaxBlockCount(maxBlockCount),
11279 m_BufferImageGranularity(bufferImageGranularity),
11280 m_FrameInUseCount(frameInUseCount),
11281 m_IsCustomPool(isCustomPool),
11282 m_ExplicitBlockSize(explicitBlockSize),
11283 m_Algorithm(algorithm),
11284 m_HasEmptyBlock(false),
11285 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11286 m_NextBlockId(0)
11287 {
11288 }
11289
~VmaBlockVector()11290 VmaBlockVector::~VmaBlockVector()
11291 {
11292 for(size_t i = m_Blocks.size(); i--; )
11293 {
11294 m_Blocks[i]->Destroy(m_hAllocator);
11295 vma_delete(m_hAllocator, m_Blocks[i]);
11296 }
11297 }
11298
CreateMinBlocks()11299 VkResult VmaBlockVector::CreateMinBlocks()
11300 {
11301 for(size_t i = 0; i < m_MinBlockCount; ++i)
11302 {
11303 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11304 if(res != VK_SUCCESS)
11305 {
11306 return res;
11307 }
11308 }
11309 return VK_SUCCESS;
11310 }
11311
GetPoolStats(VmaPoolStats * pStats)11312 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11313 {
11314 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11315
11316 const size_t blockCount = m_Blocks.size();
11317
11318 pStats->size = 0;
11319 pStats->unusedSize = 0;
11320 pStats->allocationCount = 0;
11321 pStats->unusedRangeCount = 0;
11322 pStats->unusedRangeSizeMax = 0;
11323 pStats->blockCount = blockCount;
11324
11325 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11326 {
11327 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11328 VMA_ASSERT(pBlock);
11329 VMA_HEAVY_ASSERT(pBlock->Validate());
11330 pBlock->m_pMetadata->AddPoolStats(*pStats);
11331 }
11332 }
11333
IsCorruptionDetectionEnabled()11334 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11335 {
11336 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11337 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11338 (VMA_DEBUG_MARGIN > 0) &&
11339 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11340 }
11341
11342 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11343
Allocate(VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)11344 VkResult VmaBlockVector::Allocate(
11345 VmaPool hCurrentPool,
11346 uint32_t currentFrameIndex,
11347 VkDeviceSize size,
11348 VkDeviceSize alignment,
11349 const VmaAllocationCreateInfo& createInfo,
11350 VmaSuballocationType suballocType,
11351 size_t allocationCount,
11352 VmaAllocation* pAllocations)
11353 {
11354 size_t allocIndex;
11355 VkResult res = VK_SUCCESS;
11356
11357 {
11358 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11359 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11360 {
11361 res = AllocatePage(
11362 hCurrentPool,
11363 currentFrameIndex,
11364 size,
11365 alignment,
11366 createInfo,
11367 suballocType,
11368 pAllocations + allocIndex);
11369 if(res != VK_SUCCESS)
11370 {
11371 break;
11372 }
11373 }
11374 }
11375
11376 if(res != VK_SUCCESS)
11377 {
11378 // Free all already created allocations.
11379 while(allocIndex--)
11380 {
11381 Free(pAllocations[allocIndex]);
11382 }
11383 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11384 }
11385
11386 return res;
11387 }
11388
AllocatePage(VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)11389 VkResult VmaBlockVector::AllocatePage(
11390 VmaPool hCurrentPool,
11391 uint32_t currentFrameIndex,
11392 VkDeviceSize size,
11393 VkDeviceSize alignment,
11394 const VmaAllocationCreateInfo& createInfo,
11395 VmaSuballocationType suballocType,
11396 VmaAllocation* pAllocation)
11397 {
11398 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11399 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11400 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11401 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11402 const bool canCreateNewBlock =
11403 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11404 (m_Blocks.size() < m_MaxBlockCount);
11405 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11406
11407 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11408 // Which in turn is available only when maxBlockCount = 1.
11409 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11410 {
11411 canMakeOtherLost = false;
11412 }
11413
11414 // Upper address can only be used with linear allocator and within single memory block.
11415 if(isUpperAddress &&
11416 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11417 {
11418 return VK_ERROR_FEATURE_NOT_PRESENT;
11419 }
11420
11421 // Validate strategy.
11422 switch(strategy)
11423 {
11424 case 0:
11425 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11426 break;
11427 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11428 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11429 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11430 break;
11431 default:
11432 return VK_ERROR_FEATURE_NOT_PRESENT;
11433 }
11434
11435 // Early reject: requested allocation size is larger that maximum block size for this block vector.
11436 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11437 {
11438 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11439 }
11440
11441 /*
11442 Under certain condition, this whole section can be skipped for optimization, so
11443 we move on directly to trying to allocate with canMakeOtherLost. That's the case
11444 e.g. for custom pools with linear algorithm.
11445 */
11446 if(!canMakeOtherLost || canCreateNewBlock)
11447 {
11448 // 1. Search existing allocations. Try to allocate without making other allocations lost.
11449 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11450 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11451
11452 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11453 {
11454 // Use only last block.
11455 if(!m_Blocks.empty())
11456 {
11457 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11458 VMA_ASSERT(pCurrBlock);
11459 VkResult res = AllocateFromBlock(
11460 pCurrBlock,
11461 hCurrentPool,
11462 currentFrameIndex,
11463 size,
11464 alignment,
11465 allocFlagsCopy,
11466 createInfo.pUserData,
11467 suballocType,
11468 strategy,
11469 pAllocation);
11470 if(res == VK_SUCCESS)
11471 {
11472 VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11473 return VK_SUCCESS;
11474 }
11475 }
11476 }
11477 else
11478 {
11479 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11480 {
11481 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11482 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11483 {
11484 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11485 VMA_ASSERT(pCurrBlock);
11486 VkResult res = AllocateFromBlock(
11487 pCurrBlock,
11488 hCurrentPool,
11489 currentFrameIndex,
11490 size,
11491 alignment,
11492 allocFlagsCopy,
11493 createInfo.pUserData,
11494 suballocType,
11495 strategy,
11496 pAllocation);
11497 if(res == VK_SUCCESS)
11498 {
11499 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11500 return VK_SUCCESS;
11501 }
11502 }
11503 }
11504 else // WORST_FIT, FIRST_FIT
11505 {
11506 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11507 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11508 {
11509 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11510 VMA_ASSERT(pCurrBlock);
11511 VkResult res = AllocateFromBlock(
11512 pCurrBlock,
11513 hCurrentPool,
11514 currentFrameIndex,
11515 size,
11516 alignment,
11517 allocFlagsCopy,
11518 createInfo.pUserData,
11519 suballocType,
11520 strategy,
11521 pAllocation);
11522 if(res == VK_SUCCESS)
11523 {
11524 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11525 return VK_SUCCESS;
11526 }
11527 }
11528 }
11529 }
11530
11531 // 2. Try to create new block.
11532 if(canCreateNewBlock)
11533 {
11534 // Calculate optimal size for new block.
11535 VkDeviceSize newBlockSize = m_PreferredBlockSize;
11536 uint32_t newBlockSizeShift = 0;
11537 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11538
11539 if(!m_ExplicitBlockSize)
11540 {
11541 // Allocate 1/8, 1/4, 1/2 as first blocks.
11542 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11543 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11544 {
11545 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11546 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11547 {
11548 newBlockSize = smallerNewBlockSize;
11549 ++newBlockSizeShift;
11550 }
11551 else
11552 {
11553 break;
11554 }
11555 }
11556 }
11557
11558 size_t newBlockIndex = 0;
11559 VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11560 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11561 if(!m_ExplicitBlockSize)
11562 {
11563 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11564 {
11565 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11566 if(smallerNewBlockSize >= size)
11567 {
11568 newBlockSize = smallerNewBlockSize;
11569 ++newBlockSizeShift;
11570 res = CreateBlock(newBlockSize, &newBlockIndex);
11571 }
11572 else
11573 {
11574 break;
11575 }
11576 }
11577 }
11578
11579 if(res == VK_SUCCESS)
11580 {
11581 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11582 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11583
11584 res = AllocateFromBlock(
11585 pBlock,
11586 hCurrentPool,
11587 currentFrameIndex,
11588 size,
11589 alignment,
11590 allocFlagsCopy,
11591 createInfo.pUserData,
11592 suballocType,
11593 strategy,
11594 pAllocation);
11595 if(res == VK_SUCCESS)
11596 {
11597 VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
11598 return VK_SUCCESS;
11599 }
11600 else
11601 {
11602 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11603 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11604 }
11605 }
11606 }
11607 }
11608
11609 // 3. Try to allocate from existing blocks with making other allocations lost.
11610 if(canMakeOtherLost)
11611 {
11612 uint32_t tryIndex = 0;
11613 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11614 {
11615 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11616 VmaAllocationRequest bestRequest = {};
11617 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11618
11619 // 1. Search existing allocations.
11620 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11621 {
11622 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11623 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11624 {
11625 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11626 VMA_ASSERT(pCurrBlock);
11627 VmaAllocationRequest currRequest = {};
11628 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11629 currentFrameIndex,
11630 m_FrameInUseCount,
11631 m_BufferImageGranularity,
11632 size,
11633 alignment,
11634 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11635 suballocType,
11636 canMakeOtherLost,
11637 strategy,
11638 &currRequest))
11639 {
11640 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11641 if(pBestRequestBlock == VMA_NULL ||
11642 currRequestCost < bestRequestCost)
11643 {
11644 pBestRequestBlock = pCurrBlock;
11645 bestRequest = currRequest;
11646 bestRequestCost = currRequestCost;
11647
11648 if(bestRequestCost == 0)
11649 {
11650 break;
11651 }
11652 }
11653 }
11654 }
11655 }
11656 else // WORST_FIT, FIRST_FIT
11657 {
11658 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11659 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11660 {
11661 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11662 VMA_ASSERT(pCurrBlock);
11663 VmaAllocationRequest currRequest = {};
11664 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11665 currentFrameIndex,
11666 m_FrameInUseCount,
11667 m_BufferImageGranularity,
11668 size,
11669 alignment,
11670 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11671 suballocType,
11672 canMakeOtherLost,
11673 strategy,
11674 &currRequest))
11675 {
11676 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11677 if(pBestRequestBlock == VMA_NULL ||
11678 currRequestCost < bestRequestCost ||
11679 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11680 {
11681 pBestRequestBlock = pCurrBlock;
11682 bestRequest = currRequest;
11683 bestRequestCost = currRequestCost;
11684
11685 if(bestRequestCost == 0 ||
11686 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11687 {
11688 break;
11689 }
11690 }
11691 }
11692 }
11693 }
11694
11695 if(pBestRequestBlock != VMA_NULL)
11696 {
11697 if(mapped)
11698 {
11699 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11700 if(res != VK_SUCCESS)
11701 {
11702 return res;
11703 }
11704 }
11705
11706 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11707 currentFrameIndex,
11708 m_FrameInUseCount,
11709 &bestRequest))
11710 {
11711 // We no longer have an empty Allocation.
11712 if(pBestRequestBlock->m_pMetadata->IsEmpty())
11713 {
11714 m_HasEmptyBlock = false;
11715 }
11716 // Allocate from this pBlock.
11717 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11718 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11719 (*pAllocation)->InitBlockAllocation(
11720 hCurrentPool,
11721 pBestRequestBlock,
11722 bestRequest.offset,
11723 alignment,
11724 size,
11725 suballocType,
11726 mapped,
11727 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11728 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11729 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
11730 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11731 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11732 {
11733 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11734 }
11735 if(IsCorruptionDetectionEnabled())
11736 {
11737 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11738 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11739 }
11740 return VK_SUCCESS;
11741 }
11742 // else: Some allocations must have been touched while we are here. Next try.
11743 }
11744 else
11745 {
11746 // Could not find place in any of the blocks - break outer loop.
11747 break;
11748 }
11749 }
11750 /* Maximum number of tries exceeded - a very unlike event when many other
11751 threads are simultaneously touching allocations making it impossible to make
11752 lost at the same time as we try to allocate. */
11753 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11754 {
11755 return VK_ERROR_TOO_MANY_OBJECTS;
11756 }
11757 }
11758
11759 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11760 }
11761
Free(VmaAllocation hAllocation)11762 void VmaBlockVector::Free(
11763 VmaAllocation hAllocation)
11764 {
11765 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11766
11767 // Scope for lock.
11768 {
11769 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11770
11771 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11772
11773 if(IsCorruptionDetectionEnabled())
11774 {
11775 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11776 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11777 }
11778
11779 if(hAllocation->IsPersistentMap())
11780 {
11781 pBlock->Unmap(m_hAllocator, 1);
11782 }
11783
11784 pBlock->m_pMetadata->Free(hAllocation);
11785 VMA_HEAVY_ASSERT(pBlock->Validate());
11786
11787 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11788
11789 // pBlock became empty after this deallocation.
11790 if(pBlock->m_pMetadata->IsEmpty())
11791 {
11792 // Already has empty Allocation. We don't want to have two, so delete this one.
11793 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11794 {
11795 pBlockToDelete = pBlock;
11796 Remove(pBlock);
11797 }
11798 // We now have first empty block.
11799 else
11800 {
11801 m_HasEmptyBlock = true;
11802 }
11803 }
11804 // pBlock didn't become empty, but we have another empty block - find and free that one.
11805 // (This is optional, heuristics.)
11806 else if(m_HasEmptyBlock)
11807 {
11808 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11809 if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11810 {
11811 pBlockToDelete = pLastBlock;
11812 m_Blocks.pop_back();
11813 m_HasEmptyBlock = false;
11814 }
11815 }
11816
11817 IncrementallySortBlocks();
11818 }
11819
11820 // Destruction of a free Allocation. Deferred until this point, outside of mutex
11821 // lock, for performance reason.
11822 if(pBlockToDelete != VMA_NULL)
11823 {
11824 VMA_DEBUG_LOG(" Deleted empty allocation");
11825 pBlockToDelete->Destroy(m_hAllocator);
11826 vma_delete(m_hAllocator, pBlockToDelete);
11827 }
11828 }
11829
CalcMaxBlockSize()11830 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11831 {
11832 VkDeviceSize result = 0;
11833 for(size_t i = m_Blocks.size(); i--; )
11834 {
11835 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11836 if(result >= m_PreferredBlockSize)
11837 {
11838 break;
11839 }
11840 }
11841 return result;
11842 }
11843
Remove(VmaDeviceMemoryBlock * pBlock)11844 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11845 {
11846 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11847 {
11848 if(m_Blocks[blockIndex] == pBlock)
11849 {
11850 VmaVectorRemove(m_Blocks, blockIndex);
11851 return;
11852 }
11853 }
11854 VMA_ASSERT(0);
11855 }
11856
IncrementallySortBlocks()11857 void VmaBlockVector::IncrementallySortBlocks()
11858 {
11859 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11860 {
11861 // Bubble sort only until first swap.
11862 for(size_t i = 1; i < m_Blocks.size(); ++i)
11863 {
11864 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11865 {
11866 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11867 return;
11868 }
11869 }
11870 }
11871 }
11872
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,VmaPool hCurrentPool,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)11873 VkResult VmaBlockVector::AllocateFromBlock(
11874 VmaDeviceMemoryBlock* pBlock,
11875 VmaPool hCurrentPool,
11876 uint32_t currentFrameIndex,
11877 VkDeviceSize size,
11878 VkDeviceSize alignment,
11879 VmaAllocationCreateFlags allocFlags,
11880 void* pUserData,
11881 VmaSuballocationType suballocType,
11882 uint32_t strategy,
11883 VmaAllocation* pAllocation)
11884 {
11885 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11886 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11887 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11888 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11889
11890 VmaAllocationRequest currRequest = {};
11891 if(pBlock->m_pMetadata->CreateAllocationRequest(
11892 currentFrameIndex,
11893 m_FrameInUseCount,
11894 m_BufferImageGranularity,
11895 size,
11896 alignment,
11897 isUpperAddress,
11898 suballocType,
11899 false, // canMakeOtherLost
11900 strategy,
11901 &currRequest))
11902 {
11903 // Allocate from pCurrBlock.
11904 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11905
11906 if(mapped)
11907 {
11908 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11909 if(res != VK_SUCCESS)
11910 {
11911 return res;
11912 }
11913 }
11914
11915 // We no longer have an empty Allocation.
11916 if(pBlock->m_pMetadata->IsEmpty())
11917 {
11918 m_HasEmptyBlock = false;
11919 }
11920
11921 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11922 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11923 (*pAllocation)->InitBlockAllocation(
11924 hCurrentPool,
11925 pBlock,
11926 currRequest.offset,
11927 alignment,
11928 size,
11929 suballocType,
11930 mapped,
11931 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11932 VMA_HEAVY_ASSERT(pBlock->Validate());
11933 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11934 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11935 {
11936 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11937 }
11938 if(IsCorruptionDetectionEnabled())
11939 {
11940 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11941 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11942 }
11943 return VK_SUCCESS;
11944 }
11945 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11946 }
11947
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)11948 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11949 {
11950 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11951 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11952 allocInfo.allocationSize = blockSize;
11953 VkDeviceMemory mem = VK_NULL_HANDLE;
11954 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11955 if(res < 0)
11956 {
11957 return res;
11958 }
11959
11960 // New VkDeviceMemory successfully created.
11961
11962 // Create new Allocation for it.
11963 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11964 pBlock->Init(
11965 m_hAllocator,
11966 m_MemoryTypeIndex,
11967 mem,
11968 allocInfo.allocationSize,
11969 m_NextBlockId++,
11970 m_Algorithm);
11971
11972 m_Blocks.push_back(pBlock);
11973 if(pNewBlockIndex != VMA_NULL)
11974 {
11975 *pNewBlockIndex = m_Blocks.size() - 1;
11976 }
11977
11978 return VK_SUCCESS;
11979 }
11980
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)11981 void VmaBlockVector::ApplyDefragmentationMovesCpu(
11982 class VmaBlockVectorDefragmentationContext* pDefragCtx,
11983 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11984 {
11985 const size_t blockCount = m_Blocks.size();
11986 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11987
11988 enum BLOCK_FLAG
11989 {
11990 BLOCK_FLAG_USED = 0x00000001,
11991 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11992 };
11993
11994 struct BlockInfo
11995 {
11996 uint32_t flags;
11997 void* pMappedData;
11998 };
11999 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
12000 blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
12001 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
12002
12003 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12004 const size_t moveCount = moves.size();
12005 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12006 {
12007 const VmaDefragmentationMove& move = moves[moveIndex];
12008 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
12009 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
12010 }
12011
12012 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12013
12014 // Go over all blocks. Get mapped pointer or map if necessary.
12015 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12016 {
12017 BlockInfo& currBlockInfo = blockInfo[blockIndex];
12018 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12019 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12020 {
12021 currBlockInfo.pMappedData = pBlock->GetMappedData();
12022 // It is not originally mapped - map it.
12023 if(currBlockInfo.pMappedData == VMA_NULL)
12024 {
12025 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12026 if(pDefragCtx->res == VK_SUCCESS)
12027 {
12028 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12029 }
12030 }
12031 }
12032 }
12033
12034 // Go over all moves. Do actual data transfer.
12035 if(pDefragCtx->res == VK_SUCCESS)
12036 {
12037 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12038 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12039
12040 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12041 {
12042 const VmaDefragmentationMove& move = moves[moveIndex];
12043
12044 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12045 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12046
12047 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12048
12049 // Invalidate source.
12050 if(isNonCoherent)
12051 {
12052 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12053 memRange.memory = pSrcBlock->GetDeviceMemory();
12054 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12055 memRange.size = VMA_MIN(
12056 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12057 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12058 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12059 }
12060
12061 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12062 memmove(
12063 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12064 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12065 static_cast<size_t>(move.size));
12066
12067 if(IsCorruptionDetectionEnabled())
12068 {
12069 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12070 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12071 }
12072
12073 // Flush destination.
12074 if(isNonCoherent)
12075 {
12076 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12077 memRange.memory = pDstBlock->GetDeviceMemory();
12078 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12079 memRange.size = VMA_MIN(
12080 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12081 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12082 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12083 }
12084 }
12085 }
12086
12087 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12088 // Regardless of pCtx->res == VK_SUCCESS.
12089 for(size_t blockIndex = blockCount; blockIndex--; )
12090 {
12091 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12092 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12093 {
12094 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12095 pBlock->Unmap(m_hAllocator, 1);
12096 }
12097 }
12098 }
12099
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)12100 void VmaBlockVector::ApplyDefragmentationMovesGpu(
12101 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12102 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12103 VkCommandBuffer commandBuffer)
12104 {
12105 const size_t blockCount = m_Blocks.size();
12106
12107 pDefragCtx->blockContexts.resize(blockCount);
12108 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12109
12110 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12111 const size_t moveCount = moves.size();
12112 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12113 {
12114 const VmaDefragmentationMove& move = moves[moveIndex];
12115 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12116 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12117 }
12118
12119 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12120
12121 // Go over all blocks. Create and bind buffer for whole block if necessary.
12122 {
12123 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12124 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12125 VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12126
12127 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12128 {
12129 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12130 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12131 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12132 {
12133 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12134 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12135 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12136 if(pDefragCtx->res == VK_SUCCESS)
12137 {
12138 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12139 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12140 }
12141 }
12142 }
12143 }
12144
12145 // Go over all moves. Post data transfer commands to command buffer.
12146 if(pDefragCtx->res == VK_SUCCESS)
12147 {
12148 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12149 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12150
12151 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12152 {
12153 const VmaDefragmentationMove& move = moves[moveIndex];
12154
12155 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12156 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12157
12158 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12159
12160 VkBufferCopy region = {
12161 move.srcOffset,
12162 move.dstOffset,
12163 move.size };
12164 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12165 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion);
12166 }
12167 }
12168
12169 // Save buffers to defrag context for later destruction.
12170 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12171 {
12172 pDefragCtx->res = VK_NOT_READY;
12173 }
12174 }
12175
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)12176 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12177 {
12178 m_HasEmptyBlock = false;
12179 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12180 {
12181 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12182 if(pBlock->m_pMetadata->IsEmpty())
12183 {
12184 if(m_Blocks.size() > m_MinBlockCount)
12185 {
12186 if(pDefragmentationStats != VMA_NULL)
12187 {
12188 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12189 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12190 }
12191
12192 VmaVectorRemove(m_Blocks, blockIndex);
12193 pBlock->Destroy(m_hAllocator);
12194 vma_delete(m_hAllocator, pBlock);
12195 }
12196 else
12197 {
12198 m_HasEmptyBlock = true;
12199 }
12200 }
12201 }
12202 }
12203
12204 #if VMA_STATS_STRING_ENABLED
12205
PrintDetailedMap(class VmaJsonWriter & json)12206 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12207 {
12208 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12209
12210 json.BeginObject();
12211
12212 if(m_IsCustomPool)
12213 {
12214 json.WriteString("MemoryTypeIndex");
12215 json.WriteNumber(m_MemoryTypeIndex);
12216
12217 json.WriteString("BlockSize");
12218 json.WriteNumber(m_PreferredBlockSize);
12219
12220 json.WriteString("BlockCount");
12221 json.BeginObject(true);
12222 if(m_MinBlockCount > 0)
12223 {
12224 json.WriteString("Min");
12225 json.WriteNumber((uint64_t)m_MinBlockCount);
12226 }
12227 if(m_MaxBlockCount < SIZE_MAX)
12228 {
12229 json.WriteString("Max");
12230 json.WriteNumber((uint64_t)m_MaxBlockCount);
12231 }
12232 json.WriteString("Cur");
12233 json.WriteNumber((uint64_t)m_Blocks.size());
12234 json.EndObject();
12235
12236 if(m_FrameInUseCount > 0)
12237 {
12238 json.WriteString("FrameInUseCount");
12239 json.WriteNumber(m_FrameInUseCount);
12240 }
12241
12242 if(m_Algorithm != 0)
12243 {
12244 json.WriteString("Algorithm");
12245 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12246 }
12247 }
12248 else
12249 {
12250 json.WriteString("PreferredBlockSize");
12251 json.WriteNumber(m_PreferredBlockSize);
12252 }
12253
12254 json.WriteString("Blocks");
12255 json.BeginObject();
12256 for(size_t i = 0; i < m_Blocks.size(); ++i)
12257 {
12258 json.BeginString();
12259 json.ContinueString(m_Blocks[i]->GetId());
12260 json.EndString();
12261
12262 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12263 }
12264 json.EndObject();
12265
12266 json.EndObject();
12267 }
12268
12269 #endif // #if VMA_STATS_STRING_ENABLED
12270
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)12271 void VmaBlockVector::Defragment(
12272 class VmaBlockVectorDefragmentationContext* pCtx,
12273 VmaDefragmentationStats* pStats,
12274 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12275 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12276 VkCommandBuffer commandBuffer)
12277 {
12278 pCtx->res = VK_SUCCESS;
12279
12280 const VkMemoryPropertyFlags memPropFlags =
12281 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12282 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12283 const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12284
12285 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12286 isHostVisible;
12287 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12288 (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12289
12290 // There are options to defragment this memory type.
12291 if(canDefragmentOnCpu || canDefragmentOnGpu)
12292 {
12293 bool defragmentOnGpu;
12294 // There is only one option to defragment this memory type.
12295 if(canDefragmentOnGpu != canDefragmentOnCpu)
12296 {
12297 defragmentOnGpu = canDefragmentOnGpu;
12298 }
12299 // Both options are available: Heuristics to choose the best one.
12300 else
12301 {
12302 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12303 m_hAllocator->IsIntegratedGpu();
12304 }
12305
12306 bool overlappingMoveSupported = !defragmentOnGpu;
12307
12308 if(m_hAllocator->m_UseMutex)
12309 {
12310 m_Mutex.LockWrite();
12311 pCtx->mutexLocked = true;
12312 }
12313
12314 pCtx->Begin(overlappingMoveSupported);
12315
12316 // Defragment.
12317
12318 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12319 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12320 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12321 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12322 pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12323
12324 // Accumulate statistics.
12325 if(pStats != VMA_NULL)
12326 {
12327 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12328 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12329 pStats->bytesMoved += bytesMoved;
12330 pStats->allocationsMoved += allocationsMoved;
12331 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12332 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12333 if(defragmentOnGpu)
12334 {
12335 maxGpuBytesToMove -= bytesMoved;
12336 maxGpuAllocationsToMove -= allocationsMoved;
12337 }
12338 else
12339 {
12340 maxCpuBytesToMove -= bytesMoved;
12341 maxCpuAllocationsToMove -= allocationsMoved;
12342 }
12343 }
12344
12345 if(pCtx->res >= VK_SUCCESS)
12346 {
12347 if(defragmentOnGpu)
12348 {
12349 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12350 }
12351 else
12352 {
12353 ApplyDefragmentationMovesCpu(pCtx, moves);
12354 }
12355 }
12356 }
12357 }
12358
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)12359 void VmaBlockVector::DefragmentationEnd(
12360 class VmaBlockVectorDefragmentationContext* pCtx,
12361 VmaDefragmentationStats* pStats)
12362 {
12363 // Destroy buffers.
12364 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12365 {
12366 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12367 if(blockCtx.hBuffer)
12368 {
12369 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12370 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12371 }
12372 }
12373
12374 if(pCtx->res >= VK_SUCCESS)
12375 {
12376 FreeEmptyBlocks(pStats);
12377 }
12378
12379 if(pCtx->mutexLocked)
12380 {
12381 VMA_ASSERT(m_hAllocator->m_UseMutex);
12382 m_Mutex.UnlockWrite();
12383 }
12384 }
12385
CalcAllocationCount()12386 size_t VmaBlockVector::CalcAllocationCount() const
12387 {
12388 size_t result = 0;
12389 for(size_t i = 0; i < m_Blocks.size(); ++i)
12390 {
12391 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12392 }
12393 return result;
12394 }
12395
IsBufferImageGranularityConflictPossible()12396 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12397 {
12398 if(m_BufferImageGranularity == 1)
12399 {
12400 return false;
12401 }
12402 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12403 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12404 {
12405 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12406 VMA_ASSERT(m_Algorithm == 0);
12407 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12408 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12409 {
12410 return true;
12411 }
12412 }
12413 return false;
12414 }
12415
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)12416 void VmaBlockVector::MakePoolAllocationsLost(
12417 uint32_t currentFrameIndex,
12418 size_t* pLostAllocationCount)
12419 {
12420 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12421 size_t lostAllocationCount = 0;
12422 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12423 {
12424 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12425 VMA_ASSERT(pBlock);
12426 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12427 }
12428 if(pLostAllocationCount != VMA_NULL)
12429 {
12430 *pLostAllocationCount = lostAllocationCount;
12431 }
12432 }
12433
CheckCorruption()12434 VkResult VmaBlockVector::CheckCorruption()
12435 {
12436 if(!IsCorruptionDetectionEnabled())
12437 {
12438 return VK_ERROR_FEATURE_NOT_PRESENT;
12439 }
12440
12441 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12442 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12443 {
12444 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12445 VMA_ASSERT(pBlock);
12446 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12447 if(res != VK_SUCCESS)
12448 {
12449 return res;
12450 }
12451 }
12452 return VK_SUCCESS;
12453 }
12454
AddStats(VmaStats * pStats)12455 void VmaBlockVector::AddStats(VmaStats* pStats)
12456 {
12457 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12458 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12459
12460 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12461
12462 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12463 {
12464 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12465 VMA_ASSERT(pBlock);
12466 VMA_HEAVY_ASSERT(pBlock->Validate());
12467 VmaStatInfo allocationStatInfo;
12468 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12469 VmaAddStatInfo(pStats->total, allocationStatInfo);
12470 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12471 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12472 }
12473 }
12474
12475 ////////////////////////////////////////////////////////////////////////////////
12476 // VmaDefragmentationAlgorithm_Generic members definition
12477
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12478 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12479 VmaAllocator hAllocator,
12480 VmaBlockVector* pBlockVector,
12481 uint32_t currentFrameIndex,
12482 bool overlappingMoveSupported) :
12483 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12484 m_AllocationCount(0),
12485 m_AllAllocations(false),
12486 m_BytesMoved(0),
12487 m_AllocationsMoved(0),
12488 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12489 {
12490 // Create block info for each block.
12491 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12492 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12493 {
12494 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12495 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12496 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12497 m_Blocks.push_back(pBlockInfo);
12498 }
12499
12500 // Sort them by m_pBlock pointer value.
12501 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12502 }
12503
~VmaDefragmentationAlgorithm_Generic()12504 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12505 {
12506 for(size_t i = m_Blocks.size(); i--; )
12507 {
12508 vma_delete(m_hAllocator, m_Blocks[i]);
12509 }
12510 }
12511
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)12512 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12513 {
12514 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12515 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12516 {
12517 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12518 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12519 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12520 {
12521 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12522 (*it)->m_Allocations.push_back(allocInfo);
12523 }
12524 else
12525 {
12526 VMA_ASSERT(0);
12527 }
12528
12529 ++m_AllocationCount;
12530 }
12531 }
12532
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12533 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12534 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12535 VkDeviceSize maxBytesToMove,
12536 uint32_t maxAllocationsToMove)
12537 {
12538 if(m_Blocks.empty())
12539 {
12540 return VK_SUCCESS;
12541 }
12542
12543 // This is a choice based on research.
12544 // Option 1:
12545 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12546 // Option 2:
12547 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12548 // Option 3:
12549 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12550
12551 size_t srcBlockMinIndex = 0;
12552 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12553 /*
12554 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12555 {
12556 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12557 if(blocksWithNonMovableCount > 0)
12558 {
12559 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12560 }
12561 }
12562 */
12563
12564 size_t srcBlockIndex = m_Blocks.size() - 1;
12565 size_t srcAllocIndex = SIZE_MAX;
12566 for(;;)
12567 {
12568 // 1. Find next allocation to move.
12569 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12570 // 1.2. Then start from last to first m_Allocations.
12571 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12572 {
12573 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12574 {
12575 // Finished: no more allocations to process.
12576 if(srcBlockIndex == srcBlockMinIndex)
12577 {
12578 return VK_SUCCESS;
12579 }
12580 else
12581 {
12582 --srcBlockIndex;
12583 srcAllocIndex = SIZE_MAX;
12584 }
12585 }
12586 else
12587 {
12588 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12589 }
12590 }
12591
12592 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12593 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12594
12595 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12596 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12597 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12598 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12599
12600 // 2. Try to find new place for this allocation in preceding or current block.
12601 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12602 {
12603 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12604 VmaAllocationRequest dstAllocRequest;
12605 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12606 m_CurrentFrameIndex,
12607 m_pBlockVector->GetFrameInUseCount(),
12608 m_pBlockVector->GetBufferImageGranularity(),
12609 size,
12610 alignment,
12611 false, // upperAddress
12612 suballocType,
12613 false, // canMakeOtherLost
12614 strategy,
12615 &dstAllocRequest) &&
12616 MoveMakesSense(
12617 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12618 {
12619 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12620
12621 // Reached limit on number of allocations or bytes to move.
12622 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12623 (m_BytesMoved + size > maxBytesToMove))
12624 {
12625 return VK_SUCCESS;
12626 }
12627
12628 VmaDefragmentationMove move;
12629 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12630 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12631 move.srcOffset = srcOffset;
12632 move.dstOffset = dstAllocRequest.offset;
12633 move.size = size;
12634 moves.push_back(move);
12635
12636 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12637 dstAllocRequest,
12638 suballocType,
12639 size,
12640 false, // upperAddress
12641 allocInfo.m_hAllocation);
12642 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12643
12644 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12645
12646 if(allocInfo.m_pChanged != VMA_NULL)
12647 {
12648 *allocInfo.m_pChanged = VK_TRUE;
12649 }
12650
12651 ++m_AllocationsMoved;
12652 m_BytesMoved += size;
12653
12654 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12655
12656 break;
12657 }
12658 }
12659
12660 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12661
12662 if(srcAllocIndex > 0)
12663 {
12664 --srcAllocIndex;
12665 }
12666 else
12667 {
12668 if(srcBlockIndex > 0)
12669 {
12670 --srcBlockIndex;
12671 srcAllocIndex = SIZE_MAX;
12672 }
12673 else
12674 {
12675 return VK_SUCCESS;
12676 }
12677 }
12678 }
12679 }
12680
CalcBlocksWithNonMovableCount()12681 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12682 {
12683 size_t result = 0;
12684 for(size_t i = 0; i < m_Blocks.size(); ++i)
12685 {
12686 if(m_Blocks[i]->m_HasNonMovableAllocations)
12687 {
12688 ++result;
12689 }
12690 }
12691 return result;
12692 }
12693
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12694 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12695 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12696 VkDeviceSize maxBytesToMove,
12697 uint32_t maxAllocationsToMove)
12698 {
12699 if(!m_AllAllocations && m_AllocationCount == 0)
12700 {
12701 return VK_SUCCESS;
12702 }
12703
12704 const size_t blockCount = m_Blocks.size();
12705 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12706 {
12707 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12708
12709 if(m_AllAllocations)
12710 {
12711 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12712 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12713 it != pMetadata->m_Suballocations.end();
12714 ++it)
12715 {
12716 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12717 {
12718 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12719 pBlockInfo->m_Allocations.push_back(allocInfo);
12720 }
12721 }
12722 }
12723
12724 pBlockInfo->CalcHasNonMovableAllocations();
12725
12726 // This is a choice based on research.
12727 // Option 1:
12728 pBlockInfo->SortAllocationsByOffsetDescending();
12729 // Option 2:
12730 //pBlockInfo->SortAllocationsBySizeDescending();
12731 }
12732
12733 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12734 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12735
12736 // This is a choice based on research.
12737 const uint32_t roundCount = 2;
12738
12739 // Execute defragmentation rounds (the main part).
12740 VkResult result = VK_SUCCESS;
12741 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12742 {
12743 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12744 }
12745
12746 return result;
12747 }
12748
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)12749 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12750 size_t dstBlockIndex, VkDeviceSize dstOffset,
12751 size_t srcBlockIndex, VkDeviceSize srcOffset)
12752 {
12753 if(dstBlockIndex < srcBlockIndex)
12754 {
12755 return true;
12756 }
12757 if(dstBlockIndex > srcBlockIndex)
12758 {
12759 return false;
12760 }
12761 if(dstOffset < srcOffset)
12762 {
12763 return true;
12764 }
12765 return false;
12766 }
12767
12768 ////////////////////////////////////////////////////////////////////////////////
12769 // VmaDefragmentationAlgorithm_Fast
12770
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)12771 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12772 VmaAllocator hAllocator,
12773 VmaBlockVector* pBlockVector,
12774 uint32_t currentFrameIndex,
12775 bool overlappingMoveSupported) :
12776 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12777 m_OverlappingMoveSupported(overlappingMoveSupported),
12778 m_AllocationCount(0),
12779 m_AllAllocations(false),
12780 m_BytesMoved(0),
12781 m_AllocationsMoved(0),
12782 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12783 {
12784 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12785
12786 }
12787
~VmaDefragmentationAlgorithm_Fast()12788 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12789 {
12790 }
12791
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)12792 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12793 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12794 VkDeviceSize maxBytesToMove,
12795 uint32_t maxAllocationsToMove)
12796 {
12797 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12798
12799 const size_t blockCount = m_pBlockVector->GetBlockCount();
12800 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12801 {
12802 return VK_SUCCESS;
12803 }
12804
12805 PreprocessMetadata();
12806
12807 // Sort blocks in order from most destination.
12808
12809 m_BlockInfos.resize(blockCount);
12810 for(size_t i = 0; i < blockCount; ++i)
12811 {
12812 m_BlockInfos[i].origBlockIndex = i;
12813 }
12814
12815 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12816 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12817 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12818 });
12819
12820 // THE MAIN ALGORITHM
12821
12822 FreeSpaceDatabase freeSpaceDb;
12823
12824 size_t dstBlockInfoIndex = 0;
12825 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12826 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12827 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12828 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12829 VkDeviceSize dstOffset = 0;
12830
12831 bool end = false;
12832 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12833 {
12834 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12835 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12836 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12837 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12838 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12839 {
12840 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12841 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12842 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12843 if(m_AllocationsMoved == maxAllocationsToMove ||
12844 m_BytesMoved + srcAllocSize > maxBytesToMove)
12845 {
12846 end = true;
12847 break;
12848 }
12849 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12850
12851 // Try to place it in one of free spaces from the database.
12852 size_t freeSpaceInfoIndex;
12853 VkDeviceSize dstAllocOffset;
12854 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12855 freeSpaceInfoIndex, dstAllocOffset))
12856 {
12857 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12858 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12859 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12860 VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12861
12862 // Same block
12863 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12864 {
12865 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12866
12867 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12868
12869 VmaSuballocation suballoc = *srcSuballocIt;
12870 suballoc.offset = dstAllocOffset;
12871 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12872 m_BytesMoved += srcAllocSize;
12873 ++m_AllocationsMoved;
12874
12875 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12876 ++nextSuballocIt;
12877 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12878 srcSuballocIt = nextSuballocIt;
12879
12880 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12881
12882 VmaDefragmentationMove move = {
12883 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12884 srcAllocOffset, dstAllocOffset,
12885 srcAllocSize };
12886 moves.push_back(move);
12887 }
12888 // Different block
12889 else
12890 {
12891 // MOVE OPTION 2: Move the allocation to a different block.
12892
12893 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12894
12895 VmaSuballocation suballoc = *srcSuballocIt;
12896 suballoc.offset = dstAllocOffset;
12897 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12898 m_BytesMoved += srcAllocSize;
12899 ++m_AllocationsMoved;
12900
12901 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12902 ++nextSuballocIt;
12903 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12904 srcSuballocIt = nextSuballocIt;
12905
12906 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12907
12908 VmaDefragmentationMove move = {
12909 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12910 srcAllocOffset, dstAllocOffset,
12911 srcAllocSize };
12912 moves.push_back(move);
12913 }
12914 }
12915 else
12916 {
12917 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12918
12919 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12920 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12921 dstAllocOffset + srcAllocSize > dstBlockSize)
12922 {
12923 // But before that, register remaining free space at the end of dst block.
12924 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12925
12926 ++dstBlockInfoIndex;
12927 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12928 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12929 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12930 dstBlockSize = pDstMetadata->GetSize();
12931 dstOffset = 0;
12932 dstAllocOffset = 0;
12933 }
12934
12935 // Same block
12936 if(dstBlockInfoIndex == srcBlockInfoIndex)
12937 {
12938 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12939
12940 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12941
12942 bool skipOver = overlap;
12943 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12944 {
12945 // If destination and source place overlap, skip if it would move it
12946 // by only < 1/64 of its size.
12947 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12948 }
12949
12950 if(skipOver)
12951 {
12952 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12953
12954 dstOffset = srcAllocOffset + srcAllocSize;
12955 ++srcSuballocIt;
12956 }
12957 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12958 else
12959 {
12960 srcSuballocIt->offset = dstAllocOffset;
12961 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12962 dstOffset = dstAllocOffset + srcAllocSize;
12963 m_BytesMoved += srcAllocSize;
12964 ++m_AllocationsMoved;
12965 ++srcSuballocIt;
12966 VmaDefragmentationMove move = {
12967 srcOrigBlockIndex, dstOrigBlockIndex,
12968 srcAllocOffset, dstAllocOffset,
12969 srcAllocSize };
12970 moves.push_back(move);
12971 }
12972 }
12973 // Different block
12974 else
12975 {
12976 // MOVE OPTION 2: Move the allocation to a different block.
12977
12978 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12979 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12980
12981 VmaSuballocation suballoc = *srcSuballocIt;
12982 suballoc.offset = dstAllocOffset;
12983 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12984 dstOffset = dstAllocOffset + srcAllocSize;
12985 m_BytesMoved += srcAllocSize;
12986 ++m_AllocationsMoved;
12987
12988 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12989 ++nextSuballocIt;
12990 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12991 srcSuballocIt = nextSuballocIt;
12992
12993 pDstMetadata->m_Suballocations.push_back(suballoc);
12994
12995 VmaDefragmentationMove move = {
12996 srcOrigBlockIndex, dstOrigBlockIndex,
12997 srcAllocOffset, dstAllocOffset,
12998 srcAllocSize };
12999 moves.push_back(move);
13000 }
13001 }
13002 }
13003 }
13004
13005 m_BlockInfos.clear();
13006
13007 PostprocessMetadata();
13008
13009 return VK_SUCCESS;
13010 }
13011
PreprocessMetadata()13012 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13013 {
13014 const size_t blockCount = m_pBlockVector->GetBlockCount();
13015 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13016 {
13017 VmaBlockMetadata_Generic* const pMetadata =
13018 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13019 pMetadata->m_FreeCount = 0;
13020 pMetadata->m_SumFreeSize = pMetadata->GetSize();
13021 pMetadata->m_FreeSuballocationsBySize.clear();
13022 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13023 it != pMetadata->m_Suballocations.end(); )
13024 {
13025 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13026 {
13027 VmaSuballocationList::iterator nextIt = it;
13028 ++nextIt;
13029 pMetadata->m_Suballocations.erase(it);
13030 it = nextIt;
13031 }
13032 else
13033 {
13034 ++it;
13035 }
13036 }
13037 }
13038 }
13039
PostprocessMetadata()13040 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13041 {
13042 const size_t blockCount = m_pBlockVector->GetBlockCount();
13043 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13044 {
13045 VmaBlockMetadata_Generic* const pMetadata =
13046 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13047 const VkDeviceSize blockSize = pMetadata->GetSize();
13048
13049 // No allocations in this block - entire area is free.
13050 if(pMetadata->m_Suballocations.empty())
13051 {
13052 pMetadata->m_FreeCount = 1;
13053 //pMetadata->m_SumFreeSize is already set to blockSize.
13054 VmaSuballocation suballoc = {
13055 0, // offset
13056 blockSize, // size
13057 VMA_NULL, // hAllocation
13058 VMA_SUBALLOCATION_TYPE_FREE };
13059 pMetadata->m_Suballocations.push_back(suballoc);
13060 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13061 }
13062 // There are some allocations in this block.
13063 else
13064 {
13065 VkDeviceSize offset = 0;
13066 VmaSuballocationList::iterator it;
13067 for(it = pMetadata->m_Suballocations.begin();
13068 it != pMetadata->m_Suballocations.end();
13069 ++it)
13070 {
13071 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13072 VMA_ASSERT(it->offset >= offset);
13073
13074 // Need to insert preceding free space.
13075 if(it->offset > offset)
13076 {
13077 ++pMetadata->m_FreeCount;
13078 const VkDeviceSize freeSize = it->offset - offset;
13079 VmaSuballocation suballoc = {
13080 offset, // offset
13081 freeSize, // size
13082 VMA_NULL, // hAllocation
13083 VMA_SUBALLOCATION_TYPE_FREE };
13084 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13085 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13086 {
13087 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13088 }
13089 }
13090
13091 pMetadata->m_SumFreeSize -= it->size;
13092 offset = it->offset + it->size;
13093 }
13094
13095 // Need to insert trailing free space.
13096 if(offset < blockSize)
13097 {
13098 ++pMetadata->m_FreeCount;
13099 const VkDeviceSize freeSize = blockSize - offset;
13100 VmaSuballocation suballoc = {
13101 offset, // offset
13102 freeSize, // size
13103 VMA_NULL, // hAllocation
13104 VMA_SUBALLOCATION_TYPE_FREE };
13105 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13106 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13107 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13108 {
13109 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13110 }
13111 }
13112
13113 VMA_SORT(
13114 pMetadata->m_FreeSuballocationsBySize.begin(),
13115 pMetadata->m_FreeSuballocationsBySize.end(),
13116 VmaSuballocationItemSizeLess());
13117 }
13118
13119 VMA_HEAVY_ASSERT(pMetadata->Validate());
13120 }
13121 }
13122
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)13123 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13124 {
13125 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13126 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13127 while(it != pMetadata->m_Suballocations.end())
13128 {
13129 if(it->offset < suballoc.offset)
13130 {
13131 ++it;
13132 }
13133 }
13134 pMetadata->m_Suballocations.insert(it, suballoc);
13135 }
13136
13137 ////////////////////////////////////////////////////////////////////////////////
13138 // VmaBlockVectorDefragmentationContext
13139
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex,uint32_t algorithmFlags)13140 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13141 VmaAllocator hAllocator,
13142 VmaPool hCustomPool,
13143 VmaBlockVector* pBlockVector,
13144 uint32_t currFrameIndex,
13145 uint32_t algorithmFlags) :
13146 res(VK_SUCCESS),
13147 mutexLocked(false),
13148 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13149 m_hAllocator(hAllocator),
13150 m_hCustomPool(hCustomPool),
13151 m_pBlockVector(pBlockVector),
13152 m_CurrFrameIndex(currFrameIndex),
13153 //m_AlgorithmFlags(algorithmFlags),
13154 m_pAlgorithm(VMA_NULL),
13155 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13156 m_AllAllocations(false)
13157 {
13158 }
13159
~VmaBlockVectorDefragmentationContext()13160 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13161 {
13162 vma_delete(m_hAllocator, m_pAlgorithm);
13163 }
13164
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13165 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13166 {
13167 AllocInfo info = { hAlloc, pChanged };
13168 m_Allocations.push_back(info);
13169 }
13170
Begin(bool overlappingMoveSupported)13171 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13172 {
13173 const bool allAllocations = m_AllAllocations ||
13174 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13175
13176 /********************************
13177 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13178 ********************************/
13179
13180 /*
13181 Fast algorithm is supported only when certain criteria are met:
13182 - VMA_DEBUG_MARGIN is 0.
13183 - All allocations in this block vector are moveable.
13184 - There is no possibility of image/buffer granularity conflict.
13185 */
13186 if(VMA_DEBUG_MARGIN == 0 &&
13187 allAllocations &&
13188 !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13189 {
13190 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13191 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13192 }
13193 else
13194 {
13195 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13196 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13197 }
13198
13199 if(allAllocations)
13200 {
13201 m_pAlgorithm->AddAll();
13202 }
13203 else
13204 {
13205 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13206 {
13207 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13208 }
13209 }
13210 }
13211
13212 ////////////////////////////////////////////////////////////////////////////////
13213 // VmaDefragmentationContext
13214
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)13215 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13216 VmaAllocator hAllocator,
13217 uint32_t currFrameIndex,
13218 uint32_t flags,
13219 VmaDefragmentationStats* pStats) :
13220 m_hAllocator(hAllocator),
13221 m_CurrFrameIndex(currFrameIndex),
13222 m_Flags(flags),
13223 m_pStats(pStats),
13224 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13225 {
13226 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13227 }
13228
~VmaDefragmentationContext_T()13229 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13230 {
13231 for(size_t i = m_CustomPoolContexts.size(); i--; )
13232 {
13233 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13234 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13235 vma_delete(m_hAllocator, pBlockVectorCtx);
13236 }
13237 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13238 {
13239 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13240 if(pBlockVectorCtx)
13241 {
13242 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13243 vma_delete(m_hAllocator, pBlockVectorCtx);
13244 }
13245 }
13246 }
13247
AddPools(uint32_t poolCount,VmaPool * pPools)13248 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13249 {
13250 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13251 {
13252 VmaPool pool = pPools[poolIndex];
13253 VMA_ASSERT(pool);
13254 // Pools with algorithm other than default are not defragmented.
13255 if(pool->m_BlockVector.GetAlgorithm() == 0)
13256 {
13257 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13258
13259 for(size_t i = m_CustomPoolContexts.size(); i--; )
13260 {
13261 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13262 {
13263 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13264 break;
13265 }
13266 }
13267
13268 if(!pBlockVectorDefragCtx)
13269 {
13270 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13271 m_hAllocator,
13272 pool,
13273 &pool->m_BlockVector,
13274 m_CurrFrameIndex,
13275 m_Flags);
13276 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13277 }
13278
13279 pBlockVectorDefragCtx->AddAll();
13280 }
13281 }
13282 }
13283
AddAllocations(uint32_t allocationCount,VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)13284 void VmaDefragmentationContext_T::AddAllocations(
13285 uint32_t allocationCount,
13286 VmaAllocation* pAllocations,
13287 VkBool32* pAllocationsChanged)
13288 {
13289 // Dispatch pAllocations among defragmentators. Create them when necessary.
13290 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13291 {
13292 const VmaAllocation hAlloc = pAllocations[allocIndex];
13293 VMA_ASSERT(hAlloc);
13294 // DedicatedAlloc cannot be defragmented.
13295 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13296 // Lost allocation cannot be defragmented.
13297 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13298 {
13299 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13300
13301 const VmaPool hAllocPool = hAlloc->GetPool();
13302 // This allocation belongs to custom pool.
13303 if(hAllocPool != VK_NULL_HANDLE)
13304 {
13305 // Pools with algorithm other than default are not defragmented.
13306 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13307 {
13308 for(size_t i = m_CustomPoolContexts.size(); i--; )
13309 {
13310 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13311 {
13312 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13313 break;
13314 }
13315 }
13316 if(!pBlockVectorDefragCtx)
13317 {
13318 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13319 m_hAllocator,
13320 hAllocPool,
13321 &hAllocPool->m_BlockVector,
13322 m_CurrFrameIndex,
13323 m_Flags);
13324 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13325 }
13326 }
13327 }
13328 // This allocation belongs to default pool.
13329 else
13330 {
13331 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13332 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13333 if(!pBlockVectorDefragCtx)
13334 {
13335 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13336 m_hAllocator,
13337 VMA_NULL, // hCustomPool
13338 m_hAllocator->m_pBlockVectors[memTypeIndex],
13339 m_CurrFrameIndex,
13340 m_Flags);
13341 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13342 }
13343 }
13344
13345 if(pBlockVectorDefragCtx)
13346 {
13347 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13348 &pAllocationsChanged[allocIndex] : VMA_NULL;
13349 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13350 }
13351 }
13352 }
13353 }
13354
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats)13355 VkResult VmaDefragmentationContext_T::Defragment(
13356 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13357 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13358 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13359 {
13360 if(pStats)
13361 {
13362 memset(pStats, 0, sizeof(VmaDefragmentationStats));
13363 }
13364
13365 if(commandBuffer == VK_NULL_HANDLE)
13366 {
13367 maxGpuBytesToMove = 0;
13368 maxGpuAllocationsToMove = 0;
13369 }
13370
13371 VkResult res = VK_SUCCESS;
13372
13373 // Process default pools.
13374 for(uint32_t memTypeIndex = 0;
13375 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13376 ++memTypeIndex)
13377 {
13378 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13379 if(pBlockVectorCtx)
13380 {
13381 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13382 pBlockVectorCtx->GetBlockVector()->Defragment(
13383 pBlockVectorCtx,
13384 pStats,
13385 maxCpuBytesToMove, maxCpuAllocationsToMove,
13386 maxGpuBytesToMove, maxGpuAllocationsToMove,
13387 commandBuffer);
13388 if(pBlockVectorCtx->res != VK_SUCCESS)
13389 {
13390 res = pBlockVectorCtx->res;
13391 }
13392 }
13393 }
13394
13395 // Process custom pools.
13396 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13397 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13398 ++customCtxIndex)
13399 {
13400 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13401 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13402 pBlockVectorCtx->GetBlockVector()->Defragment(
13403 pBlockVectorCtx,
13404 pStats,
13405 maxCpuBytesToMove, maxCpuAllocationsToMove,
13406 maxGpuBytesToMove, maxGpuAllocationsToMove,
13407 commandBuffer);
13408 if(pBlockVectorCtx->res != VK_SUCCESS)
13409 {
13410 res = pBlockVectorCtx->res;
13411 }
13412 }
13413
13414 return res;
13415 }
13416
13417 ////////////////////////////////////////////////////////////////////////////////
13418 // VmaRecorder
13419
13420 #if VMA_RECORDING_ENABLED
13421
VmaRecorder()13422 VmaRecorder::VmaRecorder() :
13423 m_UseMutex(true),
13424 m_Flags(0),
13425 m_File(VMA_NULL),
13426 m_Freq(INT64_MAX),
13427 m_StartCounter(INT64_MAX)
13428 {
13429 }
13430
Init(const VmaRecordSettings & settings,bool useMutex)13431 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13432 {
13433 m_UseMutex = useMutex;
13434 m_Flags = settings.flags;
13435
13436 QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13437 QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13438
13439 // Open file for writing.
13440 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13441 if(err != 0)
13442 {
13443 return VK_ERROR_INITIALIZATION_FAILED;
13444 }
13445
13446 // Write header.
13447 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13448 fprintf(m_File, "%s\n", "1,5");
13449
13450 return VK_SUCCESS;
13451 }
13452
~VmaRecorder()13453 VmaRecorder::~VmaRecorder()
13454 {
13455 if(m_File != VMA_NULL)
13456 {
13457 fclose(m_File);
13458 }
13459 }
13460
RecordCreateAllocator(uint32_t frameIndex)13461 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13462 {
13463 CallParams callParams;
13464 GetBasicParams(callParams);
13465
13466 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13467 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13468 Flush();
13469 }
13470
RecordDestroyAllocator(uint32_t frameIndex)13471 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13472 {
13473 CallParams callParams;
13474 GetBasicParams(callParams);
13475
13476 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13477 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13478 Flush();
13479 }
13480
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)13481 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13482 {
13483 CallParams callParams;
13484 GetBasicParams(callParams);
13485
13486 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13487 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13488 createInfo.memoryTypeIndex,
13489 createInfo.flags,
13490 createInfo.blockSize,
13491 (uint64_t)createInfo.minBlockCount,
13492 (uint64_t)createInfo.maxBlockCount,
13493 createInfo.frameInUseCount,
13494 pool);
13495 Flush();
13496 }
13497
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)13498 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13499 {
13500 CallParams callParams;
13501 GetBasicParams(callParams);
13502
13503 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13504 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13505 pool);
13506 Flush();
13507 }
13508
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13509 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13510 const VkMemoryRequirements& vkMemReq,
13511 const VmaAllocationCreateInfo& createInfo,
13512 VmaAllocation allocation)
13513 {
13514 CallParams callParams;
13515 GetBasicParams(callParams);
13516
13517 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13518 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13519 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13520 vkMemReq.size,
13521 vkMemReq.alignment,
13522 vkMemReq.memoryTypeBits,
13523 createInfo.flags,
13524 createInfo.usage,
13525 createInfo.requiredFlags,
13526 createInfo.preferredFlags,
13527 createInfo.memoryTypeBits,
13528 createInfo.pool,
13529 allocation,
13530 userDataStr.GetString());
13531 Flush();
13532 }
13533
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)13534 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13535 const VkMemoryRequirements& vkMemReq,
13536 const VmaAllocationCreateInfo& createInfo,
13537 uint64_t allocationCount,
13538 const VmaAllocation* pAllocations)
13539 {
13540 CallParams callParams;
13541 GetBasicParams(callParams);
13542
13543 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13544 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13545 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13546 vkMemReq.size,
13547 vkMemReq.alignment,
13548 vkMemReq.memoryTypeBits,
13549 createInfo.flags,
13550 createInfo.usage,
13551 createInfo.requiredFlags,
13552 createInfo.preferredFlags,
13553 createInfo.memoryTypeBits,
13554 createInfo.pool);
13555 PrintPointerList(allocationCount, pAllocations);
13556 fprintf(m_File, ",%s\n", userDataStr.GetString());
13557 Flush();
13558 }
13559
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13560 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13561 const VkMemoryRequirements& vkMemReq,
13562 bool requiresDedicatedAllocation,
13563 bool prefersDedicatedAllocation,
13564 const VmaAllocationCreateInfo& createInfo,
13565 VmaAllocation allocation)
13566 {
13567 CallParams callParams;
13568 GetBasicParams(callParams);
13569
13570 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13571 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13572 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,
13573 vkMemReq.size,
13574 vkMemReq.alignment,
13575 vkMemReq.memoryTypeBits,
13576 requiresDedicatedAllocation ? 1 : 0,
13577 prefersDedicatedAllocation ? 1 : 0,
13578 createInfo.flags,
13579 createInfo.usage,
13580 createInfo.requiredFlags,
13581 createInfo.preferredFlags,
13582 createInfo.memoryTypeBits,
13583 createInfo.pool,
13584 allocation,
13585 userDataStr.GetString());
13586 Flush();
13587 }
13588
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)13589 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13590 const VkMemoryRequirements& vkMemReq,
13591 bool requiresDedicatedAllocation,
13592 bool prefersDedicatedAllocation,
13593 const VmaAllocationCreateInfo& createInfo,
13594 VmaAllocation allocation)
13595 {
13596 CallParams callParams;
13597 GetBasicParams(callParams);
13598
13599 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13600 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13601 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,
13602 vkMemReq.size,
13603 vkMemReq.alignment,
13604 vkMemReq.memoryTypeBits,
13605 requiresDedicatedAllocation ? 1 : 0,
13606 prefersDedicatedAllocation ? 1 : 0,
13607 createInfo.flags,
13608 createInfo.usage,
13609 createInfo.requiredFlags,
13610 createInfo.preferredFlags,
13611 createInfo.memoryTypeBits,
13612 createInfo.pool,
13613 allocation,
13614 userDataStr.GetString());
13615 Flush();
13616 }
13617
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)13618 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13619 VmaAllocation allocation)
13620 {
13621 CallParams callParams;
13622 GetBasicParams(callParams);
13623
13624 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13625 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13626 allocation);
13627 Flush();
13628 }
13629
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)13630 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13631 uint64_t allocationCount,
13632 const VmaAllocation* pAllocations)
13633 {
13634 CallParams callParams;
13635 GetBasicParams(callParams);
13636
13637 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13638 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13639 PrintPointerList(allocationCount, pAllocations);
13640 fprintf(m_File, "\n");
13641 Flush();
13642 }
13643
RecordResizeAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize newSize)13644 void VmaRecorder::RecordResizeAllocation(
13645 uint32_t frameIndex,
13646 VmaAllocation allocation,
13647 VkDeviceSize newSize)
13648 {
13649 CallParams callParams;
13650 GetBasicParams(callParams);
13651
13652 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13653 fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13654 allocation, newSize);
13655 Flush();
13656 }
13657
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)13658 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13659 VmaAllocation allocation,
13660 const void* pUserData)
13661 {
13662 CallParams callParams;
13663 GetBasicParams(callParams);
13664
13665 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13666 UserDataString userDataStr(
13667 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13668 pUserData);
13669 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13670 allocation,
13671 userDataStr.GetString());
13672 Flush();
13673 }
13674
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)13675 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13676 VmaAllocation allocation)
13677 {
13678 CallParams callParams;
13679 GetBasicParams(callParams);
13680
13681 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13682 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13683 allocation);
13684 Flush();
13685 }
13686
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)13687 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13688 VmaAllocation allocation)
13689 {
13690 CallParams callParams;
13691 GetBasicParams(callParams);
13692
13693 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13694 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13695 allocation);
13696 Flush();
13697 }
13698
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)13699 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13700 VmaAllocation allocation)
13701 {
13702 CallParams callParams;
13703 GetBasicParams(callParams);
13704
13705 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13706 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13707 allocation);
13708 Flush();
13709 }
13710
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)13711 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13712 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13713 {
13714 CallParams callParams;
13715 GetBasicParams(callParams);
13716
13717 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13718 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13719 allocation,
13720 offset,
13721 size);
13722 Flush();
13723 }
13724
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)13725 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13726 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13727 {
13728 CallParams callParams;
13729 GetBasicParams(callParams);
13730
13731 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13732 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13733 allocation,
13734 offset,
13735 size);
13736 Flush();
13737 }
13738
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)13739 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13740 const VkBufferCreateInfo& bufCreateInfo,
13741 const VmaAllocationCreateInfo& allocCreateInfo,
13742 VmaAllocation allocation)
13743 {
13744 CallParams callParams;
13745 GetBasicParams(callParams);
13746
13747 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13748 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13749 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,
13750 bufCreateInfo.flags,
13751 bufCreateInfo.size,
13752 bufCreateInfo.usage,
13753 bufCreateInfo.sharingMode,
13754 allocCreateInfo.flags,
13755 allocCreateInfo.usage,
13756 allocCreateInfo.requiredFlags,
13757 allocCreateInfo.preferredFlags,
13758 allocCreateInfo.memoryTypeBits,
13759 allocCreateInfo.pool,
13760 allocation,
13761 userDataStr.GetString());
13762 Flush();
13763 }
13764
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)13765 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13766 const VkImageCreateInfo& imageCreateInfo,
13767 const VmaAllocationCreateInfo& allocCreateInfo,
13768 VmaAllocation allocation)
13769 {
13770 CallParams callParams;
13771 GetBasicParams(callParams);
13772
13773 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13774 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13775 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,
13776 imageCreateInfo.flags,
13777 imageCreateInfo.imageType,
13778 imageCreateInfo.format,
13779 imageCreateInfo.extent.width,
13780 imageCreateInfo.extent.height,
13781 imageCreateInfo.extent.depth,
13782 imageCreateInfo.mipLevels,
13783 imageCreateInfo.arrayLayers,
13784 imageCreateInfo.samples,
13785 imageCreateInfo.tiling,
13786 imageCreateInfo.usage,
13787 imageCreateInfo.sharingMode,
13788 imageCreateInfo.initialLayout,
13789 allocCreateInfo.flags,
13790 allocCreateInfo.usage,
13791 allocCreateInfo.requiredFlags,
13792 allocCreateInfo.preferredFlags,
13793 allocCreateInfo.memoryTypeBits,
13794 allocCreateInfo.pool,
13795 allocation,
13796 userDataStr.GetString());
13797 Flush();
13798 }
13799
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)13800 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13801 VmaAllocation allocation)
13802 {
13803 CallParams callParams;
13804 GetBasicParams(callParams);
13805
13806 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13807 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13808 allocation);
13809 Flush();
13810 }
13811
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)13812 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13813 VmaAllocation allocation)
13814 {
13815 CallParams callParams;
13816 GetBasicParams(callParams);
13817
13818 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13819 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13820 allocation);
13821 Flush();
13822 }
13823
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)13824 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13825 VmaAllocation allocation)
13826 {
13827 CallParams callParams;
13828 GetBasicParams(callParams);
13829
13830 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13831 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13832 allocation);
13833 Flush();
13834 }
13835
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)13836 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13837 VmaAllocation allocation)
13838 {
13839 CallParams callParams;
13840 GetBasicParams(callParams);
13841
13842 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13843 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13844 allocation);
13845 Flush();
13846 }
13847
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)13848 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13849 VmaPool pool)
13850 {
13851 CallParams callParams;
13852 GetBasicParams(callParams);
13853
13854 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13855 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13856 pool);
13857 Flush();
13858 }
13859
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)13860 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13861 const VmaDefragmentationInfo2& info,
13862 VmaDefragmentationContext ctx)
13863 {
13864 CallParams callParams;
13865 GetBasicParams(callParams);
13866
13867 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13868 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13869 info.flags);
13870 PrintPointerList(info.allocationCount, info.pAllocations);
13871 fprintf(m_File, ",");
13872 PrintPointerList(info.poolCount, info.pPools);
13873 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13874 info.maxCpuBytesToMove,
13875 info.maxCpuAllocationsToMove,
13876 info.maxGpuBytesToMove,
13877 info.maxGpuAllocationsToMove,
13878 info.commandBuffer,
13879 ctx);
13880 Flush();
13881 }
13882
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)13883 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13884 VmaDefragmentationContext ctx)
13885 {
13886 CallParams callParams;
13887 GetBasicParams(callParams);
13888
13889 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13890 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13891 ctx);
13892 Flush();
13893 }
13894
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)13895 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13896 {
13897 if(pUserData != VMA_NULL)
13898 {
13899 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13900 {
13901 m_Str = (const char*)pUserData;
13902 }
13903 else
13904 {
13905 sprintf_s(m_PtrStr, "%p", pUserData);
13906 m_Str = m_PtrStr;
13907 }
13908 }
13909 else
13910 {
13911 m_Str = "";
13912 }
13913 }
13914
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,bool dedicatedAllocationExtensionEnabled)13915 void VmaRecorder::WriteConfiguration(
13916 const VkPhysicalDeviceProperties& devProps,
13917 const VkPhysicalDeviceMemoryProperties& memProps,
13918 bool dedicatedAllocationExtensionEnabled)
13919 {
13920 fprintf(m_File, "Config,Begin\n");
13921
13922 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13923 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13924 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13925 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13926 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13927 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13928
13929 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13930 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13931 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13932
13933 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13934 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13935 {
13936 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13937 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13938 }
13939 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13940 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13941 {
13942 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13943 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13944 }
13945
13946 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13947
13948 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13949 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13950 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13951 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13952 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13953 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13954 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13955 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13956 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13957
13958 fprintf(m_File, "Config,End\n");
13959 }
13960
GetBasicParams(CallParams & outParams)13961 void VmaRecorder::GetBasicParams(CallParams& outParams)
13962 {
13963 outParams.threadId = GetCurrentThreadId();
13964
13965 LARGE_INTEGER counter;
13966 QueryPerformanceCounter(&counter);
13967 outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13968 }
13969
PrintPointerList(uint64_t count,const VmaAllocation * pItems)13970 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13971 {
13972 if(count)
13973 {
13974 fprintf(m_File, "%p", pItems[0]);
13975 for(uint64_t i = 1; i < count; ++i)
13976 {
13977 fprintf(m_File, " %p", pItems[i]);
13978 }
13979 }
13980 }
13981
Flush()13982 void VmaRecorder::Flush()
13983 {
13984 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13985 {
13986 fflush(m_File);
13987 }
13988 }
13989
13990 #endif // #if VMA_RECORDING_ENABLED
13991
13992 ////////////////////////////////////////////////////////////////////////////////
13993 // VmaAllocator_T
13994
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)13995 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13996 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13997 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13998 m_hDevice(pCreateInfo->device),
13999 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
14000 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
14001 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
14002 m_PreferredLargeHeapBlockSize(0),
14003 m_PhysicalDevice(pCreateInfo->physicalDevice),
14004 m_CurrentFrameIndex(0),
14005 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
14006 m_NextPoolId(0)
14007 #if VMA_RECORDING_ENABLED
14008 ,m_pRecorder(VMA_NULL)
14009 #endif
14010 {
14011 if(VMA_DEBUG_DETECT_CORRUPTION)
14012 {
14013 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14014 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14015 }
14016
14017 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
14018
14019 #if !(VMA_DEDICATED_ALLOCATION)
14020 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14021 {
14022 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14023 }
14024 #endif
14025
14026 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14027 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14028 memset(&m_MemProps, 0, sizeof(m_MemProps));
14029
14030 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14031 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14032
14033 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14034 {
14035 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14036 }
14037
14038 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14039 {
14040 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14041 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14042 }
14043
14044 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14045
14046 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14047 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14048
14049 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14050 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14051 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14052 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14053
14054 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14055 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14056
14057 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14058 {
14059 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14060 {
14061 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14062 if(limit != VK_WHOLE_SIZE)
14063 {
14064 m_HeapSizeLimit[heapIndex] = limit;
14065 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14066 {
14067 m_MemProps.memoryHeaps[heapIndex].size = limit;
14068 }
14069 }
14070 }
14071 }
14072
14073 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14074 {
14075 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14076
14077 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14078 this,
14079 memTypeIndex,
14080 preferredBlockSize,
14081 0,
14082 SIZE_MAX,
14083 GetBufferImageGranularity(),
14084 pCreateInfo->frameInUseCount,
14085 false, // isCustomPool
14086 false, // explicitBlockSize
14087 false); // linearAlgorithm
14088 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14089 // becase minBlockCount is 0.
14090 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14091
14092 }
14093 }
14094
Init(const VmaAllocatorCreateInfo * pCreateInfo)14095 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14096 {
14097 VkResult res = VK_SUCCESS;
14098
14099 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14100 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14101 {
14102 #if VMA_RECORDING_ENABLED
14103 m_pRecorder = vma_new(this, VmaRecorder)();
14104 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14105 if(res != VK_SUCCESS)
14106 {
14107 return res;
14108 }
14109 m_pRecorder->WriteConfiguration(
14110 m_PhysicalDeviceProperties,
14111 m_MemProps,
14112 m_UseKhrDedicatedAllocation);
14113 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14114 #else
14115 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14116 return VK_ERROR_FEATURE_NOT_PRESENT;
14117 #endif
14118 }
14119
14120 return res;
14121 }
14122
~VmaAllocator_T()14123 VmaAllocator_T::~VmaAllocator_T()
14124 {
14125 #if VMA_RECORDING_ENABLED
14126 if(m_pRecorder != VMA_NULL)
14127 {
14128 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14129 vma_delete(this, m_pRecorder);
14130 }
14131 #endif
14132
14133 VMA_ASSERT(m_Pools.empty());
14134
14135 for(size_t i = GetMemoryTypeCount(); i--; )
14136 {
14137 vma_delete(this, m_pDedicatedAllocations[i]);
14138 vma_delete(this, m_pBlockVectors[i]);
14139 }
14140 }
14141
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14142 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14143 {
14144 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14145 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14146 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14147 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14148 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14149 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14150 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14151 m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14152 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14153 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14154 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14155 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14156 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14157 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14158 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14159 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14160 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14161 m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14162 #if VMA_DEDICATED_ALLOCATION
14163 if(m_UseKhrDedicatedAllocation)
14164 {
14165 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14166 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14167 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14168 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14169 }
14170 #endif // #if VMA_DEDICATED_ALLOCATION
14171 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14172
14173 #define VMA_COPY_IF_NOT_NULL(funcName) \
14174 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14175
14176 if(pVulkanFunctions != VMA_NULL)
14177 {
14178 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14179 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14180 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14181 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14182 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14183 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14184 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14185 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14186 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14187 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14188 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14189 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14190 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14191 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14192 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14193 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14194 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14195 #if VMA_DEDICATED_ALLOCATION
14196 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14197 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14198 #endif
14199 }
14200
14201 #undef VMA_COPY_IF_NOT_NULL
14202
14203 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14204 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14205 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14206 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14207 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14208 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14209 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14210 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14211 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14212 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14213 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14214 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14215 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14216 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14217 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14218 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14219 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14220 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14221 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14222 #if VMA_DEDICATED_ALLOCATION
14223 if(m_UseKhrDedicatedAllocation)
14224 {
14225 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14226 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14227 }
14228 #endif
14229 }
14230
CalcPreferredBlockSize(uint32_t memTypeIndex)14231 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14232 {
14233 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14234 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14235 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14236 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14237 }
14238
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)14239 VkResult VmaAllocator_T::AllocateMemoryOfType(
14240 VkDeviceSize size,
14241 VkDeviceSize alignment,
14242 bool dedicatedAllocation,
14243 VkBuffer dedicatedBuffer,
14244 VkImage dedicatedImage,
14245 const VmaAllocationCreateInfo& createInfo,
14246 uint32_t memTypeIndex,
14247 VmaSuballocationType suballocType,
14248 size_t allocationCount,
14249 VmaAllocation* pAllocations)
14250 {
14251 VMA_ASSERT(pAllocations != VMA_NULL);
14252 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14253
14254 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14255
14256 // If memory type is not HOST_VISIBLE, disable MAPPED.
14257 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14258 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14259 {
14260 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14261 }
14262
14263 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14264 VMA_ASSERT(blockVector);
14265
14266 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14267 bool preferDedicatedMemory =
14268 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14269 dedicatedAllocation ||
14270 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14271 size > preferredBlockSize / 2;
14272
14273 if(preferDedicatedMemory &&
14274 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14275 finalCreateInfo.pool == VK_NULL_HANDLE)
14276 {
14277 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14278 }
14279
14280 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14281 {
14282 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14283 {
14284 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14285 }
14286 else
14287 {
14288 return AllocateDedicatedMemory(
14289 size,
14290 suballocType,
14291 memTypeIndex,
14292 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14293 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14294 finalCreateInfo.pUserData,
14295 dedicatedBuffer,
14296 dedicatedImage,
14297 allocationCount,
14298 pAllocations);
14299 }
14300 }
14301 else
14302 {
14303 VkResult res = blockVector->Allocate(
14304 VK_NULL_HANDLE, // hCurrentPool
14305 m_CurrentFrameIndex.load(),
14306 size,
14307 alignment,
14308 finalCreateInfo,
14309 suballocType,
14310 allocationCount,
14311 pAllocations);
14312 if(res == VK_SUCCESS)
14313 {
14314 return res;
14315 }
14316
14317 // 5. Try dedicated memory.
14318 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14319 {
14320 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14321 }
14322 else
14323 {
14324 res = AllocateDedicatedMemory(
14325 size,
14326 suballocType,
14327 memTypeIndex,
14328 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14329 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14330 finalCreateInfo.pUserData,
14331 dedicatedBuffer,
14332 dedicatedImage,
14333 allocationCount,
14334 pAllocations);
14335 if(res == VK_SUCCESS)
14336 {
14337 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14338 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14339 return VK_SUCCESS;
14340 }
14341 else
14342 {
14343 // Everything failed: Return error code.
14344 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14345 return res;
14346 }
14347 }
14348 }
14349 }
14350
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)14351 VkResult VmaAllocator_T::AllocateDedicatedMemory(
14352 VkDeviceSize size,
14353 VmaSuballocationType suballocType,
14354 uint32_t memTypeIndex,
14355 bool map,
14356 bool isUserDataString,
14357 void* pUserData,
14358 VkBuffer dedicatedBuffer,
14359 VkImage dedicatedImage,
14360 size_t allocationCount,
14361 VmaAllocation* pAllocations)
14362 {
14363 VMA_ASSERT(allocationCount > 0 && pAllocations);
14364
14365 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14366 allocInfo.memoryTypeIndex = memTypeIndex;
14367 allocInfo.allocationSize = size;
14368
14369 #if VMA_DEDICATED_ALLOCATION
14370 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14371 if(m_UseKhrDedicatedAllocation)
14372 {
14373 if(dedicatedBuffer != VK_NULL_HANDLE)
14374 {
14375 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14376 dedicatedAllocInfo.buffer = dedicatedBuffer;
14377 allocInfo.pNext = &dedicatedAllocInfo;
14378 }
14379 else if(dedicatedImage != VK_NULL_HANDLE)
14380 {
14381 dedicatedAllocInfo.image = dedicatedImage;
14382 allocInfo.pNext = &dedicatedAllocInfo;
14383 }
14384 }
14385 #endif // #if VMA_DEDICATED_ALLOCATION
14386
14387 size_t allocIndex;
14388 VkResult res = VK_SUCCESS;
14389 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14390 {
14391 res = AllocateDedicatedMemoryPage(
14392 size,
14393 suballocType,
14394 memTypeIndex,
14395 allocInfo,
14396 map,
14397 isUserDataString,
14398 pUserData,
14399 pAllocations + allocIndex);
14400 if(res != VK_SUCCESS)
14401 {
14402 break;
14403 }
14404 }
14405
14406 if(res == VK_SUCCESS)
14407 {
14408 // Register them in m_pDedicatedAllocations.
14409 {
14410 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14411 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14412 VMA_ASSERT(pDedicatedAllocations);
14413 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14414 {
14415 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14416 }
14417 }
14418
14419 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14420 }
14421 else
14422 {
14423 // Free all already created allocations.
14424 while(allocIndex--)
14425 {
14426 VmaAllocation currAlloc = pAllocations[allocIndex];
14427 VkDeviceMemory hMemory = currAlloc->GetMemory();
14428
14429 /*
14430 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14431 before vkFreeMemory.
14432
14433 if(currAlloc->GetMappedData() != VMA_NULL)
14434 {
14435 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14436 }
14437 */
14438
14439 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14440
14441 currAlloc->SetUserData(this, VMA_NULL);
14442 vma_delete(this, currAlloc);
14443 }
14444
14445 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14446 }
14447
14448 return res;
14449 }
14450
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)14451 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14452 VkDeviceSize size,
14453 VmaSuballocationType suballocType,
14454 uint32_t memTypeIndex,
14455 const VkMemoryAllocateInfo& allocInfo,
14456 bool map,
14457 bool isUserDataString,
14458 void* pUserData,
14459 VmaAllocation* pAllocation)
14460 {
14461 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14462 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14463 if(res < 0)
14464 {
14465 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14466 return res;
14467 }
14468
14469 void* pMappedData = VMA_NULL;
14470 if(map)
14471 {
14472 res = (*m_VulkanFunctions.vkMapMemory)(
14473 m_hDevice,
14474 hMemory,
14475 0,
14476 VK_WHOLE_SIZE,
14477 0,
14478 &pMappedData);
14479 if(res < 0)
14480 {
14481 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14482 FreeVulkanMemory(memTypeIndex, size, hMemory);
14483 return res;
14484 }
14485 }
14486
14487 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14488 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14489 (*pAllocation)->SetUserData(this, pUserData);
14490 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14491 {
14492 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14493 }
14494
14495 return VK_SUCCESS;
14496 }
14497
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14498 void VmaAllocator_T::GetBufferMemoryRequirements(
14499 VkBuffer hBuffer,
14500 VkMemoryRequirements& memReq,
14501 bool& requiresDedicatedAllocation,
14502 bool& prefersDedicatedAllocation) const
14503 {
14504 #if VMA_DEDICATED_ALLOCATION
14505 if(m_UseKhrDedicatedAllocation)
14506 {
14507 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14508 memReqInfo.buffer = hBuffer;
14509
14510 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14511
14512 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14513 memReq2.pNext = &memDedicatedReq;
14514
14515 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14516
14517 memReq = memReq2.memoryRequirements;
14518 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14519 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14520 }
14521 else
14522 #endif // #if VMA_DEDICATED_ALLOCATION
14523 {
14524 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14525 requiresDedicatedAllocation = false;
14526 prefersDedicatedAllocation = false;
14527 }
14528 }
14529
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14530 void VmaAllocator_T::GetImageMemoryRequirements(
14531 VkImage hImage,
14532 VkMemoryRequirements& memReq,
14533 bool& requiresDedicatedAllocation,
14534 bool& prefersDedicatedAllocation) const
14535 {
14536 #if VMA_DEDICATED_ALLOCATION
14537 if(m_UseKhrDedicatedAllocation)
14538 {
14539 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14540 memReqInfo.image = hImage;
14541
14542 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14543
14544 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14545 memReq2.pNext = &memDedicatedReq;
14546
14547 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14548
14549 memReq = memReq2.memoryRequirements;
14550 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14551 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14552 }
14553 else
14554 #endif // #if VMA_DEDICATED_ALLOCATION
14555 {
14556 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14557 requiresDedicatedAllocation = false;
14558 prefersDedicatedAllocation = false;
14559 }
14560 }
14561
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)14562 VkResult VmaAllocator_T::AllocateMemory(
14563 const VkMemoryRequirements& vkMemReq,
14564 bool requiresDedicatedAllocation,
14565 bool prefersDedicatedAllocation,
14566 VkBuffer dedicatedBuffer,
14567 VkImage dedicatedImage,
14568 const VmaAllocationCreateInfo& createInfo,
14569 VmaSuballocationType suballocType,
14570 size_t allocationCount,
14571 VmaAllocation* pAllocations)
14572 {
14573 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14574
14575 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14576
14577 if(vkMemReq.size == 0)
14578 {
14579 return VK_ERROR_VALIDATION_FAILED_EXT;
14580 }
14581 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14582 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14583 {
14584 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14585 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14586 }
14587 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14588 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14589 {
14590 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14591 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14592 }
14593 if(requiresDedicatedAllocation)
14594 {
14595 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14596 {
14597 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14598 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14599 }
14600 if(createInfo.pool != VK_NULL_HANDLE)
14601 {
14602 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14603 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14604 }
14605 }
14606 if((createInfo.pool != VK_NULL_HANDLE) &&
14607 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14608 {
14609 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14610 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14611 }
14612
14613 if(createInfo.pool != VK_NULL_HANDLE)
14614 {
14615 const VkDeviceSize alignmentForPool = VMA_MAX(
14616 vkMemReq.alignment,
14617 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14618 return createInfo.pool->m_BlockVector.Allocate(
14619 createInfo.pool,
14620 m_CurrentFrameIndex.load(),
14621 vkMemReq.size,
14622 alignmentForPool,
14623 createInfo,
14624 suballocType,
14625 allocationCount,
14626 pAllocations);
14627 }
14628 else
14629 {
14630 // Bit mask of memory Vulkan types acceptable for this allocation.
14631 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14632 uint32_t memTypeIndex = UINT32_MAX;
14633 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14634 if(res == VK_SUCCESS)
14635 {
14636 VkDeviceSize alignmentForMemType = VMA_MAX(
14637 vkMemReq.alignment,
14638 GetMemoryTypeMinAlignment(memTypeIndex));
14639
14640 res = AllocateMemoryOfType(
14641 vkMemReq.size,
14642 alignmentForMemType,
14643 requiresDedicatedAllocation || prefersDedicatedAllocation,
14644 dedicatedBuffer,
14645 dedicatedImage,
14646 createInfo,
14647 memTypeIndex,
14648 suballocType,
14649 allocationCount,
14650 pAllocations);
14651 // Succeeded on first try.
14652 if(res == VK_SUCCESS)
14653 {
14654 return res;
14655 }
14656 // Allocation from this memory type failed. Try other compatible memory types.
14657 else
14658 {
14659 for(;;)
14660 {
14661 // Remove old memTypeIndex from list of possibilities.
14662 memoryTypeBits &= ~(1u << memTypeIndex);
14663 // Find alternative memTypeIndex.
14664 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14665 if(res == VK_SUCCESS)
14666 {
14667 alignmentForMemType = VMA_MAX(
14668 vkMemReq.alignment,
14669 GetMemoryTypeMinAlignment(memTypeIndex));
14670
14671 res = AllocateMemoryOfType(
14672 vkMemReq.size,
14673 alignmentForMemType,
14674 requiresDedicatedAllocation || prefersDedicatedAllocation,
14675 dedicatedBuffer,
14676 dedicatedImage,
14677 createInfo,
14678 memTypeIndex,
14679 suballocType,
14680 allocationCount,
14681 pAllocations);
14682 // Allocation from this alternative memory type succeeded.
14683 if(res == VK_SUCCESS)
14684 {
14685 return res;
14686 }
14687 // else: Allocation from this memory type failed. Try next one - next loop iteration.
14688 }
14689 // No other matching memory type index could be found.
14690 else
14691 {
14692 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14693 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14694 }
14695 }
14696 }
14697 }
14698 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14699 else
14700 return res;
14701 }
14702 }
14703
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)14704 void VmaAllocator_T::FreeMemory(
14705 size_t allocationCount,
14706 const VmaAllocation* pAllocations)
14707 {
14708 VMA_ASSERT(pAllocations);
14709
14710 for(size_t allocIndex = allocationCount; allocIndex--; )
14711 {
14712 VmaAllocation allocation = pAllocations[allocIndex];
14713
14714 if(allocation != VK_NULL_HANDLE)
14715 {
14716 if(TouchAllocation(allocation))
14717 {
14718 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14719 {
14720 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14721 }
14722
14723 switch(allocation->GetType())
14724 {
14725 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14726 {
14727 VmaBlockVector* pBlockVector = VMA_NULL;
14728 VmaPool hPool = allocation->GetPool();
14729 if(hPool != VK_NULL_HANDLE)
14730 {
14731 pBlockVector = &hPool->m_BlockVector;
14732 }
14733 else
14734 {
14735 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14736 pBlockVector = m_pBlockVectors[memTypeIndex];
14737 }
14738 pBlockVector->Free(allocation);
14739 }
14740 break;
14741 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14742 FreeDedicatedMemory(allocation);
14743 break;
14744 default:
14745 VMA_ASSERT(0);
14746 }
14747 }
14748
14749 allocation->SetUserData(this, VMA_NULL);
14750 vma_delete(this, allocation);
14751 }
14752 }
14753 }
14754
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)14755 VkResult VmaAllocator_T::ResizeAllocation(
14756 const VmaAllocation alloc,
14757 VkDeviceSize newSize)
14758 {
14759 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14760 {
14761 return VK_ERROR_VALIDATION_FAILED_EXT;
14762 }
14763 if(newSize == alloc->GetSize())
14764 {
14765 return VK_SUCCESS;
14766 }
14767
14768 switch(alloc->GetType())
14769 {
14770 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14771 return VK_ERROR_FEATURE_NOT_PRESENT;
14772 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14773 if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14774 {
14775 alloc->ChangeSize(newSize);
14776 VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14777 return VK_SUCCESS;
14778 }
14779 else
14780 {
14781 return VK_ERROR_OUT_OF_POOL_MEMORY;
14782 }
14783 default:
14784 VMA_ASSERT(0);
14785 return VK_ERROR_VALIDATION_FAILED_EXT;
14786 }
14787 }
14788
CalculateStats(VmaStats * pStats)14789 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14790 {
14791 // Initialize.
14792 InitStatInfo(pStats->total);
14793 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14794 InitStatInfo(pStats->memoryType[i]);
14795 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14796 InitStatInfo(pStats->memoryHeap[i]);
14797
14798 // Process default pools.
14799 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14800 {
14801 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14802 VMA_ASSERT(pBlockVector);
14803 pBlockVector->AddStats(pStats);
14804 }
14805
14806 // Process custom pools.
14807 {
14808 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14809 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14810 {
14811 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14812 }
14813 }
14814
14815 // Process dedicated allocations.
14816 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14817 {
14818 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14819 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14820 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14821 VMA_ASSERT(pDedicatedAllocVector);
14822 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14823 {
14824 VmaStatInfo allocationStatInfo;
14825 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14826 VmaAddStatInfo(pStats->total, allocationStatInfo);
14827 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14828 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14829 }
14830 }
14831
14832 // Postprocess.
14833 VmaPostprocessCalcStatInfo(pStats->total);
14834 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14835 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14836 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14837 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14838 }
14839
14840 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14841
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)14842 VkResult VmaAllocator_T::DefragmentationBegin(
14843 const VmaDefragmentationInfo2& info,
14844 VmaDefragmentationStats* pStats,
14845 VmaDefragmentationContext* pContext)
14846 {
14847 if(info.pAllocationsChanged != VMA_NULL)
14848 {
14849 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14850 }
14851
14852 *pContext = vma_new(this, VmaDefragmentationContext_T)(
14853 this, m_CurrentFrameIndex.load(), info.flags, pStats);
14854
14855 (*pContext)->AddPools(info.poolCount, info.pPools);
14856 (*pContext)->AddAllocations(
14857 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14858
14859 VkResult res = (*pContext)->Defragment(
14860 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14861 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14862 info.commandBuffer, pStats);
14863
14864 if(res != VK_NOT_READY)
14865 {
14866 vma_delete(this, *pContext);
14867 *pContext = VMA_NULL;
14868 }
14869
14870 return res;
14871 }
14872
DefragmentationEnd(VmaDefragmentationContext context)14873 VkResult VmaAllocator_T::DefragmentationEnd(
14874 VmaDefragmentationContext context)
14875 {
14876 vma_delete(this, context);
14877 return VK_SUCCESS;
14878 }
14879
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)14880 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14881 {
14882 if(hAllocation->CanBecomeLost())
14883 {
14884 /*
14885 Warning: This is a carefully designed algorithm.
14886 Do not modify unless you really know what you're doing :)
14887 */
14888 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14889 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14890 for(;;)
14891 {
14892 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14893 {
14894 pAllocationInfo->memoryType = UINT32_MAX;
14895 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14896 pAllocationInfo->offset = 0;
14897 pAllocationInfo->size = hAllocation->GetSize();
14898 pAllocationInfo->pMappedData = VMA_NULL;
14899 pAllocationInfo->pUserData = hAllocation->GetUserData();
14900 return;
14901 }
14902 else if(localLastUseFrameIndex == localCurrFrameIndex)
14903 {
14904 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14905 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14906 pAllocationInfo->offset = hAllocation->GetOffset();
14907 pAllocationInfo->size = hAllocation->GetSize();
14908 pAllocationInfo->pMappedData = VMA_NULL;
14909 pAllocationInfo->pUserData = hAllocation->GetUserData();
14910 return;
14911 }
14912 else // Last use time earlier than current time.
14913 {
14914 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14915 {
14916 localLastUseFrameIndex = localCurrFrameIndex;
14917 }
14918 }
14919 }
14920 }
14921 else
14922 {
14923 #if VMA_STATS_STRING_ENABLED
14924 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14925 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14926 for(;;)
14927 {
14928 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14929 if(localLastUseFrameIndex == localCurrFrameIndex)
14930 {
14931 break;
14932 }
14933 else // Last use time earlier than current time.
14934 {
14935 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14936 {
14937 localLastUseFrameIndex = localCurrFrameIndex;
14938 }
14939 }
14940 }
14941 #endif
14942
14943 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14944 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14945 pAllocationInfo->offset = hAllocation->GetOffset();
14946 pAllocationInfo->size = hAllocation->GetSize();
14947 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14948 pAllocationInfo->pUserData = hAllocation->GetUserData();
14949 }
14950 }
14951
TouchAllocation(VmaAllocation hAllocation)14952 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14953 {
14954 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14955 if(hAllocation->CanBecomeLost())
14956 {
14957 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14958 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14959 for(;;)
14960 {
14961 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14962 {
14963 return false;
14964 }
14965 else if(localLastUseFrameIndex == localCurrFrameIndex)
14966 {
14967 return true;
14968 }
14969 else // Last use time earlier than current time.
14970 {
14971 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14972 {
14973 localLastUseFrameIndex = localCurrFrameIndex;
14974 }
14975 }
14976 }
14977 }
14978 else
14979 {
14980 #if VMA_STATS_STRING_ENABLED
14981 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14982 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14983 for(;;)
14984 {
14985 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14986 if(localLastUseFrameIndex == localCurrFrameIndex)
14987 {
14988 break;
14989 }
14990 else // Last use time earlier than current time.
14991 {
14992 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14993 {
14994 localLastUseFrameIndex = localCurrFrameIndex;
14995 }
14996 }
14997 }
14998 #endif
14999
15000 return true;
15001 }
15002 }
15003
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15004 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15005 {
15006 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15007
15008 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15009
15010 if(newCreateInfo.maxBlockCount == 0)
15011 {
15012 newCreateInfo.maxBlockCount = SIZE_MAX;
15013 }
15014 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15015 {
15016 return VK_ERROR_INITIALIZATION_FAILED;
15017 }
15018
15019 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15020
15021 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15022
15023 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15024 if(res != VK_SUCCESS)
15025 {
15026 vma_delete(this, *pPool);
15027 *pPool = VMA_NULL;
15028 return res;
15029 }
15030
15031 // Add to m_Pools.
15032 {
15033 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15034 (*pPool)->SetId(m_NextPoolId++);
15035 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15036 }
15037
15038 return VK_SUCCESS;
15039 }
15040
DestroyPool(VmaPool pool)15041 void VmaAllocator_T::DestroyPool(VmaPool pool)
15042 {
15043 // Remove from m_Pools.
15044 {
15045 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15046 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15047 VMA_ASSERT(success && "Pool not found in Allocator.");
15048 }
15049
15050 vma_delete(this, pool);
15051 }
15052
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)15053 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15054 {
15055 pool->m_BlockVector.GetPoolStats(pPoolStats);
15056 }
15057
SetCurrentFrameIndex(uint32_t frameIndex)15058 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15059 {
15060 m_CurrentFrameIndex.store(frameIndex);
15061 }
15062
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)15063 void VmaAllocator_T::MakePoolAllocationsLost(
15064 VmaPool hPool,
15065 size_t* pLostAllocationCount)
15066 {
15067 hPool->m_BlockVector.MakePoolAllocationsLost(
15068 m_CurrentFrameIndex.load(),
15069 pLostAllocationCount);
15070 }
15071
CheckPoolCorruption(VmaPool hPool)15072 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15073 {
15074 return hPool->m_BlockVector.CheckCorruption();
15075 }
15076
CheckCorruption(uint32_t memoryTypeBits)15077 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15078 {
15079 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15080
15081 // Process default pools.
15082 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15083 {
15084 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15085 {
15086 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15087 VMA_ASSERT(pBlockVector);
15088 VkResult localRes = pBlockVector->CheckCorruption();
15089 switch(localRes)
15090 {
15091 case VK_ERROR_FEATURE_NOT_PRESENT:
15092 break;
15093 case VK_SUCCESS:
15094 finalRes = VK_SUCCESS;
15095 break;
15096 default:
15097 return localRes;
15098 }
15099 }
15100 }
15101
15102 // Process custom pools.
15103 {
15104 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15105 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15106 {
15107 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15108 {
15109 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15110 switch(localRes)
15111 {
15112 case VK_ERROR_FEATURE_NOT_PRESENT:
15113 break;
15114 case VK_SUCCESS:
15115 finalRes = VK_SUCCESS;
15116 break;
15117 default:
15118 return localRes;
15119 }
15120 }
15121 }
15122 }
15123
15124 return finalRes;
15125 }
15126
CreateLostAllocation(VmaAllocation * pAllocation)15127 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15128 {
15129 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15130 (*pAllocation)->InitLost();
15131 }
15132
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)15133 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15134 {
15135 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15136
15137 VkResult res;
15138 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15139 {
15140 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15141 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15142 {
15143 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15144 if(res == VK_SUCCESS)
15145 {
15146 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15147 }
15148 }
15149 else
15150 {
15151 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15152 }
15153 }
15154 else
15155 {
15156 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15157 }
15158
15159 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15160 {
15161 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15162 }
15163
15164 return res;
15165 }
15166
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)15167 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15168 {
15169 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15170 {
15171 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15172 }
15173
15174 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15175
15176 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15177 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15178 {
15179 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15180 m_HeapSizeLimit[heapIndex] += size;
15181 }
15182 }
15183
Map(VmaAllocation hAllocation,void ** ppData)15184 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15185 {
15186 if(hAllocation->CanBecomeLost())
15187 {
15188 return VK_ERROR_MEMORY_MAP_FAILED;
15189 }
15190
15191 switch(hAllocation->GetType())
15192 {
15193 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15194 {
15195 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15196 char *pBytes = VMA_NULL;
15197 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15198 if(res == VK_SUCCESS)
15199 {
15200 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15201 hAllocation->BlockAllocMap();
15202 }
15203 return res;
15204 }
15205 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15206 return hAllocation->DedicatedAllocMap(this, ppData);
15207 default:
15208 VMA_ASSERT(0);
15209 return VK_ERROR_MEMORY_MAP_FAILED;
15210 }
15211 }
15212
Unmap(VmaAllocation hAllocation)15213 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15214 {
15215 switch(hAllocation->GetType())
15216 {
15217 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15218 {
15219 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15220 hAllocation->BlockAllocUnmap();
15221 pBlock->Unmap(this, 1);
15222 }
15223 break;
15224 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15225 hAllocation->DedicatedAllocUnmap(this);
15226 break;
15227 default:
15228 VMA_ASSERT(0);
15229 }
15230 }
15231
BindBufferMemory(VmaAllocation hAllocation,VkBuffer hBuffer)15232 VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15233 {
15234 VkResult res = VK_SUCCESS;
15235 switch(hAllocation->GetType())
15236 {
15237 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15238 res = GetVulkanFunctions().vkBindBufferMemory(
15239 m_hDevice,
15240 hBuffer,
15241 hAllocation->GetMemory(),
15242 0); //memoryOffset
15243 break;
15244 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15245 {
15246 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15247 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15248 res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15249 break;
15250 }
15251 default:
15252 VMA_ASSERT(0);
15253 }
15254 return res;
15255 }
15256
BindImageMemory(VmaAllocation hAllocation,VkImage hImage)15257 VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15258 {
15259 VkResult res = VK_SUCCESS;
15260 switch(hAllocation->GetType())
15261 {
15262 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15263 res = GetVulkanFunctions().vkBindImageMemory(
15264 m_hDevice,
15265 hImage,
15266 hAllocation->GetMemory(),
15267 0); //memoryOffset
15268 break;
15269 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15270 {
15271 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15272 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15273 res = pBlock->BindImageMemory(this, hAllocation, hImage);
15274 break;
15275 }
15276 default:
15277 VMA_ASSERT(0);
15278 }
15279 return res;
15280 }
15281
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)15282 void VmaAllocator_T::FlushOrInvalidateAllocation(
15283 VmaAllocation hAllocation,
15284 VkDeviceSize offset, VkDeviceSize size,
15285 VMA_CACHE_OPERATION op)
15286 {
15287 const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15288 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15289 {
15290 const VkDeviceSize allocationSize = hAllocation->GetSize();
15291 VMA_ASSERT(offset <= allocationSize);
15292
15293 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15294
15295 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15296 memRange.memory = hAllocation->GetMemory();
15297
15298 switch(hAllocation->GetType())
15299 {
15300 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15301 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15302 if(size == VK_WHOLE_SIZE)
15303 {
15304 memRange.size = allocationSize - memRange.offset;
15305 }
15306 else
15307 {
15308 VMA_ASSERT(offset + size <= allocationSize);
15309 memRange.size = VMA_MIN(
15310 VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15311 allocationSize - memRange.offset);
15312 }
15313 break;
15314
15315 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15316 {
15317 // 1. Still within this allocation.
15318 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15319 if(size == VK_WHOLE_SIZE)
15320 {
15321 size = allocationSize - offset;
15322 }
15323 else
15324 {
15325 VMA_ASSERT(offset + size <= allocationSize);
15326 }
15327 memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15328
15329 // 2. Adjust to whole block.
15330 const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15331 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15332 const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15333 memRange.offset += allocationOffset;
15334 memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15335
15336 break;
15337 }
15338
15339 default:
15340 VMA_ASSERT(0);
15341 }
15342
15343 switch(op)
15344 {
15345 case VMA_CACHE_FLUSH:
15346 (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15347 break;
15348 case VMA_CACHE_INVALIDATE:
15349 (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15350 break;
15351 default:
15352 VMA_ASSERT(0);
15353 }
15354 }
15355 // else: Just ignore this call.
15356 }
15357
FreeDedicatedMemory(VmaAllocation allocation)15358 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15359 {
15360 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15361
15362 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15363 {
15364 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15365 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15366 VMA_ASSERT(pDedicatedAllocations);
15367 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15368 VMA_ASSERT(success);
15369 }
15370
15371 VkDeviceMemory hMemory = allocation->GetMemory();
15372
15373 /*
15374 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15375 before vkFreeMemory.
15376
15377 if(allocation->GetMappedData() != VMA_NULL)
15378 {
15379 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15380 }
15381 */
15382
15383 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15384
15385 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15386 }
15387
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)15388 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15389 {
15390 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15391 !hAllocation->CanBecomeLost() &&
15392 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15393 {
15394 void* pData = VMA_NULL;
15395 VkResult res = Map(hAllocation, &pData);
15396 if(res == VK_SUCCESS)
15397 {
15398 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15399 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15400 Unmap(hAllocation);
15401 }
15402 else
15403 {
15404 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15405 }
15406 }
15407 }
15408
15409 #if VMA_STATS_STRING_ENABLED
15410
PrintDetailedMap(VmaJsonWriter & json)15411 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15412 {
15413 bool dedicatedAllocationsStarted = false;
15414 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15415 {
15416 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15417 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15418 VMA_ASSERT(pDedicatedAllocVector);
15419 if(pDedicatedAllocVector->empty() == false)
15420 {
15421 if(dedicatedAllocationsStarted == false)
15422 {
15423 dedicatedAllocationsStarted = true;
15424 json.WriteString("DedicatedAllocations");
15425 json.BeginObject();
15426 }
15427
15428 json.BeginString("Type ");
15429 json.ContinueString(memTypeIndex);
15430 json.EndString();
15431
15432 json.BeginArray();
15433
15434 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15435 {
15436 json.BeginObject(true);
15437 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15438 hAlloc->PrintParameters(json);
15439 json.EndObject();
15440 }
15441
15442 json.EndArray();
15443 }
15444 }
15445 if(dedicatedAllocationsStarted)
15446 {
15447 json.EndObject();
15448 }
15449
15450 {
15451 bool allocationsStarted = false;
15452 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15453 {
15454 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15455 {
15456 if(allocationsStarted == false)
15457 {
15458 allocationsStarted = true;
15459 json.WriteString("DefaultPools");
15460 json.BeginObject();
15461 }
15462
15463 json.BeginString("Type ");
15464 json.ContinueString(memTypeIndex);
15465 json.EndString();
15466
15467 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15468 }
15469 }
15470 if(allocationsStarted)
15471 {
15472 json.EndObject();
15473 }
15474 }
15475
15476 // Custom pools
15477 {
15478 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15479 const size_t poolCount = m_Pools.size();
15480 if(poolCount > 0)
15481 {
15482 json.WriteString("Pools");
15483 json.BeginObject();
15484 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15485 {
15486 json.BeginString();
15487 json.ContinueString(m_Pools[poolIndex]->GetId());
15488 json.EndString();
15489
15490 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15491 }
15492 json.EndObject();
15493 }
15494 }
15495 }
15496
15497 #endif // #if VMA_STATS_STRING_ENABLED
15498
15499 ////////////////////////////////////////////////////////////////////////////////
15500 // Public interface
15501
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)15502 VkResult vmaCreateAllocator(
15503 const VmaAllocatorCreateInfo* pCreateInfo,
15504 VmaAllocator* pAllocator)
15505 {
15506 VMA_ASSERT(pCreateInfo && pAllocator);
15507 VMA_DEBUG_LOG("vmaCreateAllocator");
15508 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15509 return (*pAllocator)->Init(pCreateInfo);
15510 }
15511
vmaDestroyAllocator(VmaAllocator allocator)15512 void vmaDestroyAllocator(
15513 VmaAllocator allocator)
15514 {
15515 if(allocator != VK_NULL_HANDLE)
15516 {
15517 VMA_DEBUG_LOG("vmaDestroyAllocator");
15518 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15519 vma_delete(&allocationCallbacks, allocator);
15520 }
15521 }
15522
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)15523 void vmaGetPhysicalDeviceProperties(
15524 VmaAllocator allocator,
15525 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15526 {
15527 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15528 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15529 }
15530
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)15531 void vmaGetMemoryProperties(
15532 VmaAllocator allocator,
15533 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15534 {
15535 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15536 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15537 }
15538
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)15539 void vmaGetMemoryTypeProperties(
15540 VmaAllocator allocator,
15541 uint32_t memoryTypeIndex,
15542 VkMemoryPropertyFlags* pFlags)
15543 {
15544 VMA_ASSERT(allocator && pFlags);
15545 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15546 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15547 }
15548
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)15549 void vmaSetCurrentFrameIndex(
15550 VmaAllocator allocator,
15551 uint32_t frameIndex)
15552 {
15553 VMA_ASSERT(allocator);
15554 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15555
15556 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15557
15558 allocator->SetCurrentFrameIndex(frameIndex);
15559 }
15560
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)15561 void vmaCalculateStats(
15562 VmaAllocator allocator,
15563 VmaStats* pStats)
15564 {
15565 VMA_ASSERT(allocator && pStats);
15566 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15567 allocator->CalculateStats(pStats);
15568 }
15569
15570 #if VMA_STATS_STRING_ENABLED
15571
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)15572 void vmaBuildStatsString(
15573 VmaAllocator allocator,
15574 char** ppStatsString,
15575 VkBool32 detailedMap)
15576 {
15577 VMA_ASSERT(allocator && ppStatsString);
15578 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15579
15580 VmaStringBuilder sb(allocator);
15581 {
15582 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15583 json.BeginObject();
15584
15585 VmaStats stats;
15586 allocator->CalculateStats(&stats);
15587
15588 json.WriteString("Total");
15589 VmaPrintStatInfo(json, stats.total);
15590
15591 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15592 {
15593 json.BeginString("Heap ");
15594 json.ContinueString(heapIndex);
15595 json.EndString();
15596 json.BeginObject();
15597
15598 json.WriteString("Size");
15599 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15600
15601 json.WriteString("Flags");
15602 json.BeginArray(true);
15603 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15604 {
15605 json.WriteString("DEVICE_LOCAL");
15606 }
15607 json.EndArray();
15608
15609 if(stats.memoryHeap[heapIndex].blockCount > 0)
15610 {
15611 json.WriteString("Stats");
15612 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15613 }
15614
15615 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15616 {
15617 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15618 {
15619 json.BeginString("Type ");
15620 json.ContinueString(typeIndex);
15621 json.EndString();
15622
15623 json.BeginObject();
15624
15625 json.WriteString("Flags");
15626 json.BeginArray(true);
15627 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15628 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15629 {
15630 json.WriteString("DEVICE_LOCAL");
15631 }
15632 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15633 {
15634 json.WriteString("HOST_VISIBLE");
15635 }
15636 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15637 {
15638 json.WriteString("HOST_COHERENT");
15639 }
15640 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15641 {
15642 json.WriteString("HOST_CACHED");
15643 }
15644 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15645 {
15646 json.WriteString("LAZILY_ALLOCATED");
15647 }
15648 json.EndArray();
15649
15650 if(stats.memoryType[typeIndex].blockCount > 0)
15651 {
15652 json.WriteString("Stats");
15653 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15654 }
15655
15656 json.EndObject();
15657 }
15658 }
15659
15660 json.EndObject();
15661 }
15662 if(detailedMap == VK_TRUE)
15663 {
15664 allocator->PrintDetailedMap(json);
15665 }
15666
15667 json.EndObject();
15668 }
15669
15670 const size_t len = sb.GetLength();
15671 char* const pChars = vma_new_array(allocator, char, len + 1);
15672 if(len > 0)
15673 {
15674 memcpy(pChars, sb.GetData(), len);
15675 }
15676 pChars[len] = '\0';
15677 *ppStatsString = pChars;
15678 }
15679
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)15680 void vmaFreeStatsString(
15681 VmaAllocator allocator,
15682 char* pStatsString)
15683 {
15684 if(pStatsString != VMA_NULL)
15685 {
15686 VMA_ASSERT(allocator);
15687 size_t len = strlen(pStatsString);
15688 vma_delete_array(allocator, pStatsString, len + 1);
15689 }
15690 }
15691
15692 #endif // #if VMA_STATS_STRING_ENABLED
15693
15694 /*
15695 This function is not protected by any mutex because it just reads immutable data.
15696 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15697 VkResult vmaFindMemoryTypeIndex(
15698 VmaAllocator allocator,
15699 uint32_t memoryTypeBits,
15700 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15701 uint32_t* pMemoryTypeIndex)
15702 {
15703 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15704 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15705 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15706
15707 if(pAllocationCreateInfo->memoryTypeBits != 0)
15708 {
15709 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15710 }
15711
15712 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15713 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15714
15715 const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15716 if(mapped)
15717 {
15718 preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15719 }
15720
15721 // Convert usage to requiredFlags and preferredFlags.
15722 switch(pAllocationCreateInfo->usage)
15723 {
15724 case VMA_MEMORY_USAGE_UNKNOWN:
15725 break;
15726 case VMA_MEMORY_USAGE_GPU_ONLY:
15727 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15728 {
15729 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15730 }
15731 break;
15732 case VMA_MEMORY_USAGE_CPU_ONLY:
15733 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15734 break;
15735 case VMA_MEMORY_USAGE_CPU_TO_GPU:
15736 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15737 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15738 {
15739 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15740 }
15741 break;
15742 case VMA_MEMORY_USAGE_GPU_TO_CPU:
15743 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15744 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15745 break;
15746 default:
15747 break;
15748 }
15749
15750 *pMemoryTypeIndex = UINT32_MAX;
15751 uint32_t minCost = UINT32_MAX;
15752 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15753 memTypeIndex < allocator->GetMemoryTypeCount();
15754 ++memTypeIndex, memTypeBit <<= 1)
15755 {
15756 // This memory type is acceptable according to memoryTypeBits bitmask.
15757 if((memTypeBit & memoryTypeBits) != 0)
15758 {
15759 const VkMemoryPropertyFlags currFlags =
15760 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15761 // This memory type contains requiredFlags.
15762 if((requiredFlags & ~currFlags) == 0)
15763 {
15764 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15765 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15766 // Remember memory type with lowest cost.
15767 if(currCost < minCost)
15768 {
15769 *pMemoryTypeIndex = memTypeIndex;
15770 if(currCost == 0)
15771 {
15772 return VK_SUCCESS;
15773 }
15774 minCost = currCost;
15775 }
15776 }
15777 }
15778 }
15779 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15780 }
15781
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15782 VkResult vmaFindMemoryTypeIndexForBufferInfo(
15783 VmaAllocator allocator,
15784 const VkBufferCreateInfo* pBufferCreateInfo,
15785 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15786 uint32_t* pMemoryTypeIndex)
15787 {
15788 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15789 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15790 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15791 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15792
15793 const VkDevice hDev = allocator->m_hDevice;
15794 VkBuffer hBuffer = VK_NULL_HANDLE;
15795 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15796 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15797 if(res == VK_SUCCESS)
15798 {
15799 VkMemoryRequirements memReq = {};
15800 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15801 hDev, hBuffer, &memReq);
15802
15803 res = vmaFindMemoryTypeIndex(
15804 allocator,
15805 memReq.memoryTypeBits,
15806 pAllocationCreateInfo,
15807 pMemoryTypeIndex);
15808
15809 allocator->GetVulkanFunctions().vkDestroyBuffer(
15810 hDev, hBuffer, allocator->GetAllocationCallbacks());
15811 }
15812 return res;
15813 }
15814
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)15815 VkResult vmaFindMemoryTypeIndexForImageInfo(
15816 VmaAllocator allocator,
15817 const VkImageCreateInfo* pImageCreateInfo,
15818 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15819 uint32_t* pMemoryTypeIndex)
15820 {
15821 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15822 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15823 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15824 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15825
15826 const VkDevice hDev = allocator->m_hDevice;
15827 VkImage hImage = VK_NULL_HANDLE;
15828 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15829 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15830 if(res == VK_SUCCESS)
15831 {
15832 VkMemoryRequirements memReq = {};
15833 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15834 hDev, hImage, &memReq);
15835
15836 res = vmaFindMemoryTypeIndex(
15837 allocator,
15838 memReq.memoryTypeBits,
15839 pAllocationCreateInfo,
15840 pMemoryTypeIndex);
15841
15842 allocator->GetVulkanFunctions().vkDestroyImage(
15843 hDev, hImage, allocator->GetAllocationCallbacks());
15844 }
15845 return res;
15846 }
15847
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15848 VkResult vmaCreatePool(
15849 VmaAllocator allocator,
15850 const VmaPoolCreateInfo* pCreateInfo,
15851 VmaPool* pPool)
15852 {
15853 VMA_ASSERT(allocator && pCreateInfo && pPool);
15854
15855 VMA_DEBUG_LOG("vmaCreatePool");
15856
15857 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15858
15859 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15860
15861 #if VMA_RECORDING_ENABLED
15862 if(allocator->GetRecorder() != VMA_NULL)
15863 {
15864 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15865 }
15866 #endif
15867
15868 return res;
15869 }
15870
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)15871 void vmaDestroyPool(
15872 VmaAllocator allocator,
15873 VmaPool pool)
15874 {
15875 VMA_ASSERT(allocator);
15876
15877 if(pool == VK_NULL_HANDLE)
15878 {
15879 return;
15880 }
15881
15882 VMA_DEBUG_LOG("vmaDestroyPool");
15883
15884 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15885
15886 #if VMA_RECORDING_ENABLED
15887 if(allocator->GetRecorder() != VMA_NULL)
15888 {
15889 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15890 }
15891 #endif
15892
15893 allocator->DestroyPool(pool);
15894 }
15895
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)15896 void vmaGetPoolStats(
15897 VmaAllocator allocator,
15898 VmaPool pool,
15899 VmaPoolStats* pPoolStats)
15900 {
15901 VMA_ASSERT(allocator && pool && pPoolStats);
15902
15903 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15904
15905 allocator->GetPoolStats(pool, pPoolStats);
15906 }
15907
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)15908 void vmaMakePoolAllocationsLost(
15909 VmaAllocator allocator,
15910 VmaPool pool,
15911 size_t* pLostAllocationCount)
15912 {
15913 VMA_ASSERT(allocator && pool);
15914
15915 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15916
15917 #if VMA_RECORDING_ENABLED
15918 if(allocator->GetRecorder() != VMA_NULL)
15919 {
15920 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15921 }
15922 #endif
15923
15924 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15925 }
15926
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)15927 VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15928 {
15929 VMA_ASSERT(allocator && pool);
15930
15931 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15932
15933 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15934
15935 return allocator->CheckPoolCorruption(pool);
15936 }
15937
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)15938 VkResult vmaAllocateMemory(
15939 VmaAllocator allocator,
15940 const VkMemoryRequirements* pVkMemoryRequirements,
15941 const VmaAllocationCreateInfo* pCreateInfo,
15942 VmaAllocation* pAllocation,
15943 VmaAllocationInfo* pAllocationInfo)
15944 {
15945 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15946
15947 VMA_DEBUG_LOG("vmaAllocateMemory");
15948
15949 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15950
15951 VkResult result = allocator->AllocateMemory(
15952 *pVkMemoryRequirements,
15953 false, // requiresDedicatedAllocation
15954 false, // prefersDedicatedAllocation
15955 VK_NULL_HANDLE, // dedicatedBuffer
15956 VK_NULL_HANDLE, // dedicatedImage
15957 *pCreateInfo,
15958 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15959 1, // allocationCount
15960 pAllocation);
15961
15962 #if VMA_RECORDING_ENABLED
15963 if(allocator->GetRecorder() != VMA_NULL)
15964 {
15965 allocator->GetRecorder()->RecordAllocateMemory(
15966 allocator->GetCurrentFrameIndex(),
15967 *pVkMemoryRequirements,
15968 *pCreateInfo,
15969 *pAllocation);
15970 }
15971 #endif
15972
15973 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15974 {
15975 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15976 }
15977
15978 return result;
15979 }
15980
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)15981 VkResult vmaAllocateMemoryPages(
15982 VmaAllocator allocator,
15983 const VkMemoryRequirements* pVkMemoryRequirements,
15984 const VmaAllocationCreateInfo* pCreateInfo,
15985 size_t allocationCount,
15986 VmaAllocation* pAllocations,
15987 VmaAllocationInfo* pAllocationInfo)
15988 {
15989 if(allocationCount == 0)
15990 {
15991 return VK_SUCCESS;
15992 }
15993
15994 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15995
15996 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15997
15998 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15999
16000 VkResult result = allocator->AllocateMemory(
16001 *pVkMemoryRequirements,
16002 false, // requiresDedicatedAllocation
16003 false, // prefersDedicatedAllocation
16004 VK_NULL_HANDLE, // dedicatedBuffer
16005 VK_NULL_HANDLE, // dedicatedImage
16006 *pCreateInfo,
16007 VMA_SUBALLOCATION_TYPE_UNKNOWN,
16008 allocationCount,
16009 pAllocations);
16010
16011 #if VMA_RECORDING_ENABLED
16012 if(allocator->GetRecorder() != VMA_NULL)
16013 {
16014 allocator->GetRecorder()->RecordAllocateMemoryPages(
16015 allocator->GetCurrentFrameIndex(),
16016 *pVkMemoryRequirements,
16017 *pCreateInfo,
16018 (uint64_t)allocationCount,
16019 pAllocations);
16020 }
16021 #endif
16022
16023 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16024 {
16025 for(size_t i = 0; i < allocationCount; ++i)
16026 {
16027 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16028 }
16029 }
16030
16031 return result;
16032 }
16033
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16034 VkResult vmaAllocateMemoryForBuffer(
16035 VmaAllocator allocator,
16036 VkBuffer buffer,
16037 const VmaAllocationCreateInfo* pCreateInfo,
16038 VmaAllocation* pAllocation,
16039 VmaAllocationInfo* pAllocationInfo)
16040 {
16041 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16042
16043 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16044
16045 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16046
16047 VkMemoryRequirements vkMemReq = {};
16048 bool requiresDedicatedAllocation = false;
16049 bool prefersDedicatedAllocation = false;
16050 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16051 requiresDedicatedAllocation,
16052 prefersDedicatedAllocation);
16053
16054 VkResult result = allocator->AllocateMemory(
16055 vkMemReq,
16056 requiresDedicatedAllocation,
16057 prefersDedicatedAllocation,
16058 buffer, // dedicatedBuffer
16059 VK_NULL_HANDLE, // dedicatedImage
16060 *pCreateInfo,
16061 VMA_SUBALLOCATION_TYPE_BUFFER,
16062 1, // allocationCount
16063 pAllocation);
16064
16065 #if VMA_RECORDING_ENABLED
16066 if(allocator->GetRecorder() != VMA_NULL)
16067 {
16068 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16069 allocator->GetCurrentFrameIndex(),
16070 vkMemReq,
16071 requiresDedicatedAllocation,
16072 prefersDedicatedAllocation,
16073 *pCreateInfo,
16074 *pAllocation);
16075 }
16076 #endif
16077
16078 if(pAllocationInfo && result == VK_SUCCESS)
16079 {
16080 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16081 }
16082
16083 return result;
16084 }
16085
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16086 VkResult vmaAllocateMemoryForImage(
16087 VmaAllocator allocator,
16088 VkImage image,
16089 const VmaAllocationCreateInfo* pCreateInfo,
16090 VmaAllocation* pAllocation,
16091 VmaAllocationInfo* pAllocationInfo)
16092 {
16093 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16094
16095 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16096
16097 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16098
16099 VkMemoryRequirements vkMemReq = {};
16100 bool requiresDedicatedAllocation = false;
16101 bool prefersDedicatedAllocation = false;
16102 allocator->GetImageMemoryRequirements(image, vkMemReq,
16103 requiresDedicatedAllocation, prefersDedicatedAllocation);
16104
16105 VkResult result = allocator->AllocateMemory(
16106 vkMemReq,
16107 requiresDedicatedAllocation,
16108 prefersDedicatedAllocation,
16109 VK_NULL_HANDLE, // dedicatedBuffer
16110 image, // dedicatedImage
16111 *pCreateInfo,
16112 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16113 1, // allocationCount
16114 pAllocation);
16115
16116 #if VMA_RECORDING_ENABLED
16117 if(allocator->GetRecorder() != VMA_NULL)
16118 {
16119 allocator->GetRecorder()->RecordAllocateMemoryForImage(
16120 allocator->GetCurrentFrameIndex(),
16121 vkMemReq,
16122 requiresDedicatedAllocation,
16123 prefersDedicatedAllocation,
16124 *pCreateInfo,
16125 *pAllocation);
16126 }
16127 #endif
16128
16129 if(pAllocationInfo && result == VK_SUCCESS)
16130 {
16131 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16132 }
16133
16134 return result;
16135 }
16136
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)16137 void vmaFreeMemory(
16138 VmaAllocator allocator,
16139 VmaAllocation allocation)
16140 {
16141 VMA_ASSERT(allocator);
16142
16143 if(allocation == VK_NULL_HANDLE)
16144 {
16145 return;
16146 }
16147
16148 VMA_DEBUG_LOG("vmaFreeMemory");
16149
16150 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16151
16152 #if VMA_RECORDING_ENABLED
16153 if(allocator->GetRecorder() != VMA_NULL)
16154 {
16155 allocator->GetRecorder()->RecordFreeMemory(
16156 allocator->GetCurrentFrameIndex(),
16157 allocation);
16158 }
16159 #endif
16160
16161 allocator->FreeMemory(
16162 1, // allocationCount
16163 &allocation);
16164 }
16165
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,VmaAllocation * pAllocations)16166 void vmaFreeMemoryPages(
16167 VmaAllocator allocator,
16168 size_t allocationCount,
16169 VmaAllocation* pAllocations)
16170 {
16171 if(allocationCount == 0)
16172 {
16173 return;
16174 }
16175
16176 VMA_ASSERT(allocator);
16177
16178 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16179
16180 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16181
16182 #if VMA_RECORDING_ENABLED
16183 if(allocator->GetRecorder() != VMA_NULL)
16184 {
16185 allocator->GetRecorder()->RecordFreeMemoryPages(
16186 allocator->GetCurrentFrameIndex(),
16187 (uint64_t)allocationCount,
16188 pAllocations);
16189 }
16190 #endif
16191
16192 allocator->FreeMemory(allocationCount, pAllocations);
16193 }
16194
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)16195 VkResult vmaResizeAllocation(
16196 VmaAllocator allocator,
16197 VmaAllocation allocation,
16198 VkDeviceSize newSize)
16199 {
16200 VMA_ASSERT(allocator && allocation);
16201
16202 VMA_DEBUG_LOG("vmaResizeAllocation");
16203
16204 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16205
16206 #if VMA_RECORDING_ENABLED
16207 if(allocator->GetRecorder() != VMA_NULL)
16208 {
16209 allocator->GetRecorder()->RecordResizeAllocation(
16210 allocator->GetCurrentFrameIndex(),
16211 allocation,
16212 newSize);
16213 }
16214 #endif
16215
16216 return allocator->ResizeAllocation(allocation, newSize);
16217 }
16218
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)16219 void vmaGetAllocationInfo(
16220 VmaAllocator allocator,
16221 VmaAllocation allocation,
16222 VmaAllocationInfo* pAllocationInfo)
16223 {
16224 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16225
16226 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16227
16228 #if VMA_RECORDING_ENABLED
16229 if(allocator->GetRecorder() != VMA_NULL)
16230 {
16231 allocator->GetRecorder()->RecordGetAllocationInfo(
16232 allocator->GetCurrentFrameIndex(),
16233 allocation);
16234 }
16235 #endif
16236
16237 allocator->GetAllocationInfo(allocation, pAllocationInfo);
16238 }
16239
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)16240 VkBool32 vmaTouchAllocation(
16241 VmaAllocator allocator,
16242 VmaAllocation allocation)
16243 {
16244 VMA_ASSERT(allocator && allocation);
16245
16246 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16247
16248 #if VMA_RECORDING_ENABLED
16249 if(allocator->GetRecorder() != VMA_NULL)
16250 {
16251 allocator->GetRecorder()->RecordTouchAllocation(
16252 allocator->GetCurrentFrameIndex(),
16253 allocation);
16254 }
16255 #endif
16256
16257 return allocator->TouchAllocation(allocation);
16258 }
16259
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)16260 void vmaSetAllocationUserData(
16261 VmaAllocator allocator,
16262 VmaAllocation allocation,
16263 void* pUserData)
16264 {
16265 VMA_ASSERT(allocator && allocation);
16266
16267 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16268
16269 allocation->SetUserData(allocator, pUserData);
16270
16271 #if VMA_RECORDING_ENABLED
16272 if(allocator->GetRecorder() != VMA_NULL)
16273 {
16274 allocator->GetRecorder()->RecordSetAllocationUserData(
16275 allocator->GetCurrentFrameIndex(),
16276 allocation,
16277 pUserData);
16278 }
16279 #endif
16280 }
16281
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)16282 void vmaCreateLostAllocation(
16283 VmaAllocator allocator,
16284 VmaAllocation* pAllocation)
16285 {
16286 VMA_ASSERT(allocator && pAllocation);
16287
16288 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16289
16290 allocator->CreateLostAllocation(pAllocation);
16291
16292 #if VMA_RECORDING_ENABLED
16293 if(allocator->GetRecorder() != VMA_NULL)
16294 {
16295 allocator->GetRecorder()->RecordCreateLostAllocation(
16296 allocator->GetCurrentFrameIndex(),
16297 *pAllocation);
16298 }
16299 #endif
16300 }
16301
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)16302 VkResult vmaMapMemory(
16303 VmaAllocator allocator,
16304 VmaAllocation allocation,
16305 void** ppData)
16306 {
16307 VMA_ASSERT(allocator && allocation && ppData);
16308
16309 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16310
16311 VkResult res = allocator->Map(allocation, ppData);
16312
16313 #if VMA_RECORDING_ENABLED
16314 if(allocator->GetRecorder() != VMA_NULL)
16315 {
16316 allocator->GetRecorder()->RecordMapMemory(
16317 allocator->GetCurrentFrameIndex(),
16318 allocation);
16319 }
16320 #endif
16321
16322 return res;
16323 }
16324
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)16325 void vmaUnmapMemory(
16326 VmaAllocator allocator,
16327 VmaAllocation allocation)
16328 {
16329 VMA_ASSERT(allocator && allocation);
16330
16331 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16332
16333 #if VMA_RECORDING_ENABLED
16334 if(allocator->GetRecorder() != VMA_NULL)
16335 {
16336 allocator->GetRecorder()->RecordUnmapMemory(
16337 allocator->GetCurrentFrameIndex(),
16338 allocation);
16339 }
16340 #endif
16341
16342 allocator->Unmap(allocation);
16343 }
16344
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16345 void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16346 {
16347 VMA_ASSERT(allocator && allocation);
16348
16349 VMA_DEBUG_LOG("vmaFlushAllocation");
16350
16351 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16352
16353 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16354
16355 #if VMA_RECORDING_ENABLED
16356 if(allocator->GetRecorder() != VMA_NULL)
16357 {
16358 allocator->GetRecorder()->RecordFlushAllocation(
16359 allocator->GetCurrentFrameIndex(),
16360 allocation, offset, size);
16361 }
16362 #endif
16363 }
16364
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16365 void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16366 {
16367 VMA_ASSERT(allocator && allocation);
16368
16369 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16370
16371 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16372
16373 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16374
16375 #if VMA_RECORDING_ENABLED
16376 if(allocator->GetRecorder() != VMA_NULL)
16377 {
16378 allocator->GetRecorder()->RecordInvalidateAllocation(
16379 allocator->GetCurrentFrameIndex(),
16380 allocation, offset, size);
16381 }
16382 #endif
16383 }
16384
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)16385 VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16386 {
16387 VMA_ASSERT(allocator);
16388
16389 VMA_DEBUG_LOG("vmaCheckCorruption");
16390
16391 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16392
16393 return allocator->CheckCorruption(memoryTypeBits);
16394 }
16395
vmaDefragment(VmaAllocator allocator,VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)16396 VkResult vmaDefragment(
16397 VmaAllocator allocator,
16398 VmaAllocation* pAllocations,
16399 size_t allocationCount,
16400 VkBool32* pAllocationsChanged,
16401 const VmaDefragmentationInfo *pDefragmentationInfo,
16402 VmaDefragmentationStats* pDefragmentationStats)
16403 {
16404 // Deprecated interface, reimplemented using new one.
16405
16406 VmaDefragmentationInfo2 info2 = {};
16407 info2.allocationCount = (uint32_t)allocationCount;
16408 info2.pAllocations = pAllocations;
16409 info2.pAllocationsChanged = pAllocationsChanged;
16410 if(pDefragmentationInfo != VMA_NULL)
16411 {
16412 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16413 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16414 }
16415 else
16416 {
16417 info2.maxCpuAllocationsToMove = UINT32_MAX;
16418 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16419 }
16420 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16421
16422 VmaDefragmentationContext ctx;
16423 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16424 if(res == VK_NOT_READY)
16425 {
16426 res = vmaDefragmentationEnd( allocator, ctx);
16427 }
16428 return res;
16429 }
16430
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16431 VkResult vmaDefragmentationBegin(
16432 VmaAllocator allocator,
16433 const VmaDefragmentationInfo2* pInfo,
16434 VmaDefragmentationStats* pStats,
16435 VmaDefragmentationContext *pContext)
16436 {
16437 VMA_ASSERT(allocator && pInfo && pContext);
16438
16439 // Degenerate case: Nothing to defragment.
16440 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16441 {
16442 return VK_SUCCESS;
16443 }
16444
16445 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16446 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16447 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16448 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16449
16450 VMA_DEBUG_LOG("vmaDefragmentationBegin");
16451
16452 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16453
16454 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16455
16456 #if VMA_RECORDING_ENABLED
16457 if(allocator->GetRecorder() != VMA_NULL)
16458 {
16459 allocator->GetRecorder()->RecordDefragmentationBegin(
16460 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16461 }
16462 #endif
16463
16464 return res;
16465 }
16466
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)16467 VkResult vmaDefragmentationEnd(
16468 VmaAllocator allocator,
16469 VmaDefragmentationContext context)
16470 {
16471 VMA_ASSERT(allocator);
16472
16473 VMA_DEBUG_LOG("vmaDefragmentationEnd");
16474
16475 if(context != VK_NULL_HANDLE)
16476 {
16477 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16478
16479 #if VMA_RECORDING_ENABLED
16480 if(allocator->GetRecorder() != VMA_NULL)
16481 {
16482 allocator->GetRecorder()->RecordDefragmentationEnd(
16483 allocator->GetCurrentFrameIndex(), context);
16484 }
16485 #endif
16486
16487 return allocator->DefragmentationEnd(context);
16488 }
16489 else
16490 {
16491 return VK_SUCCESS;
16492 }
16493 }
16494
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)16495 VkResult vmaBindBufferMemory(
16496 VmaAllocator allocator,
16497 VmaAllocation allocation,
16498 VkBuffer buffer)
16499 {
16500 VMA_ASSERT(allocator && allocation && buffer);
16501
16502 VMA_DEBUG_LOG("vmaBindBufferMemory");
16503
16504 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16505
16506 return allocator->BindBufferMemory(allocation, buffer);
16507 }
16508
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)16509 VkResult vmaBindImageMemory(
16510 VmaAllocator allocator,
16511 VmaAllocation allocation,
16512 VkImage image)
16513 {
16514 VMA_ASSERT(allocator && allocation && image);
16515
16516 VMA_DEBUG_LOG("vmaBindImageMemory");
16517
16518 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16519
16520 return allocator->BindImageMemory(allocation, image);
16521 }
16522
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16523 VkResult vmaCreateBuffer(
16524 VmaAllocator allocator,
16525 const VkBufferCreateInfo* pBufferCreateInfo,
16526 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16527 VkBuffer* pBuffer,
16528 VmaAllocation* pAllocation,
16529 VmaAllocationInfo* pAllocationInfo)
16530 {
16531 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16532
16533 if(pBufferCreateInfo->size == 0)
16534 {
16535 return VK_ERROR_VALIDATION_FAILED_EXT;
16536 }
16537
16538 VMA_DEBUG_LOG("vmaCreateBuffer");
16539
16540 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16541
16542 *pBuffer = VK_NULL_HANDLE;
16543 *pAllocation = VK_NULL_HANDLE;
16544
16545 // 1. Create VkBuffer.
16546 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16547 allocator->m_hDevice,
16548 pBufferCreateInfo,
16549 allocator->GetAllocationCallbacks(),
16550 pBuffer);
16551 if(res >= 0)
16552 {
16553 // 2. vkGetBufferMemoryRequirements.
16554 VkMemoryRequirements vkMemReq = {};
16555 bool requiresDedicatedAllocation = false;
16556 bool prefersDedicatedAllocation = false;
16557 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16558 requiresDedicatedAllocation, prefersDedicatedAllocation);
16559
16560 // Make sure alignment requirements for specific buffer usages reported
16561 // in Physical Device Properties are included in alignment reported by memory requirements.
16562 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16563 {
16564 VMA_ASSERT(vkMemReq.alignment %
16565 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16566 }
16567 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16568 {
16569 VMA_ASSERT(vkMemReq.alignment %
16570 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16571 }
16572 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16573 {
16574 VMA_ASSERT(vkMemReq.alignment %
16575 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16576 }
16577
16578 // 3. Allocate memory using allocator.
16579 res = allocator->AllocateMemory(
16580 vkMemReq,
16581 requiresDedicatedAllocation,
16582 prefersDedicatedAllocation,
16583 *pBuffer, // dedicatedBuffer
16584 VK_NULL_HANDLE, // dedicatedImage
16585 *pAllocationCreateInfo,
16586 VMA_SUBALLOCATION_TYPE_BUFFER,
16587 1, // allocationCount
16588 pAllocation);
16589
16590 #if VMA_RECORDING_ENABLED
16591 if(allocator->GetRecorder() != VMA_NULL)
16592 {
16593 allocator->GetRecorder()->RecordCreateBuffer(
16594 allocator->GetCurrentFrameIndex(),
16595 *pBufferCreateInfo,
16596 *pAllocationCreateInfo,
16597 *pAllocation);
16598 }
16599 #endif
16600
16601 if(res >= 0)
16602 {
16603 // 3. Bind buffer with memory.
16604 res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16605 if(res >= 0)
16606 {
16607 // All steps succeeded.
16608 #if VMA_STATS_STRING_ENABLED
16609 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16610 #endif
16611 if(pAllocationInfo != VMA_NULL)
16612 {
16613 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16614 }
16615
16616 return VK_SUCCESS;
16617 }
16618 allocator->FreeMemory(
16619 1, // allocationCount
16620 pAllocation);
16621 *pAllocation = VK_NULL_HANDLE;
16622 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16623 *pBuffer = VK_NULL_HANDLE;
16624 return res;
16625 }
16626 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16627 *pBuffer = VK_NULL_HANDLE;
16628 return res;
16629 }
16630 return res;
16631 }
16632
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)16633 void vmaDestroyBuffer(
16634 VmaAllocator allocator,
16635 VkBuffer buffer,
16636 VmaAllocation allocation)
16637 {
16638 VMA_ASSERT(allocator);
16639
16640 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16641 {
16642 return;
16643 }
16644
16645 VMA_DEBUG_LOG("vmaDestroyBuffer");
16646
16647 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16648
16649 #if VMA_RECORDING_ENABLED
16650 if(allocator->GetRecorder() != VMA_NULL)
16651 {
16652 allocator->GetRecorder()->RecordDestroyBuffer(
16653 allocator->GetCurrentFrameIndex(),
16654 allocation);
16655 }
16656 #endif
16657
16658 if(buffer != VK_NULL_HANDLE)
16659 {
16660 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16661 }
16662
16663 if(allocation != VK_NULL_HANDLE)
16664 {
16665 allocator->FreeMemory(
16666 1, // allocationCount
16667 &allocation);
16668 }
16669 }
16670
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16671 VkResult vmaCreateImage(
16672 VmaAllocator allocator,
16673 const VkImageCreateInfo* pImageCreateInfo,
16674 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16675 VkImage* pImage,
16676 VmaAllocation* pAllocation,
16677 VmaAllocationInfo* pAllocationInfo)
16678 {
16679 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16680
16681 if(pImageCreateInfo->extent.width == 0 ||
16682 pImageCreateInfo->extent.height == 0 ||
16683 pImageCreateInfo->extent.depth == 0 ||
16684 pImageCreateInfo->mipLevels == 0 ||
16685 pImageCreateInfo->arrayLayers == 0)
16686 {
16687 return VK_ERROR_VALIDATION_FAILED_EXT;
16688 }
16689
16690 VMA_DEBUG_LOG("vmaCreateImage");
16691
16692 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16693
16694 *pImage = VK_NULL_HANDLE;
16695 *pAllocation = VK_NULL_HANDLE;
16696
16697 // 1. Create VkImage.
16698 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16699 allocator->m_hDevice,
16700 pImageCreateInfo,
16701 allocator->GetAllocationCallbacks(),
16702 pImage);
16703 if(res >= 0)
16704 {
16705 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16706 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16707 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16708
16709 // 2. Allocate memory using allocator.
16710 VkMemoryRequirements vkMemReq = {};
16711 bool requiresDedicatedAllocation = false;
16712 bool prefersDedicatedAllocation = false;
16713 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16714 requiresDedicatedAllocation, prefersDedicatedAllocation);
16715
16716 res = allocator->AllocateMemory(
16717 vkMemReq,
16718 requiresDedicatedAllocation,
16719 prefersDedicatedAllocation,
16720 VK_NULL_HANDLE, // dedicatedBuffer
16721 *pImage, // dedicatedImage
16722 *pAllocationCreateInfo,
16723 suballocType,
16724 1, // allocationCount
16725 pAllocation);
16726
16727 #if VMA_RECORDING_ENABLED
16728 if(allocator->GetRecorder() != VMA_NULL)
16729 {
16730 allocator->GetRecorder()->RecordCreateImage(
16731 allocator->GetCurrentFrameIndex(),
16732 *pImageCreateInfo,
16733 *pAllocationCreateInfo,
16734 *pAllocation);
16735 }
16736 #endif
16737
16738 if(res >= 0)
16739 {
16740 // 3. Bind image with memory.
16741 res = allocator->BindImageMemory(*pAllocation, *pImage);
16742 if(res >= 0)
16743 {
16744 // All steps succeeded.
16745 #if VMA_STATS_STRING_ENABLED
16746 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16747 #endif
16748 if(pAllocationInfo != VMA_NULL)
16749 {
16750 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16751 }
16752
16753 return VK_SUCCESS;
16754 }
16755 allocator->FreeMemory(
16756 1, // allocationCount
16757 pAllocation);
16758 *pAllocation = VK_NULL_HANDLE;
16759 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16760 *pImage = VK_NULL_HANDLE;
16761 return res;
16762 }
16763 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16764 *pImage = VK_NULL_HANDLE;
16765 return res;
16766 }
16767 return res;
16768 }
16769
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)16770 void vmaDestroyImage(
16771 VmaAllocator allocator,
16772 VkImage image,
16773 VmaAllocation allocation)
16774 {
16775 VMA_ASSERT(allocator);
16776
16777 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16778 {
16779 return;
16780 }
16781
16782 VMA_DEBUG_LOG("vmaDestroyImage");
16783
16784 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16785
16786 #if VMA_RECORDING_ENABLED
16787 if(allocator->GetRecorder() != VMA_NULL)
16788 {
16789 allocator->GetRecorder()->RecordDestroyImage(
16790 allocator->GetCurrentFrameIndex(),
16791 allocation);
16792 }
16793 #endif
16794
16795 if(image != VK_NULL_HANDLE)
16796 {
16797 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16798 }
16799 if(allocation != VK_NULL_HANDLE)
16800 {
16801 allocator->FreeMemory(
16802 1, // allocationCount
16803 &allocation);
16804 }
16805 }
16806 #if defined(__GNUC__)
16807 #pragma GCC diagnostic pop
16808 #if defined(__clang__)
16809 #pragma clang diagnostic pop
16810 #endif
16811 #endif
16812 #endif // #ifdef VMA_IMPLEMENTATION
16813 // clang-format on
16814