1 //
2 // Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25
26 /** \mainpage Vulkan Memory Allocator
27
28 <b>Version 3.0.0-development</b> (2020-03-23)
29
30 Copyright (c) 2017-2020 Advanced Micro Devices, Inc. All rights reserved. \n
31 License: MIT
32
33 Documentation of all members: vk_mem_alloc.h
34
35 \section main_table_of_contents Table of contents
36
37 - <b>User guide</b>
38 - \subpage quick_start
39 - [Project setup](@ref quick_start_project_setup)
40 - [Initialization](@ref quick_start_initialization)
41 - [Resource allocation](@ref quick_start_resource_allocation)
42 - \subpage choosing_memory_type
43 - [Usage](@ref choosing_memory_type_usage)
44 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
45 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
46 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
47 - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
48 - \subpage memory_mapping
49 - [Mapping functions](@ref memory_mapping_mapping_functions)
50 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
51 - [Cache flush and invalidate](@ref memory_mapping_cache_control)
52 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
53 - \subpage staying_within_budget
54 - [Querying for budget](@ref staying_within_budget_querying_for_budget)
55 - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
56 - \subpage custom_memory_pools
57 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58 - [Linear allocation algorithm](@ref linear_algorithm)
59 - [Free-at-once](@ref linear_algorithm_free_at_once)
60 - [Stack](@ref linear_algorithm_stack)
61 - [Double stack](@ref linear_algorithm_double_stack)
62 - [Ring buffer](@ref linear_algorithm_ring_buffer)
63 - [Buddy allocation algorithm](@ref buddy_algorithm)
64 - \subpage defragmentation
65 - [Defragmenting CPU memory](@ref defragmentation_cpu)
66 - [Defragmenting GPU memory](@ref defragmentation_gpu)
67 - [Additional notes](@ref defragmentation_additional_notes)
68 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
69 - \subpage lost_allocations
70 - \subpage statistics
71 - [Numeric statistics](@ref statistics_numeric_statistics)
72 - [JSON dump](@ref statistics_json_dump)
73 - \subpage allocation_annotation
74 - [Allocation user data](@ref allocation_user_data)
75 - [Allocation names](@ref allocation_names)
76 - \subpage debugging_memory_usage
77 - [Memory initialization](@ref debugging_memory_usage_initialization)
78 - [Margins](@ref debugging_memory_usage_margins)
79 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
80 - \subpage record_and_replay
81 - \subpage usage_patterns
82 - [Common mistakes](@ref usage_patterns_common_mistakes)
83 - [Simple patterns](@ref usage_patterns_simple)
84 - [Advanced patterns](@ref usage_patterns_advanced)
85 - \subpage configuration
86 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
87 - [Custom host memory allocator](@ref custom_memory_allocator)
88 - [Device memory allocation callbacks](@ref allocation_callbacks)
89 - [Device heap memory limit](@ref heap_memory_limit)
90 - \subpage vk_khr_dedicated_allocation
91 - \subpage enabling_buffer_device_address
92 - \subpage vk_amd_device_coherent_memory
93 - \subpage general_considerations
94 - [Thread safety](@ref general_considerations_thread_safety)
95 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
96 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
97 - [Features not supported](@ref general_considerations_features_not_supported)
98
99 \section main_see_also See also
100
101 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
102 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
103
104
105
106
107 \page quick_start Quick start
108
109 \section quick_start_project_setup Project setup
110
111 Vulkan Memory Allocator comes in form of a "stb-style" single header file.
112 You don't need to build it as a separate library project.
113 You can add this file directly to your project and submit it to code repository next to your other source files.
114
115 "Single header" doesn't mean that everything is contained in C/C++ declarations,
116 like it tends to be in case of inline functions or C++ templates.
117 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
118 If you don't do it properly, you will get linker errors.
119
120 To do it properly:
121
122 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
123 This includes declarations of all members of the library.
124 -# In exacly one CPP file define following macro before this include.
125 It enables also internal definitions.
126
127 \code
128 #define VMA_IMPLEMENTATION
129 #include "vk_mem_alloc.h"
130 \endcode
131
132 It may be a good idea to create dedicated CPP file just for this purpose.
133
134 Note on language: This library is written in C++, but has C-compatible interface.
135 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
136 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
137
138 Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
139 includes `<windows.h>` on Windows. If you need some specific macros defined
140 before including these headers (like `WIN32_LEAN_AND_MEAN` or
141 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
142 them before every `#include` of this library.
143
144
145 \section quick_start_initialization Initialization
146
147 At program startup:
148
149 -# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.
150 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
151 calling vmaCreateAllocator().
152
153 \code
154 VmaAllocatorCreateInfo allocatorInfo = {};
155 allocatorInfo.physicalDevice = physicalDevice;
156 allocatorInfo.device = device;
157 allocatorInfo.instance = instance;
158
159 VmaAllocator allocator;
160 vmaCreateAllocator(&allocatorInfo, &allocator);
161 \endcode
162
163 \section quick_start_resource_allocation Resource allocation
164
165 When you want to create a buffer or image:
166
167 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
168 -# Fill VmaAllocationCreateInfo structure.
169 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
170 already allocated and bound to it.
171
172 \code
173 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
174 bufferInfo.size = 65536;
175 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
176
177 VmaAllocationCreateInfo allocInfo = {};
178 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
179
180 VkBuffer buffer;
181 VmaAllocation allocation;
182 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
183 \endcode
184
185 Don't forget to destroy your objects when no longer needed:
186
187 \code
188 vmaDestroyBuffer(allocator, buffer, allocation);
189 vmaDestroyAllocator(allocator);
190 \endcode
191
192
193 \page choosing_memory_type Choosing memory type
194
195 Physical devices in Vulkan support various combinations of memory heaps and
196 types. Help with choosing correct and optimal memory type for your specific
197 resource is one of the key features of this library. You can use it by filling
198 appropriate members of VmaAllocationCreateInfo structure, as described below.
199 You can also combine multiple methods.
200
201 -# If you just want to find memory type index that meets your requirements, you
202 can use function: vmaFindMemoryTypeIndex(), vmaFindMemoryTypeIndexForBufferInfo(),
203 vmaFindMemoryTypeIndexForImageInfo().
204 -# If you want to allocate a region of device memory without association with any
205 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
206 this function is not recommended and usually not needed.
207 vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
208 which may be useful for sparse binding.
209 -# If you already have a buffer or an image created, you want to allocate memory
210 for it and then you will bind it yourself, you can use function
211 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
212 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
213 or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
214 -# If you want to create a buffer or an image, allocate memory for it and bind
215 them together, all in one call, you can use function vmaCreateBuffer(),
216 vmaCreateImage(). This is the easiest and recommended way to use this library.
217
218 When using 3. or 4., the library internally queries Vulkan for memory types
219 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
220 and uses only one of these types.
221
222 If no memory type can be found that meets all the requirements, these functions
223 return `VK_ERROR_FEATURE_NOT_PRESENT`.
224
225 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
226 It means no requirements are specified for memory type.
227 It is valid, although not very useful.
228
229 \section choosing_memory_type_usage Usage
230
231 The easiest way to specify memory requirements is to fill member
232 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
233 It defines high level, common usage types.
234 For more details, see description of this enum.
235
236 For example, if you want to create a uniform buffer that will be filled using
237 transfer only once or infrequently and used for rendering every frame, you can
238 do it using following code:
239
240 \code
241 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
242 bufferInfo.size = 65536;
243 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
244
245 VmaAllocationCreateInfo allocInfo = {};
246 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
247
248 VkBuffer buffer;
249 VmaAllocation allocation;
250 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
251 \endcode
252
253 \section choosing_memory_type_required_preferred_flags Required and preferred flags
254
255 You can specify more detailed requirements by filling members
256 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
257 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
258 if you want to create a buffer that will be persistently mapped on host (so it
259 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
260 use following code:
261
262 \code
263 VmaAllocationCreateInfo allocInfo = {};
264 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
265 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
266 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
267
268 VkBuffer buffer;
269 VmaAllocation allocation;
270 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
271 \endcode
272
273 A memory type is chosen that has all the required flags and as many preferred
274 flags set as possible.
275
276 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
277 a set of required and preferred flags.
278
279 \section choosing_memory_type_explicit_memory_types Explicit memory types
280
281 If you inspected memory types available on the physical device and you have
282 a preference for memory types that you want to use, you can fill member
283 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
284 means that a memory type with that index is allowed to be used for the
285 allocation. Special value 0, just like `UINT32_MAX`, means there are no
286 restrictions to memory type index.
287
288 Please note that this member is NOT just a memory type index.
289 Still you can use it to choose just one, specific memory type.
290 For example, if you already determined that your buffer should be created in
291 memory type 2, use following code:
292
293 \code
294 uint32_t memoryTypeIndex = 2;
295
296 VmaAllocationCreateInfo allocInfo = {};
297 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
298
299 VkBuffer buffer;
300 VmaAllocation allocation;
301 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
302 \endcode
303
304 \section choosing_memory_type_custom_memory_pools Custom memory pools
305
306 If you allocate from custom memory pool, all the ways of specifying memory
307 requirements described above are not applicable and the aforementioned members
308 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
309 explicitly when creating the pool and then used to make all the allocations from
310 that pool. For further details, see \ref custom_memory_pools.
311
312 \section choosing_memory_type_dedicated_allocations Dedicated allocations
313
314 Memory for allocations is reserved out of larger block of `VkDeviceMemory`
315 allocated from Vulkan internally. That's the main feature of this whole library.
316 You can still request a separate memory block to be created for an allocation,
317 just like you would do in a trivial solution without using any allocator.
318 In that case, a buffer or image is always bound to that memory at offset 0.
319 This is called a "dedicated allocation".
320 You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
321 The library can also internally decide to use dedicated allocation in some cases, e.g.:
322
323 - When the size of the allocation is large.
324 - When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
325 and it reports that dedicated allocation is required or recommended for the resource.
326 - When allocation of next big memory block fails due to not enough device memory,
327 but allocation with the exact requested size succeeds.
328
329
330 \page memory_mapping Memory mapping
331
332 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
333 to be able to read from it or write to it in CPU code.
334 Mapping is possible only of memory allocated from a memory type that has
335 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
336 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
337 You can use them directly with memory allocated by this library,
338 but it is not recommended because of following issue:
339 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
340 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
341 Because of this, Vulkan Memory Allocator provides following facilities:
342
343 \section memory_mapping_mapping_functions Mapping functions
344
345 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
346 They are safer and more convenient to use than standard Vulkan functions.
347 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
348 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
349 The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
350 For further details, see description of vmaMapMemory() function.
351 Example:
352
353 \code
354 // Having these objects initialized:
355
356 struct ConstantBuffer
357 {
358 ...
359 };
360 ConstantBuffer constantBufferData;
361
362 VmaAllocator allocator;
363 VkBuffer constantBuffer;
364 VmaAllocation constantBufferAllocation;
365
366 // You can map and fill your buffer using following code:
367
368 void* mappedData;
369 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
370 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
371 vmaUnmapMemory(allocator, constantBufferAllocation);
372 \endcode
373
374 When mapping, you may see a warning from Vulkan validation layer similar to this one:
375
376 <i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>
377
378 It happens because the library maps entire `VkDeviceMemory` block, where different
379 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
380 You can safely ignore it if you are sure you access only memory of the intended
381 object that you wanted to map.
382
383
384 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
385
386 Kepping your memory persistently mapped is generally OK in Vulkan.
387 You don't need to unmap it before using its data on the GPU.
388 The library provides a special feature designed for that:
389 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
390 VmaAllocationCreateInfo::flags stay mapped all the time,
391 so you can just access CPU pointer to it any time
392 without a need to call any "map" or "unmap" function.
393 Example:
394
395 \code
396 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
397 bufCreateInfo.size = sizeof(ConstantBuffer);
398 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
399
400 VmaAllocationCreateInfo allocCreateInfo = {};
401 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
402 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
403
404 VkBuffer buf;
405 VmaAllocation alloc;
406 VmaAllocationInfo allocInfo;
407 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
408
409 // Buffer is already mapped. You can access its memory.
410 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
411 \endcode
412
413 There are some exceptions though, when you should consider mapping memory only for a short period of time:
414
415 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
416 device is discrete AMD GPU,
417 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
418 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
419 then whenever a memory block allocated from this memory type stays mapped
420 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
421 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
422 matter if that particular memory block is actually used by the command buffer
423 being submitted.
424 - On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
425 which requires unmapping before GPU can see updated texture.
426 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
427
428 \section memory_mapping_cache_control Cache flush and invalidate
429
430 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
431 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
432 you need to manually **invalidate** cache before reading of mapped pointer
433 and **flush** cache after writing to mapped pointer.
434 Map/unmap operations don't do that automatically.
435 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
436 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
437 functions that refer to given allocation object: vmaFlushAllocation(),
438 vmaInvalidateAllocation(),
439 or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
440
441 Regions of memory specified for flush/invalidate must be aligned to
442 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
443 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
444 within blocks are aligned to this value, so their offsets are always multiply of
445 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
446
447 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
448
449 Also, Windows drivers from all 3 **PC** GPU vendors (AMD, Intel, NVIDIA)
450 currently provide `HOST_COHERENT` flag on all memory types that are
451 `HOST_VISIBLE`, so on this platform you may not need to bother.
452
453 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
454
455 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
456 despite it wasn't explicitly requested.
457 For example, application may work on integrated graphics with unified memory (like Intel) or
458 allocation from video memory might have failed, so the library chose system memory as fallback.
459
460 You can detect this case and map such allocation to access its memory on CPU directly,
461 instead of launching a transfer operation.
462 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
463 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
464
465 \code
466 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
467 bufCreateInfo.size = sizeof(ConstantBuffer);
468 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
469
470 VmaAllocationCreateInfo allocCreateInfo = {};
471 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
472 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
473
474 VkBuffer buf;
475 VmaAllocation alloc;
476 VmaAllocationInfo allocInfo;
477 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
478
479 VkMemoryPropertyFlags memFlags;
480 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
481 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
482 {
483 // Allocation ended up in mappable memory. You can map it and access it directly.
484 void* mappedData;
485 vmaMapMemory(allocator, alloc, &mappedData);
486 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
487 vmaUnmapMemory(allocator, alloc);
488 }
489 else
490 {
491 // Allocation ended up in non-mappable memory.
492 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
493 }
494 \endcode
495
496 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
497 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
498 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
499 If not, the flag is just ignored.
500 Example:
501
502 \code
503 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
504 bufCreateInfo.size = sizeof(ConstantBuffer);
505 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
506
507 VmaAllocationCreateInfo allocCreateInfo = {};
508 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
509 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
510
511 VkBuffer buf;
512 VmaAllocation alloc;
513 VmaAllocationInfo allocInfo;
514 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
515
516 if(allocInfo.pUserData != nullptr)
517 {
518 // Allocation ended up in mappable memory.
519 // It's persistently mapped. You can access it directly.
520 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
521 }
522 else
523 {
524 // Allocation ended up in non-mappable memory.
525 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
526 }
527 \endcode
528
529
530 \page staying_within_budget Staying within budget
531
532 When developing a graphics-intensive game or program, it is important to avoid allocating
533 more GPU memory than it's physically available. When the memory is over-committed,
534 various bad things can happen, depending on the specific GPU, graphics driver, and
535 operating system:
536
537 - It may just work without any problems.
538 - The application may slow down because some memory blocks are moved to system RAM
539 and the GPU has to access them through PCI Express bus.
540 - A new allocation may take very long time to complete, even few seconds, and possibly
541 freeze entire system.
542 - The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
543 - It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
544 returned somewhere later.
545
546 \section staying_within_budget_querying_for_budget Querying for budget
547
548 To query for current memory usage and available budget, use function vmaGetBudget().
549 Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
550
551 Please note that this function returns different information and works faster than
552 vmaCalculateStats(). vmaGetBudget() can be called every frame or even before every
553 allocation, while vmaCalculateStats() is intended to be used rarely,
554 only to obtain statistical information, e.g. for debugging purposes.
555
556 It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
557 about the budget from Vulkan device. VMA is able to use this extension automatically.
558 When not enabled, the allocator behaves same way, but then it estimates current usage
559 and available budget based on its internal information and Vulkan memory heap sizes,
560 which may be less precise. In order to use this extension:
561
562 1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
563 required by it are available and enable them. Please note that the first is a device
564 extension and the second is instance extension!
565 2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
566 3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
567 Vulkan inside of it to avoid overhead of querying it with every allocation.
568
569 \section staying_within_budget_controlling_memory_usage Controlling memory usage
570
571 There are many ways in which you can try to stay within the budget.
572
573 First, when making new allocation requires allocating a new memory block, the library
574 tries not to exceed the budget automatically. If a block with default recommended size
575 (e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
576 dedicated memory for just this resource.
577
578 If the size of the requested resource plus current memory usage is more than the
579 budget, by default the library still tries to create it, leaving it to the Vulkan
580 implementation whether the allocation succeeds or fails. You can change this behavior
581 by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
582 not made if it would exceed the budget or if the budget is already exceeded.
583 Some other allocations become lost instead to make room for it, if the mechanism of
584 [lost allocations](@ref lost_allocations) is used.
585 If that is not possible, the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
586 Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
587 when creating resources that are not essential for the application (e.g. the texture
588 of a specific object) and not to pass it when creating critically important resources
589 (e.g. render targets).
590
591 Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
592 a new allocation is created only when it fits inside one of the existing memory blocks.
593 If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
594 This also ensures that the function call is very fast because it never goes to Vulkan
595 to obtain a new block.
596
597 Please note that creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
598 set to more than 0 will try to allocate memory blocks without checking whether they
599 fit within budget.
600
601
602 \page custom_memory_pools Custom memory pools
603
604 A memory pool contains a number of `VkDeviceMemory` blocks.
605 The library automatically creates and manages default pool for each memory type available on the device.
606 Default memory pool automatically grows in size.
607 Size of allocated blocks is also variable and managed automatically.
608
609 You can create custom pool and allocate memory out of it.
610 It can be useful if you want to:
611
612 - Keep certain kind of allocations separate from others.
613 - Enforce particular, fixed size of Vulkan memory blocks.
614 - Limit maximum amount of Vulkan memory allocated for that pool.
615 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
616
617 To use custom memory pools:
618
619 -# Fill VmaPoolCreateInfo structure.
620 -# Call vmaCreatePool() to obtain #VmaPool handle.
621 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
622 You don't need to specify any other parameters of this structure, like `usage`.
623
624 Example:
625
626 \code
627 // Create a pool that can have at most 2 blocks, 128 MiB each.
628 VmaPoolCreateInfo poolCreateInfo = {};
629 poolCreateInfo.memoryTypeIndex = ...
630 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
631 poolCreateInfo.maxBlockCount = 2;
632
633 VmaPool pool;
634 vmaCreatePool(allocator, &poolCreateInfo, &pool);
635
636 // Allocate a buffer out of it.
637 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
638 bufCreateInfo.size = 1024;
639 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
640
641 VmaAllocationCreateInfo allocCreateInfo = {};
642 allocCreateInfo.pool = pool;
643
644 VkBuffer buf;
645 VmaAllocation alloc;
646 VmaAllocationInfo allocInfo;
647 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
648 \endcode
649
650 You have to free all allocations made from this pool before destroying it.
651
652 \code
653 vmaDestroyBuffer(allocator, buf, alloc);
654 vmaDestroyPool(allocator, pool);
655 \endcode
656
657 \section custom_memory_pools_MemTypeIndex Choosing memory type index
658
659 When creating a pool, you must explicitly specify memory type index.
660 To find the one suitable for your buffers or images, you can use helper functions
661 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
662 You need to provide structures with example parameters of buffers or images
663 that you are going to create in that pool.
664
665 \code
666 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
667 exampleBufCreateInfo.size = 1024; // Whatever.
668 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
669
670 VmaAllocationCreateInfo allocCreateInfo = {};
671 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
672
673 uint32_t memTypeIndex;
674 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
675
676 VmaPoolCreateInfo poolCreateInfo = {};
677 poolCreateInfo.memoryTypeIndex = memTypeIndex;
678 // ...
679 \endcode
680
681 When creating buffers/images allocated in that pool, provide following parameters:
682
683 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
684 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
685 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
686 or the other way around.
687 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
688 Other members are ignored anyway.
689
690 \section linear_algorithm Linear allocation algorithm
691
692 Each Vulkan memory block managed by this library has accompanying metadata that
693 keeps track of used and unused regions. By default, the metadata structure and
694 algorithm tries to find best place for new allocations among free regions to
695 optimize memory usage. This way you can allocate and free objects in any order.
696
697 
698
699 Sometimes there is a need to use simpler, linear allocation algorithm. You can
700 create custom pool that uses such algorithm by adding flag
701 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
702 #VmaPool object. Then an alternative metadata management is used. It always
703 creates new allocations after last one and doesn't reuse free regions after
704 allocations freed in the middle. It results in better allocation performance and
705 less memory consumed by metadata.
706
707 
708
709 With this one flag, you can create a custom pool that can be used in many ways:
710 free-at-once, stack, double stack, and ring buffer. See below for details.
711
712 \subsection linear_algorithm_free_at_once Free-at-once
713
714 In a pool that uses linear algorithm, you still need to free all the allocations
715 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
716 them in any order. New allocations are always made after last one - free space
717 in the middle is not reused. However, when you release all the allocation and
718 the pool becomes empty, allocation starts from the beginning again. This way you
719 can use linear algorithm to speed up creation of allocations that you are going
720 to release all at once.
721
722 
723
724 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
725 value that allows multiple memory blocks.
726
727 \subsection linear_algorithm_stack Stack
728
729 When you free an allocation that was created last, its space can be reused.
730 Thanks to this, if you always release allocations in the order opposite to their
731 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
732
733 
734
735 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
736 value that allows multiple memory blocks.
737
738 \subsection linear_algorithm_double_stack Double stack
739
740 The space reserved by a custom pool with linear algorithm may be used by two
741 stacks:
742
743 - First, default one, growing up from offset 0.
744 - Second, "upper" one, growing down from the end towards lower offsets.
745
746 To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
747 to VmaAllocationCreateInfo::flags.
748
749 
750
751 Double stack is available only in pools with one memory block -
752 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
753
754 When the two stacks' ends meet so there is not enough space between them for a
755 new allocation, such allocation fails with usual
756 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
757
758 \subsection linear_algorithm_ring_buffer Ring buffer
759
760 When you free some allocations from the beginning and there is not enough free space
761 for a new one at the end of a pool, allocator's "cursor" wraps around to the
762 beginning and starts allocation there. Thanks to this, if you always release
763 allocations in the same order as you created them (FIFO - First In First Out),
764 you can achieve behavior of a ring buffer / queue.
765
766 
767
768 Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
769 If there is not enough free space for a new allocation, but existing allocations
770 from the front of the queue can become lost, they become lost and the allocation
771 succeeds.
772
773 
774
775 Ring buffer is available only in pools with one memory block -
776 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
777
778 \section buddy_algorithm Buddy allocation algorithm
779
780 There is another allocation algorithm that can be used with custom pools, called
781 "buddy". Its internal data structure is based on a tree of blocks, each having
782 size that is a power of two and a half of its parent's size. When you want to
783 allocate memory of certain size, a free node in the tree is located. If it's too
784 large, it is recursively split into two halves (called "buddies"). However, if
785 requested allocation size is not a power of two, the size of a tree node is
786 aligned up to the nearest power of two and the remaining space is wasted. When
787 two buddy nodes become free, they are merged back into one larger node.
788
789 
790
791 The advantage of buddy allocation algorithm over default algorithm is faster
792 allocation and deallocation, as well as smaller external fragmentation. The
793 disadvantage is more wasted space (internal fragmentation).
794
795 For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
796 or other sources that describe this concept in general.
797
798 To use buddy allocation algorithm with a custom pool, add flag
799 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
800 #VmaPool object.
801
802 Several limitations apply to pools that use buddy algorithm:
803
804 - It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
805 Otherwise, only largest power of two smaller than the size is used for
806 allocations. The remaining space always stays unused.
807 - [Margins](@ref debugging_memory_usage_margins) and
808 [corruption detection](@ref debugging_memory_usage_corruption_detection)
809 don't work in such pools.
810 - [Lost allocations](@ref lost_allocations) don't work in such pools. You can
811 use them, but they never become lost. Support may be added in the future.
812 - [Defragmentation](@ref defragmentation) doesn't work with allocations made from
813 such pool.
814
815 \page defragmentation Defragmentation
816
817 Interleaved allocations and deallocations of many objects of varying size can
818 cause fragmentation over time, which can lead to a situation where the library is unable
819 to find a continuous range of free memory for a new allocation despite there is
820 enough free space, just scattered across many small free ranges between existing
821 allocations.
822
823 To mitigate this problem, you can use defragmentation feature:
824 structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
825 Given set of allocations,
826 this function can move them to compact used memory, ensure more continuous free
827 space and possibly also free some `VkDeviceMemory` blocks.
828
829 What the defragmentation does is:
830
831 - Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
832 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
833 VmaAllocationInfo::offset changes. You must query them again using
834 vmaGetAllocationInfo() if you need them.
835 - Moves actual data in memory.
836
837 What it doesn't do, so you need to do it yourself:
838
839 - Recreate buffers and images that were bound to allocations that were defragmented and
840 bind them with their new places in memory.
841 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
842 `vkCreateBuffer()`, `vkCreateImage()`, vmaBindBufferMemory(), vmaBindImageMemory()
843 for that purpose and NOT vmaDestroyBuffer(),
844 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
845 destroy or create allocation objects!
846 - Recreate views and update descriptors that point to these buffers and images.
847
848 \section defragmentation_cpu Defragmenting CPU memory
849
850 Following example demonstrates how you can run defragmentation on CPU.
851 Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
852 Others are ignored.
853
854 The way it works is:
855
856 - It temporarily maps entire memory blocks when necessary.
857 - It moves data using `memmove()` function.
858
859 \code
860 // Given following variables already initialized:
861 VkDevice device;
862 VmaAllocator allocator;
863 std::vector<VkBuffer> buffers;
864 std::vector<VmaAllocation> allocations;
865
866
867 const uint32_t allocCount = (uint32_t)allocations.size();
868 std::vector<VkBool32> allocationsChanged(allocCount);
869
870 VmaDefragmentationInfo2 defragInfo = {};
871 defragInfo.allocationCount = allocCount;
872 defragInfo.pAllocations = allocations.data();
873 defragInfo.pAllocationsChanged = allocationsChanged.data();
874 defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
875 defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
876
877 VmaDefragmentationContext defragCtx;
878 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
879 vmaDefragmentationEnd(allocator, defragCtx);
880
881 for(uint32_t i = 0; i < allocCount; ++i)
882 {
883 if(allocationsChanged[i])
884 {
885 // Destroy buffer that is immutably bound to memory region which is no longer valid.
886 vkDestroyBuffer(device, buffers[i], nullptr);
887
888 // Create new buffer with same parameters.
889 VkBufferCreateInfo bufferInfo = ...;
890 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
891
892 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
893
894 // Bind new buffer to new memory region. Data contained in it is already moved.
895 VmaAllocationInfo allocInfo;
896 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
897 vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
898 }
899 }
900 \endcode
901
902 Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
903 This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
904 has been modified during defragmentation.
905 You can pass null, but you then need to query every allocation passed to defragmentation
906 for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
907
908 If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
909 you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
910 instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
911 to defragment all allocations in given pools.
912 You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
913 You can also combine both methods.
914
915 \section defragmentation_gpu Defragmenting GPU memory
916
917 It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
918 To do that, you need to pass a command buffer that meets requirements as described in
919 VmaDefragmentationInfo2::commandBuffer. The way it works is:
920
921 - It creates temporary buffers and binds them to entire memory blocks when necessary.
922 - It issues `vkCmdCopyBuffer()` to passed command buffer.
923
924 Example:
925
926 \code
927 // Given following variables already initialized:
928 VkDevice device;
929 VmaAllocator allocator;
930 VkCommandBuffer commandBuffer;
931 std::vector<VkBuffer> buffers;
932 std::vector<VmaAllocation> allocations;
933
934
935 const uint32_t allocCount = (uint32_t)allocations.size();
936 std::vector<VkBool32> allocationsChanged(allocCount);
937
938 VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
939 vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
940
941 VmaDefragmentationInfo2 defragInfo = {};
942 defragInfo.allocationCount = allocCount;
943 defragInfo.pAllocations = allocations.data();
944 defragInfo.pAllocationsChanged = allocationsChanged.data();
945 defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
946 defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
947 defragInfo.commandBuffer = commandBuffer;
948
949 VmaDefragmentationContext defragCtx;
950 vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
951
952 vkEndCommandBuffer(commandBuffer);
953
954 // Submit commandBuffer.
955 // Wait for a fence that ensures commandBuffer execution finished.
956
957 vmaDefragmentationEnd(allocator, defragCtx);
958
959 for(uint32_t i = 0; i < allocCount; ++i)
960 {
961 if(allocationsChanged[i])
962 {
963 // Destroy buffer that is immutably bound to memory region which is no longer valid.
964 vkDestroyBuffer(device, buffers[i], nullptr);
965
966 // Create new buffer with same parameters.
967 VkBufferCreateInfo bufferInfo = ...;
968 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
969
970 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
971
972 // Bind new buffer to new memory region. Data contained in it is already moved.
973 VmaAllocationInfo allocInfo;
974 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
975 vmaBindBufferMemory(allocator, allocations[i], buffers[i]);
976 }
977 }
978 \endcode
979
980 You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
981 The library automatically chooses best method to defragment each memory pool.
982
983 You may try not to block your entire program to wait until defragmentation finishes,
984 but do it in the background, as long as you carefully fullfill requirements described
985 in function vmaDefragmentationBegin().
986
987 \section defragmentation_additional_notes Additional notes
988
989 It is only legal to defragment allocations bound to:
990
991 - buffers
992 - images created with `VK_IMAGE_CREATE_ALIAS_BIT`, `VK_IMAGE_TILING_LINEAR`, and
993 being currently in `VK_IMAGE_LAYOUT_GENERAL` or `VK_IMAGE_LAYOUT_PREINITIALIZED`.
994
995 Defragmentation of images created with `VK_IMAGE_TILING_OPTIMAL` or in any other
996 layout may give undefined results.
997
998 If you defragment allocations bound to images, new images to be bound to new
999 memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
1000 and then transitioned to their original layout from before defragmentation if
1001 needed using an image memory barrier.
1002
1003 While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
1004 See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
1005
1006 Please don't expect memory to be fully compacted after defragmentation.
1007 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
1008 memory blocks to make totally empty to release them, as well as to maximimze continuous
1009 empty space inside remaining blocks, while minimizing the number and size of allocations that
1010 need to be moved. Some fragmentation may still remain - this is normal.
1011
1012 \section defragmentation_custom_algorithm Writing custom defragmentation algorithm
1013
1014 If you want to implement your own, custom defragmentation algorithm,
1015 there is infrastructure prepared for that,
1016 but it is not exposed through the library API - you need to hack its source code.
1017 Here are steps needed to do this:
1018
1019 -# Main thing you need to do is to define your own class derived from base abstract
1020 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
1021 See definition and comments of this class for details.
1022 -# Your code needs to interact with device memory block metadata.
1023 If you need more access to its data than it's provided by its public interface,
1024 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
1025 -# If you want to create a flag that would enable your algorithm or pass some additional
1026 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
1027 VmaDefragmentationInfo2::flags.
1028 -# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
1029 of your new class whenever needed.
1030
1031
1032 \page lost_allocations Lost allocations
1033
1034 If your game oversubscribes video memory, if may work OK in previous-generation
1035 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
1036 paged to system RAM. In Vulkan you can't do it because when you run out of
1037 memory, an allocation just fails. If you have more data (e.g. textures) that can
1038 fit into VRAM and you don't need it all at once, you may want to upload them to
1039 GPU on demand and "push out" ones that are not used for a long time to make room
1040 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
1041 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
1042 "lost allocations".
1043
1044 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
1045 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
1046 such allocation in every new frame, you need to query it if it's not lost.
1047 To check it, call vmaTouchAllocation().
1048 If the allocation is lost, you should not use it or buffer/image bound to it.
1049 You mustn't forget to destroy this allocation and this buffer/image.
1050 vmaGetAllocationInfo() can also be used for checking status of the allocation.
1051 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
1052
1053 To create an allocation that can make some other allocations lost to make room
1054 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
1055 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
1056 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
1057
1058 Warning! Current implementation uses quite naive, brute force algorithm,
1059 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
1060 flag quite slow. A new, more optimal algorithm and data structure to speed this
1061 up is planned for the future.
1062
1063 <b>Q: When interleaving creation of new allocations with usage of existing ones,
1064 how do you make sure that an allocation won't become lost while it's used in the
1065 current frame?</b>
1066
1067 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
1068 status/parameters and checks whether it's not lost, but when it's not, it also
1069 atomically marks it as used in the current frame, which makes it impossible to
1070 become lost in that frame. It uses lockless algorithm, so it works fast and
1071 doesn't involve locking any internal mutex.
1072
1073 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
1074 previous frame while I already submit new frame on the CPU?</b>
1075
1076 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
1077 become lost for a number of additional frames back from the current one by
1078 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
1079 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
1080
1081 <b>Q: How do you inform the library when new frame starts?</b>
1082
1083 You need to call function vmaSetCurrentFrameIndex().
1084
1085 Example code:
1086
1087 \code
1088 struct MyBuffer
1089 {
1090 VkBuffer m_Buf = nullptr;
1091 VmaAllocation m_Alloc = nullptr;
1092
1093 // Called when the buffer is really needed in the current frame.
1094 void EnsureBuffer();
1095 };
1096
1097 void MyBuffer::EnsureBuffer()
1098 {
1099 // Buffer has been created.
1100 if(m_Buf != VK_NULL_HANDLE)
1101 {
1102 // Check if its allocation is not lost + mark it as used in current frame.
1103 if(vmaTouchAllocation(allocator, m_Alloc))
1104 {
1105 // It's all OK - safe to use m_Buf.
1106 return;
1107 }
1108 }
1109
1110 // Buffer not yet exists or lost - destroy and recreate it.
1111
1112 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1113
1114 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1115 bufCreateInfo.size = 1024;
1116 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1117
1118 VmaAllocationCreateInfo allocCreateInfo = {};
1119 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1120 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1121 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1122
1123 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1124 }
1125 \endcode
1126
1127 When using lost allocations, you may see some Vulkan validation layer warnings
1128 about overlapping regions of memory bound to different kinds of buffers and
1129 images. This is still valid as long as you implement proper handling of lost
1130 allocations (like in the example above) and don't use them.
1131
1132 You can create an allocation that is already in lost state from the beginning using function
1133 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1134
1135 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1136 in a specified custom pool to lost state.
1137 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1138 cannot become lost.
1139
1140 <b>Q: Can I touch allocation that cannot become lost?</b>
1141
1142 Yes, although it has no visible effect.
1143 Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1144 also for allocations that cannot become lost, but the only way to observe it is to dump
1145 internal allocator state using vmaBuildStatsString().
1146 You can use this feature for debugging purposes to explicitly mark allocations that you use
1147 in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1148
1149
1150 \page statistics Statistics
1151
1152 This library contains functions that return information about its internal state,
1153 especially the amount of memory allocated from Vulkan.
1154 Please keep in mind that these functions need to traverse all internal data structures
1155 to gather these information, so they may be quite time-consuming.
1156 Don't call them too often.
1157
1158 \section statistics_numeric_statistics Numeric statistics
1159
1160 You can query for overall statistics of the allocator using function vmaCalculateStats().
1161 Information are returned using structure #VmaStats.
1162 It contains #VmaStatInfo - number of allocated blocks, number of allocations
1163 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1164 number of bytes used and unused (but still allocated from Vulkan) and other information.
1165 They are summed across memory heaps, memory types and total for whole allocator.
1166
1167 You can query for statistics of a custom pool using function vmaGetPoolStats().
1168 Information are returned using structure #VmaPoolStats.
1169
1170 You can query for information about specific allocation using function vmaGetAllocationInfo().
1171 It fill structure #VmaAllocationInfo.
1172
1173 \section statistics_json_dump JSON dump
1174
1175 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1176 The result is guaranteed to be correct JSON.
1177 It uses ANSI encoding.
1178 Any strings provided by user (see [Allocation names](@ref allocation_names))
1179 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1180 this JSON string can be treated as using this encoding.
1181 It must be freed using function vmaFreeStatsString().
1182
1183 The format of this JSON string is not part of official documentation of the library,
1184 but it will not change in backward-incompatible way without increasing library major version number
1185 and appropriate mention in changelog.
1186
1187 The JSON string contains all the data that can be obtained using vmaCalculateStats().
1188 It can also contain detailed map of allocated memory blocks and their regions -
1189 free and occupied by allocations.
1190 This allows e.g. to visualize the memory or assess fragmentation.
1191
1192
1193 \page allocation_annotation Allocation names and user data
1194
1195 \section allocation_user_data Allocation user data
1196
1197 You can annotate allocations with your own information, e.g. for debugging purposes.
1198 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1199 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1200 some handle, index, key, ordinal number or any other value that would associate
1201 the allocation with your custom metadata.
1202
1203 \code
1204 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1205 // Fill bufferInfo...
1206
1207 MyBufferMetadata* pMetadata = CreateBufferMetadata();
1208
1209 VmaAllocationCreateInfo allocCreateInfo = {};
1210 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1211 allocCreateInfo.pUserData = pMetadata;
1212
1213 VkBuffer buffer;
1214 VmaAllocation allocation;
1215 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1216 \endcode
1217
1218 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1219
1220 \code
1221 VmaAllocationInfo allocInfo;
1222 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1223 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1224 \endcode
1225
1226 It can also be changed using function vmaSetAllocationUserData().
1227
1228 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1229 vmaBuildStatsString(), in hexadecimal form.
1230
1231 \section allocation_names Allocation names
1232
1233 There is alternative mode available where `pUserData` pointer is used to point to
1234 a null-terminated string, giving a name to the allocation. To use this mode,
1235 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1236 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1237 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1238 The library creates internal copy of the string, so the pointer you pass doesn't need
1239 to be valid for whole lifetime of the allocation. You can free it after the call.
1240
1241 \code
1242 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1243 // Fill imageInfo...
1244
1245 std::string imageName = "Texture: ";
1246 imageName += fileName;
1247
1248 VmaAllocationCreateInfo allocCreateInfo = {};
1249 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1250 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1251 allocCreateInfo.pUserData = imageName.c_str();
1252
1253 VkImage image;
1254 VmaAllocation allocation;
1255 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1256 \endcode
1257
1258 The value of `pUserData` pointer of the allocation will be different than the one
1259 you passed when setting allocation's name - pointing to a buffer managed
1260 internally that holds copy of the string.
1261
1262 \code
1263 VmaAllocationInfo allocInfo;
1264 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1265 const char* imageName = (const char*)allocInfo.pUserData;
1266 printf("Image name: %s\n", imageName);
1267 \endcode
1268
1269 That string is also printed in JSON report created by vmaBuildStatsString().
1270
1271 \note Passing string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
1272 You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
1273
1274
1275 \page debugging_memory_usage Debugging incorrect memory usage
1276
1277 If you suspect a bug with memory usage, like usage of uninitialized memory or
1278 memory being overwritten out of bounds of an allocation,
1279 you can use debug features of this library to verify this.
1280
1281 \section debugging_memory_usage_initialization Memory initialization
1282
1283 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1284 you can enable automatic memory initialization to verify this.
1285 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1286
1287 \code
1288 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1289 #include "vk_mem_alloc.h"
1290 \endcode
1291
1292 It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1293 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1294 Memory is automatically mapped and unmapped if necessary.
1295
1296 If you find these values while debugging your program, good chances are that you incorrectly
1297 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1298
1299 Memory initialization works only with memory types that are `HOST_VISIBLE`.
1300 It works also with dedicated allocations.
1301 It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1302 as they cannot be mapped.
1303
1304 \section debugging_memory_usage_margins Margins
1305
1306 By default, allocations are laid out in memory blocks next to each other if possible
1307 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1308
1309 
1310
1311 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1312 number of bytes as a margin before and after every allocation.
1313
1314 \code
1315 #define VMA_DEBUG_MARGIN 16
1316 #include "vk_mem_alloc.h"
1317 \endcode
1318
1319 
1320
1321 If your bug goes away after enabling margins, it means it may be caused by memory
1322 being overwritten outside of allocation boundaries. It is not 100% certain though.
1323 Change in application behavior may also be caused by different order and distribution
1324 of allocations across memory blocks after margins are applied.
1325
1326 The margin is applied also before first and after last allocation in a block.
1327 It may occur only once between two adjacent allocations.
1328
1329 Margins work with all types of memory.
1330
1331 Margin is applied only to allocations made out of memory blocks and not to dedicated
1332 allocations, which have their own memory block of specific size.
1333 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1334 or those automatically decided to put into dedicated allocations, e.g. due to its
1335 large size or recommended by VK_KHR_dedicated_allocation extension.
1336 Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1337
1338 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1339
1340 Note that enabling margins increases memory usage and fragmentation.
1341
1342 \section debugging_memory_usage_corruption_detection Corruption detection
1343
1344 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1345 of contents of the margins.
1346
1347 \code
1348 #define VMA_DEBUG_MARGIN 16
1349 #define VMA_DEBUG_DETECT_CORRUPTION 1
1350 #include "vk_mem_alloc.h"
1351 \endcode
1352
1353 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1354 (it must be multiply of 4) before and after every allocation is filled with a magic number.
1355 This idea is also know as "canary".
1356 Memory is automatically mapped and unmapped if necessary.
1357
1358 This number is validated automatically when the allocation is destroyed.
1359 If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1360 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1361 which indicates a serious bug.
1362
1363 You can also explicitly request checking margins of all allocations in all memory blocks
1364 that belong to specified memory types by using function vmaCheckCorruption(),
1365 or in memory blocks that belong to specified custom pool, by using function
1366 vmaCheckPoolCorruption().
1367
1368 Margin validation (corruption detection) works only for memory types that are
1369 `HOST_VISIBLE` and `HOST_COHERENT`.
1370
1371
1372 \page record_and_replay Record and replay
1373
1374 \section record_and_replay_introduction Introduction
1375
1376 While using the library, sequence of calls to its functions together with their
1377 parameters can be recorded to a file and later replayed using standalone player
1378 application. It can be useful to:
1379
1380 - Test correctness - check if same sequence of calls will not cause crash or
1381 failures on a target platform.
1382 - Gather statistics - see number of allocations, peak memory usage, number of
1383 calls etc.
1384 - Benchmark performance - see how much time it takes to replay the whole
1385 sequence.
1386
1387 \section record_and_replay_usage Usage
1388
1389 Recording functionality is disabled by default.
1390 To enable it, define following macro before every include of this library:
1391
1392 \code
1393 #define VMA_RECORDING_ENABLED 1
1394 \endcode
1395
1396 <b>To record sequence of calls to a file:</b> Fill in
1397 VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1398 object. File is opened and written during whole lifetime of the allocator.
1399
1400 <b>To replay file:</b> Use VmaReplay - standalone command-line program.
1401 Precompiled binary can be found in "bin" directory.
1402 Its source can be found in "src/VmaReplay" directory.
1403 Its project is generated by Premake.
1404 Command line syntax is printed when the program is launched without parameters.
1405 Basic usage:
1406
1407 VmaReplay.exe MyRecording.csv
1408
1409 <b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1410 It's a human-readable, text file in CSV format (Comma Separated Values).
1411
1412 \section record_and_replay_additional_considerations Additional considerations
1413
1414 - Replaying file that was recorded on a different GPU (with different parameters
1415 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1416 set of memory heaps and types) may give different performance and memory usage
1417 results, as well as issue some warnings and errors.
1418 - Current implementation of recording in VMA, as well as VmaReplay application, is
1419 coded and tested only on Windows. Inclusion of recording code is driven by
1420 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1421 add. Contributions are welcomed.
1422
1423
1424 \page usage_patterns Recommended usage patterns
1425
1426 See also slides from talk:
1427 [Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)
1428
1429
1430 \section usage_patterns_common_mistakes Common mistakes
1431
1432 <b>Use of CPU_TO_GPU instead of CPU_ONLY memory</b>
1433
1434 #VMA_MEMORY_USAGE_CPU_TO_GPU is recommended only for resources that will be
1435 mapped and written by the CPU, as well as read directly by the GPU - like some
1436 buffers or textures updated every frame (dynamic). If you create a staging copy
1437 of a resource to be written by CPU and then used as a source of transfer to
1438 another resource placed in the GPU memory, that staging resource should be
1439 created with #VMA_MEMORY_USAGE_CPU_ONLY. Please read the descriptions of these
1440 enums carefully for details.
1441
1442 <b>Unnecessary use of custom pools</b>
1443
1444 \ref custom_memory_pools may be useful for special purposes - when you want to
1445 keep certain type of resources separate e.g. to reserve minimum amount of memory
1446 for them, limit maximum amount of memory they can occupy, or make some of them
1447 push out the other through the mechanism of \ref lost_allocations. For most
1448 resources this is not needed and so it is not recommended to create #VmaPool
1449 objects and allocations out of them. Allocating from the default pool is sufficient.
1450
1451 \section usage_patterns_simple Simple patterns
1452
1453 \subsection usage_patterns_simple_render_targets Render targets
1454
1455 <b>When:</b>
1456 Any resources that you frequently write and read on GPU,
1457 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1458 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1459
1460 <b>What to do:</b>
1461 Create them in video memory that is fastest to access from GPU using
1462 #VMA_MEMORY_USAGE_GPU_ONLY.
1463
1464 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1465 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1466 especially if they are large or if you plan to destroy and recreate them e.g. when
1467 display resolution changes.
1468 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1469
1470 \subsection usage_patterns_simple_immutable_resources Immutable resources
1471
1472 <b>When:</b>
1473 Any resources that you fill on CPU only once (aka "immutable") or infrequently
1474 and then read frequently on GPU,
1475 e.g. textures, vertex and index buffers, constant buffers that don't change often.
1476
1477 <b>What to do:</b>
1478 Create them in video memory that is fastest to access from GPU using
1479 #VMA_MEMORY_USAGE_GPU_ONLY.
1480
1481 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1482 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1483 and submit a transfer from it to the GPU resource.
1484 You can keep the staging copy if you need it for another upload transfer in the future.
1485 If you don't, you can destroy it or reuse this buffer for uploading different resource
1486 after the transfer finishes.
1487
1488 Prefer to create just buffers in system memory rather than images, even for uploading textures.
1489 Use `vkCmdCopyBufferToImage()`.
1490 Dont use images with `VK_IMAGE_TILING_LINEAR`.
1491
1492 \subsection usage_patterns_dynamic_resources Dynamic resources
1493
1494 <b>When:</b>
1495 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1496 written on CPU, read on GPU.
1497
1498 <b>What to do:</b>
1499 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1500 You can map it and write to it directly on CPU, as well as read from it on GPU.
1501
1502 This is a more complex situation. Different solutions are possible,
1503 and the best one depends on specific GPU type, but you can use this simple approach for the start.
1504 Prefer to write to such resource sequentially (e.g. using `memcpy`).
1505 Don't perform random access or any reads from it on CPU, as it may be very slow.
1506 Also note that textures written directly from the host through a mapped pointer need to be in LINEAR not OPTIMAL layout.
1507
1508 \subsection usage_patterns_readback Readback
1509
1510 <b>When:</b>
1511 Resources that contain data written by GPU that you want to read back on CPU,
1512 e.g. results of some computations.
1513
1514 <b>What to do:</b>
1515 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1516 You can write to them directly on GPU, as well as map and read them on CPU.
1517
1518 \section usage_patterns_advanced Advanced patterns
1519
1520 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
1521
1522 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1523 by detecting it in Vulkan.
1524 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1525 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1526 When you find it, you can assume that memory is unified and all memory types are comparably fast
1527 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1528
1529 You can then sum up sizes of all available memory heaps and treat them as useful for
1530 your GPU resources, instead of only `DEVICE_LOCAL` ones.
1531 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1532 directly instead of submitting explicit transfer (see below).
1533
1534 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1535
1536 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1537
1538 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1539 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit transfer each time.
1540 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1541 read it directly on GPU.
1542 -# Create just a single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1543 read it directly on GPU.
1544
1545 Which solution is the most efficient depends on your resource and especially on the GPU.
1546 It is best to measure it and then make the decision.
1547 Some general recommendations:
1548
1549 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1550 related to using a second copy and making transfer.
1551 - For small resources (e.g. constant buffers) use (2).
1552 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1553 Even if the resource ends up in system memory, its data may be cached on GPU after first
1554 fetch over PCIe bus.
1555 - For larger resources (e.g. textures), decide between (1) and (2).
1556 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1557 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1558
1559 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1560 solutions are possible:
1561
1562 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1563 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1564 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1565 map it and read it on CPU.
1566
1567 You should take some measurements to decide which option is faster in case of your specific
1568 resource.
1569
1570 Note that textures accessed directly from the host through a mapped pointer need to be in LINEAR layout,
1571 which may slow down their usage on the device.
1572 Textures accessed only by the device and transfer operations can use OPTIMAL layout.
1573
1574 If you don't want to specialize your code for specific types of GPUs, you can still make
1575 an simple optimization for cases when your resource ends up in mappable memory to use it
1576 directly in this case instead of creating CPU-side staging copy.
1577 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1578
1579
1580 \page configuration Configuration
1581
1582 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1583 before each include of this file or change directly in this file to provide
1584 your own implementation of basic facilities like assert, `min()` and `max()` functions,
1585 mutex, atomic etc.
1586 The library uses its own implementation of containers by default, but you can switch to using
1587 STL containers instead.
1588
1589 For example, define `VMA_ASSERT(expr)` before including the library to provide
1590 custom implementation of the assertion, compatible with your project.
1591 By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
1592 and empty otherwise.
1593
1594 \section config_Vulkan_functions Pointers to Vulkan functions
1595
1596 There are multiple ways to import pointers to Vulkan functions in the library.
1597 In the simplest case you don't need to do anything.
1598 If the compilation or linking of your program or the initialization of the #VmaAllocator
1599 doesn't work for you, you can try to reconfigure it.
1600
1601 First, the allocator tries to fetch pointers to Vulkan functions linked statically,
1602 like this:
1603
1604 \code
1605 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
1606 \endcode
1607
1608 If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
1609
1610 Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
1611 You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
1612 by using a helper library like [volk](https://github.com/zeux/volk).
1613
1614 Third, VMA tries to fetch remaining pointers that are still null by calling
1615 `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
1616 If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
1617
1618 Finally, all the function pointers required by the library (considering selected
1619 Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
1620
1621
1622 \section custom_memory_allocator Custom host memory allocator
1623
1624 If you use custom allocator for CPU memory rather than default operator `new`
1625 and `delete` from C++, you can make this library using your allocator as well
1626 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1627 functions will be passed to Vulkan, as well as used by the library itself to
1628 make any CPU-side allocations.
1629
1630 \section allocation_callbacks Device memory allocation callbacks
1631
1632 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1633 You can setup callbacks to be informed about these calls, e.g. for the purpose
1634 of gathering some statistics. To do it, fill optional member
1635 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1636
1637 \section heap_memory_limit Device heap memory limit
1638
1639 When device memory of certain heap runs out of free space, new allocations may
1640 fail (returning error code) or they may succeed, silently pushing some existing
1641 memory blocks from GPU VRAM to system RAM (which degrades performance). This
1642 behavior is implementation-dependant - it depends on GPU vendor and graphics
1643 driver.
1644
1645 On AMD cards it can be controlled while creating Vulkan device object by using
1646 VK_AMD_memory_overallocation_behavior extension, if available.
1647
1648 Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
1649 memory available without switching your graphics card to one that really has
1650 smaller VRAM, you can use a feature of this library intended for this purpose.
1651 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1652
1653
1654
1655 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1656
1657 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1658 performance on some GPUs. It augments Vulkan API with possibility to query
1659 driver whether it prefers particular buffer or image to have its own, dedicated
1660 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1661 to do some internal optimizations.
1662
1663 The extension is supported by this library. It will be used automatically when
1664 enabled. To enable it:
1665
1666 1 . When creating Vulkan device, check if following 2 device extensions are
1667 supported (call `vkEnumerateDeviceExtensionProperties()`).
1668 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1669
1670 - VK_KHR_get_memory_requirements2
1671 - VK_KHR_dedicated_allocation
1672
1673 If you enabled these extensions:
1674
1675 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1676 your #VmaAllocator`to inform the library that you enabled required extensions
1677 and you want the library to use them.
1678
1679 \code
1680 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1681
1682 vmaCreateAllocator(&allocatorInfo, &allocator);
1683 \endcode
1684
1685 That's all. The extension will be automatically used whenever you create a
1686 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1687
1688 When using the extension together with Vulkan Validation Layer, you will receive
1689 warnings like this:
1690
1691 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1692
1693 It is OK, you should just ignore it. It happens because you use function
1694 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1695 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1696 unaware of it.
1697
1698 To learn more about this extension, see:
1699
1700 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap44.html#VK_KHR_dedicated_allocation)
1701 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1702
1703
1704
1705 \page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
1706
1707 VK_AMD_device_coherent_memory is a device extension that enables access to
1708 additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
1709 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
1710 allocation of buffers intended for writing "breadcrumb markers" in between passes
1711 or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
1712
1713 When the extension is available but has not been enabled, Vulkan physical device
1714 still exposes those memory types, but their usage is forbidden. VMA automatically
1715 takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
1716 to allocate memory of such type is made.
1717
1718 If you want to use this extension in connection with VMA, follow these steps:
1719
1720 \section vk_amd_device_coherent_memory_initialization Initialization
1721
1722 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
1723 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
1724
1725 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
1726 Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
1727 Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
1728
1729 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
1730 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
1731
1732 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
1733 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
1734 Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
1735 `VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
1736
1737 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
1738 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
1739 to VmaAllocatorCreateInfo::flags.
1740
1741 \section vk_amd_device_coherent_memory_usage Usage
1742
1743 After following steps described above, you can create VMA allocations and custom pools
1744 out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
1745 devices. There are multiple ways to do it, for example:
1746
1747 - You can request or prefer to allocate out of such memory types by adding
1748 `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
1749 or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
1750 other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
1751 - If you manually found memory type index to use for this purpose, force allocation
1752 from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
1753
1754 \section vk_amd_device_coherent_memory_more_information More information
1755
1756 To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap44.html#VK_AMD_device_coherent_memory)
1757
1758 Example use of this extension can be found in the code of the sample and test suite
1759 accompanying this library.
1760
1761
1762 \page enabling_buffer_device_address Enabling buffer device address
1763
1764 Device extension VK_KHR_buffer_device_address
1765 allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
1766 It is promoted to core Vulkan 1.2.
1767
1768 If you want to use this feature in connection with VMA, follow these steps:
1769
1770 \section enabling_buffer_device_address_initialization Initialization
1771
1772 1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
1773 Check if the extension is supported - if returned array of `VkExtensionProperties` contains
1774 "VK_KHR_buffer_device_address".
1775
1776 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
1777 Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
1778 Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures*::bufferDeviceAddress` is true.
1779
1780 3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
1781 "VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
1782
1783 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
1784 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
1785 Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
1786 `VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
1787
1788 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
1789 have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
1790 to VmaAllocatorCreateInfo::flags.
1791
1792 \section enabling_buffer_device_address_usage Usage
1793
1794 After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
1795 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
1796 allocated memory blocks wherever it might be needed.
1797
1798 Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
1799 The second part of this functionality related to "capture and replay" is not supported,
1800 as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
1801
1802 \section enabling_buffer_device_address_more_information More information
1803
1804 To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address)
1805
1806 Example use of this extension can be found in the code of the sample and test suite
1807 accompanying this library.
1808
1809 \page general_considerations General considerations
1810
1811 \section general_considerations_thread_safety Thread safety
1812
1813 - The library has no global state, so separate #VmaAllocator objects can be used
1814 independently.
1815 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1816 - By default, all calls to functions that take #VmaAllocator as first parameter
1817 are safe to call from multiple threads simultaneously because they are
1818 synchronized internally when needed.
1819 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1820 flag, calls to functions that take such #VmaAllocator object must be
1821 synchronized externally.
1822 - Access to a #VmaAllocation object must be externally synchronized. For example,
1823 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1824 threads at the same time if you pass the same #VmaAllocation object to these
1825 functions.
1826
1827 \section general_considerations_validation_layer_warnings Validation layer warnings
1828
1829 When using this library, you can meet following types of warnings issued by
1830 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1831 to just ignore them.
1832
1833 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1834 - It happens when VK_KHR_dedicated_allocation extension is enabled.
1835 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1836 - *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.*
1837 - It happens when you map a buffer or image, because the library maps entire
1838 `VkDeviceMemory` block, where different types of images and buffers may end
1839 up together, especially on GPUs with unified memory like Intel.
1840 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1841 - It happens when you use lost allocations, and a new image or buffer is
1842 created in place of an existing object that bacame lost.
1843 - It may happen also when you use [defragmentation](@ref defragmentation).
1844
1845 \section general_considerations_allocation_algorithm Allocation algorithm
1846
1847 The library uses following algorithm for allocation, in order:
1848
1849 -# Try to find free range of memory in existing blocks.
1850 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1851 -# If failed, try to create such block with size/2, size/4, size/8.
1852 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1853 specified, try to find space in existing blocks, possilby making some other
1854 allocations lost.
1855 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1856 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1857 -# If failed, choose other memory type that meets the requirements specified in
1858 VmaAllocationCreateInfo and go to point 1.
1859 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1860
1861 \section general_considerations_features_not_supported Features not supported
1862
1863 Features deliberately excluded from the scope of this library:
1864
1865 - Data transfer. Uploading (straming) and downloading data of buffers and images
1866 between CPU and GPU memory and related synchronization is responsibility of the user.
1867 Defining some "texture" object that would automatically stream its data from a
1868 staging copy in CPU memory to GPU memory would rather be a feature of another,
1869 higher-level library implemented on top of VMA.
1870 - Allocations for imported/exported external memory. They tend to require
1871 explicit memory type index and dedicated allocation anyway, so they don't
1872 interact with main features of this library. Such special purpose allocations
1873 should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1874 - Recreation of buffers and images. Although the library has functions for
1875 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1876 recreate these objects yourself after defragmentation. That's because the big
1877 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1878 #VmaAllocation object.
1879 - Handling CPU memory allocation failures. When dynamically creating small C++
1880 objects in CPU memory (not Vulkan memory), allocation failures are not checked
1881 and handled gracefully, because that would complicate code significantly and
1882 is usually not needed in desktop PC applications anyway.
1883 - Code free of any compiler warnings. Maintaining the library to compile and
1884 work correctly on so many different platforms is hard enough. Being free of
1885 any warnings, on any version of any compiler, is simply not feasible.
1886 - This is a C++ library with C interface.
1887 Bindings or ports to any other programming languages are welcomed as external projects and
1888 are not going to be included into this repository.
1889
1890 */
1891
1892 #if VMA_RECORDING_ENABLED
1893 #include <chrono>
1894 #if defined(_WIN32)
1895 #include <windows.h>
1896 #else
1897 #include <sstream>
1898 #include <thread>
1899 #endif
1900 #endif
1901
1902 #ifdef __cplusplus
1903 extern "C" {
1904 #endif
1905
1906 /*
1907 Define this macro to 0/1 to disable/enable support for recording functionality,
1908 available through VmaAllocatorCreateInfo::pRecordSettings.
1909 */
1910 #ifndef VMA_RECORDING_ENABLED
1911 #define VMA_RECORDING_ENABLED 0
1912 #endif
1913
1914 #ifndef NOMINMAX
1915 #define NOMINMAX // For windows.h
1916 #endif
1917
1918 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
1919 extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
1920 extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
1921 extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1922 extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1923 extern PFN_vkAllocateMemory vkAllocateMemory;
1924 extern PFN_vkFreeMemory vkFreeMemory;
1925 extern PFN_vkMapMemory vkMapMemory;
1926 extern PFN_vkUnmapMemory vkUnmapMemory;
1927 extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1928 extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1929 extern PFN_vkBindBufferMemory vkBindBufferMemory;
1930 extern PFN_vkBindImageMemory vkBindImageMemory;
1931 extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1932 extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1933 extern PFN_vkCreateBuffer vkCreateBuffer;
1934 extern PFN_vkDestroyBuffer vkDestroyBuffer;
1935 extern PFN_vkCreateImage vkCreateImage;
1936 extern PFN_vkDestroyImage vkDestroyImage;
1937 extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1938 #if VMA_VULKAN_VERSION >= 1001000
1939 extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
1940 extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
1941 extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
1942 extern PFN_vkBindImageMemory2 vkBindImageMemory2;
1943 extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
1944 #endif // #if VMA_VULKAN_VERSION >= 1001000
1945 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
1946
1947 #ifndef VULKAN_H_
1948 // For skia we don't include vulkan.h here. Before including this header we always include
1949 // Skias internal vulkan header which should have everything we need. We can't depend on the
1950 // VULKAN_H_ guard casue skia just inclues vulkan_core.h which doesn't not set the general
1951 // vulkan guard.
1952 //#include <vulkan/vulkan.h>
1953 #endif
1954
1955 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
1956 // where AAA = major, BBB = minor, CCC = patch.
1957 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
1958 #if !defined(VMA_VULKAN_VERSION)
1959 #if defined(VK_VERSION_1_2)
1960 #define VMA_VULKAN_VERSION 1002000
1961 #elif defined(VK_VERSION_1_1)
1962 #define VMA_VULKAN_VERSION 1001000
1963 #else
1964 #define VMA_VULKAN_VERSION 1000000
1965 #endif
1966 #endif
1967
1968 #if !defined(VMA_DEDICATED_ALLOCATION)
1969 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1970 #define VMA_DEDICATED_ALLOCATION 1
1971 #else
1972 #define VMA_DEDICATED_ALLOCATION 0
1973 #endif
1974 #endif
1975
1976 #if !defined(VMA_BIND_MEMORY2)
1977 #if VK_KHR_bind_memory2
1978 #define VMA_BIND_MEMORY2 1
1979 #else
1980 #define VMA_BIND_MEMORY2 0
1981 #endif
1982 #endif
1983
1984 #if !defined(VMA_MEMORY_BUDGET)
1985 #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
1986 #define VMA_MEMORY_BUDGET 1
1987 #else
1988 #define VMA_MEMORY_BUDGET 0
1989 #endif
1990 #endif
1991
1992 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
1993 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
1994 #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
1995 #define VMA_BUFFER_DEVICE_ADDRESS 1
1996 #else
1997 #define VMA_BUFFER_DEVICE_ADDRESS 0
1998 #endif
1999 #endif
2000
2001 // Define these macros to decorate all public functions with additional code,
2002 // before and after returned type, appropriately. This may be useful for
2003 // exporing the functions when compiling VMA as a separate library. Example:
2004 // #define VMA_CALL_PRE __declspec(dllexport)
2005 // #define VMA_CALL_POST __cdecl
2006 #ifndef VMA_CALL_PRE
2007 #define VMA_CALL_PRE
2008 #endif
2009 #ifndef VMA_CALL_POST
2010 #define VMA_CALL_POST
2011 #endif
2012
2013 // Define this macro to decorate pointers with an attribute specifying the
2014 // length of the array they point to if they are not null.
2015 //
2016 // The length may be one of
2017 // - The name of another parameter in the argument list where the pointer is declared
2018 // - The name of another member in the struct where the pointer is declared
2019 // - The name of a member of a struct type, meaning the value of that member in
2020 // the context of the call. For example
2021 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
2022 // this means the number of memory heaps available in the device associated
2023 // with the VmaAllocator being dealt with.
2024 #ifndef VMA_LEN_IF_NOT_NULL
2025 #define VMA_LEN_IF_NOT_NULL(len)
2026 #endif
2027
2028 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
2029 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
2030 #ifndef VMA_NULLABLE
2031 #ifdef __clang__
2032 #define VMA_NULLABLE _Nullable
2033 #else
2034 #define VMA_NULLABLE
2035 #endif
2036 #endif
2037
2038 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
2039 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
2040 #ifndef VMA_NOT_NULL
2041 #ifdef __clang__
2042 #define VMA_NOT_NULL _Nonnull
2043 #else
2044 #define VMA_NOT_NULL
2045 #endif
2046 #endif
2047
2048 // If non-dispatchable handles are represented as pointers then we can give
2049 // then nullability annotations
2050 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
2051 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2052 #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
2053 #else
2054 #define VMA_NOT_NULL_NON_DISPATCHABLE
2055 #endif
2056 #endif
2057
2058 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
2059 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
2060 #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
2061 #else
2062 #define VMA_NULLABLE_NON_DISPATCHABLE
2063 #endif
2064 #endif
2065
2066 /** \struct VmaAllocator
2067 \brief Represents main object of this library initialized.
2068
2069 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
2070 Call function vmaDestroyAllocator() to destroy it.
2071
2072 It is recommended to create just one object of this type per `VkDevice` object,
2073 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
2074 */
2075 VK_DEFINE_HANDLE(VmaAllocator)
2076
2077 /// Callback function called after successful vkAllocateMemory.
2078 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
2079 VmaAllocator VMA_NOT_NULL allocator,
2080 uint32_t memoryType,
2081 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2082 VkDeviceSize size,
2083 void* VMA_NULLABLE pUserData);
2084 /// Callback function called before vkFreeMemory.
2085 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
2086 VmaAllocator VMA_NOT_NULL allocator,
2087 uint32_t memoryType,
2088 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
2089 VkDeviceSize size,
2090 void* VMA_NULLABLE pUserData);
2091
2092 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
2093
2094 Provided for informative purpose, e.g. to gather statistics about number of
2095 allocations or total amount of memory allocated in Vulkan.
2096
2097 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
2098 */
2099 typedef struct VmaDeviceMemoryCallbacks {
2100 /// Optional, can be null.
2101 PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
2102 /// Optional, can be null.
2103 PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
2104 /// Optional, can be null.
2105 void* VMA_NULLABLE pUserData;
2106 } VmaDeviceMemoryCallbacks;
2107
2108 /// Flags for created #VmaAllocator.
2109 typedef enum VmaAllocatorCreateFlagBits {
2110 /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you.
2111
2112 Using this flag may increase performance because internal mutexes are not used.
2113 */
2114 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
2115 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
2116
2117 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
2118 When it's `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
2119
2120 Using this extenion will automatically allocate dedicated blocks of memory for
2121 some buffers and images instead of suballocating place for them out of bigger
2122 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
2123 flag) when it is recommended by the driver. It may improve performance on some
2124 GPUs.
2125
2126 You may set this flag only if you found out that following device extensions are
2127 supported, you enabled them while creating Vulkan device passed as
2128 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
2129 library:
2130
2131 - VK_KHR_get_memory_requirements2 (device extension)
2132 - VK_KHR_dedicated_allocation (device extension)
2133
2134 When this flag is set, you can experience following warnings reported by Vulkan
2135 validation layer. You can ignore them.
2136
2137 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
2138 */
2139 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
2140 /**
2141 Enables usage of VK_KHR_bind_memory2 extension.
2142
2143 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
2144 When it's `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
2145
2146 You may set this flag only if you found out that this device extension is supported,
2147 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2148 and you want it to be used internally by this library.
2149
2150 The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
2151 which allow to pass a chain of `pNext` structures while binding.
2152 This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
2153 */
2154 VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
2155 /**
2156 Enables usage of VK_EXT_memory_budget extension.
2157
2158 You may set this flag only if you found out that this device extension is supported,
2159 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2160 and you want it to be used internally by this library, along with another instance extension
2161 VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
2162
2163 The extension provides query for current memory usage and budget, which will probably
2164 be more accurate than an estimation used by the library otherwise.
2165 */
2166 VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
2167 /**
2168 Enables usage of VK_AMD_device_coherent_memory extension.
2169
2170 You may set this flag only if you:
2171
2172 - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
2173 - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
2174 - want it to be used internally by this library.
2175
2176 The extension and accompanying device feature provide access to memory types with
2177 `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
2178 They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
2179
2180 When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
2181 To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,
2182 returning `VK_ERROR_FEATURE_NOT_PRESENT`.
2183 */
2184 VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
2185 /**
2186 Enables usage of "buffer device address" feature, which allows you to use function
2187 `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
2188
2189 You may set this flag only if you:
2190
2191 1. (For Vulkan version < 1.2) Found as available and enabled device extension
2192 VK_KHR_buffer_device_address.
2193 This extension is promoted to core Vulkan 1.2.
2194 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures*::bufferDeviceAddress`.
2195
2196 When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
2197 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
2198 allocated memory blocks wherever it might be needed.
2199
2200 For more information, see documentation chapter \ref enabling_buffer_device_address.
2201 */
2202 VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
2203
2204 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2205 } VmaAllocatorCreateFlagBits;
2206 typedef VkFlags VmaAllocatorCreateFlags;
2207
2208 /** \brief Pointers to some Vulkan functions - a subset used by the library.
2209
2210 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
2211 */
2212 typedef struct VmaVulkanFunctions {
2213 PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
2214 PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
2215 PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
2216 PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
2217 PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
2218 PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
2219 PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
2220 PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
2221 PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
2222 PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
2223 PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
2224 PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
2225 PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
2226 PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
2227 PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
2228 PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
2229 PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
2230 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
2231 PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
2232 PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
2233 #endif
2234 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
2235 PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
2236 PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
2237 #endif
2238 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
2239 PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
2240 #endif
2241 } VmaVulkanFunctions;
2242
2243 /// Flags to be used in VmaRecordSettings::flags.
2244 typedef enum VmaRecordFlagBits {
2245 /** \brief Enables flush after recording every function call.
2246
2247 Enable it if you expect your application to crash, which may leave recording file truncated.
2248 It may degrade performance though.
2249 */
2250 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
2251
2252 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2253 } VmaRecordFlagBits;
2254 typedef VkFlags VmaRecordFlags;
2255
2256 /// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
2257 typedef struct VmaRecordSettings
2258 {
2259 /// Flags for recording. Use #VmaRecordFlagBits enum.
2260 VmaRecordFlags flags;
2261 /** \brief Path to the file that should be written by the recording.
2262
2263 Suggested extension: "csv".
2264 If the file already exists, it will be overwritten.
2265 It will be opened for the whole time #VmaAllocator object is alive.
2266 If opening this file fails, creation of the whole allocator object fails.
2267 */
2268 const char* VMA_NOT_NULL pFilePath;
2269 } VmaRecordSettings;
2270
2271 /// Description of a Allocator to be created.
2272 typedef struct VmaAllocatorCreateInfo
2273 {
2274 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
2275 VmaAllocatorCreateFlags flags;
2276 /// Vulkan physical device.
2277 /** It must be valid throughout whole lifetime of created allocator. */
2278 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2279 /// Vulkan device.
2280 /** It must be valid throughout whole lifetime of created allocator. */
2281 VkDevice VMA_NOT_NULL device;
2282 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
2283 /** Set to 0 to use default, which is currently 256 MiB. */
2284 VkDeviceSize preferredLargeHeapBlockSize;
2285 /// Custom CPU memory allocation callbacks. Optional.
2286 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
2287 const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
2288 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
2289 /** Optional, can be null. */
2290 const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
2291 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2292
2293 This value is used only when you make allocations with
2294 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2295 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2296
2297 For example, if you double-buffer your command buffers, so resources used for
2298 rendering in previous frame may still be in use by the GPU at the moment you
2299 allocate resources needed for the current frame, set this value to 1.
2300
2301 If you want to allow any allocations other than used in the current frame to
2302 become lost, set this value to 0.
2303 */
2304 uint32_t frameInUseCount;
2305 /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap.
2306
2307 If not NULL, it must be a pointer to an array of
2308 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
2309 maximum number of bytes that can be allocated out of particular Vulkan memory
2310 heap.
2311
2312 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
2313 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
2314
2315 If there is a limit defined for a heap:
2316
2317 - If user tries to allocate more memory from that heap using this allocator,
2318 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2319 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
2320 value of this limit will be reported instead when using vmaGetMemoryProperties().
2321
2322 Warning! Using this feature may not be equivalent to installing a GPU with
2323 smaller amount of memory, because graphics driver doesn't necessary fail new
2324 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
2325 exceeded. It may return success and just silently migrate some device memory
2326 blocks to system RAM. This driver behavior can also be controlled using
2327 VK_AMD_memory_overallocation_behavior extension.
2328 */
2329 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
2330
2331 /** \brief Pointers to Vulkan functions. Can be null.
2332
2333 For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
2334 */
2335 const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
2336 /** \brief Parameters for recording of VMA calls. Can be null.
2337
2338 If not null, it enables recording of calls to VMA functions to a file.
2339 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
2340 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
2341 */
2342 const VmaRecordSettings* VMA_NULLABLE pRecordSettings;
2343 /** \brief Handle to Vulkan instance object.
2344
2345 Starting from version 3.0.0 this member is no longer optional, it must be set!
2346 */
2347 VkInstance VMA_NOT_NULL instance;
2348 /** \brief Optional. The highest version of Vulkan that the application is designed to use.
2349
2350 It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`.
2351 The patch version number specified is ignored. Only the major and minor versions are considered.
2352 It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
2353 Only versions 1.0 and 1.1 are supported by the current implementation.
2354 Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
2355 */
2356 uint32_t vulkanApiVersion;
2357 } VmaAllocatorCreateInfo;
2358
2359 /// Creates Allocator object.
2360 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2361 const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2362 VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2363
2364 /// Destroys allocator object.
2365 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2366 VmaAllocator VMA_NULLABLE allocator);
2367
2368 /** \brief Information about existing #VmaAllocator object.
2369 */
2370 typedef struct VmaAllocatorInfo
2371 {
2372 /** \brief Handle to Vulkan instance object.
2373
2374 This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
2375 */
2376 VkInstance VMA_NOT_NULL instance;
2377 /** \brief Handle to Vulkan physical device object.
2378
2379 This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
2380 */
2381 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2382 /** \brief Handle to Vulkan device object.
2383
2384 This is the same value as has been passed through VmaAllocatorCreateInfo::device.
2385 */
2386 VkDevice VMA_NOT_NULL device;
2387 } VmaAllocatorInfo;
2388
2389 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
2390
2391 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
2392 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
2393 */
2394 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2395
2396 /**
2397 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
2398 You can access it here, without fetching it again on your own.
2399 */
2400 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2401 VmaAllocator VMA_NOT_NULL allocator,
2402 const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2403
2404 /**
2405 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
2406 You can access it here, without fetching it again on your own.
2407 */
2408 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2409 VmaAllocator VMA_NOT_NULL allocator,
2410 const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2411
2412 /**
2413 \brief Given Memory Type Index, returns Property Flags of this memory type.
2414
2415 This is just a convenience function. Same information can be obtained using
2416 vmaGetMemoryProperties().
2417 */
2418 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2419 VmaAllocator VMA_NOT_NULL allocator,
2420 uint32_t memoryTypeIndex,
2421 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2422
2423 /** \brief Sets index of the current frame.
2424
2425 This function must be used if you make allocations with
2426 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
2427 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
2428 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
2429 become lost in the current frame.
2430 */
2431 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2432 VmaAllocator VMA_NOT_NULL allocator,
2433 uint32_t frameIndex);
2434
2435 /** \brief Calculated statistics of memory usage in entire allocator.
2436 */
2437 typedef struct VmaStatInfo
2438 {
2439 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
2440 uint32_t blockCount;
2441 /// Number of #VmaAllocation allocation objects allocated.
2442 uint32_t allocationCount;
2443 /// Number of free ranges of memory between allocations.
2444 uint32_t unusedRangeCount;
2445 /// Total number of bytes occupied by all allocations.
2446 VkDeviceSize usedBytes;
2447 /// Total number of bytes occupied by unused ranges.
2448 VkDeviceSize unusedBytes;
2449 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2450 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2451 } VmaStatInfo;
2452
2453 /// General statistics from current state of Allocator.
2454 typedef struct VmaStats
2455 {
2456 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2457 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2458 VmaStatInfo total;
2459 } VmaStats;
2460
2461 /** \brief Retrieves statistics from current state of the Allocator.
2462
2463 This function is called "calculate" not "get" because it has to traverse all
2464 internal data structures, so it may be quite slow. For faster but more brief statistics
2465 suitable to be called every frame or every allocation, use vmaGetBudget().
2466
2467 Note that when using allocator from multiple threads, returned information may immediately
2468 become outdated.
2469 */
2470 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2471 VmaAllocator VMA_NOT_NULL allocator,
2472 VmaStats* VMA_NOT_NULL pStats);
2473
2474 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
2475 */
2476 typedef struct VmaBudget
2477 {
2478 /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
2479 */
2480 VkDeviceSize blockBytes;
2481
2482 /** \brief Sum size of all allocations created in particular heap, in bytes.
2483
2484 Usually less or equal than `blockBytes`.
2485 Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
2486 available for new allocations or wasted due to fragmentation.
2487
2488 It might be greater than `blockBytes` if there are some allocations in lost state, as they account
2489 to this value as well.
2490 */
2491 VkDeviceSize allocationBytes;
2492
2493 /** \brief Estimated current memory usage of the program, in bytes.
2494
2495 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2496
2497 It might be different than `blockBytes` (usually higher) due to additional implicit objects
2498 also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
2499 `VkDeviceMemory` blocks allocated outside of this library, if any.
2500 */
2501 VkDeviceSize usage;
2502
2503 /** \brief Estimated amount of memory available to the program, in bytes.
2504
2505 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2506
2507 It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
2508 external to the program, like other programs also consuming system resources.
2509 Difference `budget - usage` is the amount of additional memory that can probably
2510 be allocated without problems. Exceeding the budget may result in various problems.
2511 */
2512 VkDeviceSize budget;
2513 } VmaBudget;
2514
2515 /** \brief Retrieves information about current memory budget for all memory heaps.
2516
2517 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
2518
2519 This function is called "get" not "calculate" because it is very fast, suitable to be called
2520 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
2521
2522 Note that when using allocator from multiple threads, returned information may immediately
2523 become outdated.
2524 */
2525 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2526 VmaAllocator VMA_NOT_NULL allocator,
2527 VmaBudget* VMA_NOT_NULL pBudget);
2528
2529 #ifndef VMA_STATS_STRING_ENABLED
2530 #define VMA_STATS_STRING_ENABLED 1
2531 #endif
2532
2533 #if VMA_STATS_STRING_ENABLED
2534
2535 /// Builds and returns statistics as string in JSON format.
2536 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2537 */
2538 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2539 VmaAllocator VMA_NOT_NULL allocator,
2540 char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2541 VkBool32 detailedMap);
2542
2543 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2544 VmaAllocator VMA_NOT_NULL allocator,
2545 char* VMA_NULLABLE pStatsString);
2546
2547 #endif // #if VMA_STATS_STRING_ENABLED
2548
2549 /** \struct VmaPool
2550 \brief Represents custom memory pool
2551
2552 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
2553 Call function vmaDestroyPool() to destroy it.
2554
2555 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
2556 */
2557 VK_DEFINE_HANDLE(VmaPool)
2558
2559 typedef enum VmaMemoryUsage
2560 {
2561 /** No intended memory usage specified.
2562 Use other members of VmaAllocationCreateInfo to specify your requirements.
2563 */
2564 VMA_MEMORY_USAGE_UNKNOWN = 0,
2565 /** Memory will be used on device only, so fast access from the device is preferred.
2566 It usually means device-local GPU (video) memory.
2567 No need to be mappable on host.
2568 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
2569
2570 Usage:
2571
2572 - Resources written and read by device, e.g. images used as attachments.
2573 - Resources transferred from host once (immutable) or infrequently and read by
2574 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
2575 (constant) buffers, and majority of other types of resources used on GPU.
2576
2577 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
2578 In such case, you are free to map it.
2579 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
2580 */
2581 VMA_MEMORY_USAGE_GPU_ONLY = 1,
2582 /** Memory will be mappable on host.
2583 It usually means CPU (system) memory.
2584 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2585 CPU access is typically uncached. Writes may be write-combined.
2586 Resources created in this pool may still be accessible to the device, but access to them can be slow.
2587 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2588
2589 Usage: Staging copy of resources used as transfer source.
2590 */
2591 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2592 /**
2593 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2594 CPU access is typically uncached. Writes may be write-combined.
2595
2596 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures (with LINEAR layout), vertex buffers, uniform buffers updated every frame or every draw call.
2597 */
2598 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2599 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2600 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2601
2602 Usage:
2603
2604 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2605 - 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.
2606 */
2607 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2608 /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
2609
2610 Usage: Staging copy of resources moved from GPU memory to CPU memory as part
2611 of custom paging/residency mechanism, to be moved back to GPU memory when needed.
2612 */
2613 VMA_MEMORY_USAGE_CPU_COPY = 5,
2614 /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
2615 Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
2616
2617 Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
2618
2619 Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2620 */
2621 VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
2622
2623 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2624 } VmaMemoryUsage;
2625
2626 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2627 typedef enum VmaAllocationCreateFlagBits {
2628 /** \brief Set this flag if the allocation should have its own memory block.
2629
2630 Use it for special, big resources, like fullscreen images used as attachments.
2631
2632 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2633 */
2634 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2635
2636 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2637
2638 If new allocation cannot be placed in any of the existing blocks, allocation
2639 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2640
2641 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2642 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2643
2644 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2645 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2646 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2647
2648 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2649
2650 Is it valid to use this flag for allocation made from memory type that is not
2651 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2652 useful if you need an allocation that is efficient to use on GPU
2653 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2654 support it (e.g. Intel GPU).
2655
2656 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2657 */
2658 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2659 /** Allocation created with this flag can become lost as a result of another
2660 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2661 must check it before use.
2662
2663 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2664 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2665
2666 For details about supporting lost allocations, see Lost Allocations
2667 chapter of User Guide on Main Page.
2668
2669 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2670 */
2671 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2672 /** While creating allocation using this flag, other allocations that were
2673 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2674
2675 For details about supporting lost allocations, see Lost Allocations
2676 chapter of User Guide on Main Page.
2677 */
2678 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2679 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2680 null-terminated string. Instead of copying pointer value, a local copy of the
2681 string is made and stored in allocation's `pUserData`. The string is automatically
2682 freed together with the allocation. It is also used in vmaBuildStatsString().
2683 */
2684 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2685 /** Allocation will be created from upper stack in a double stack pool.
2686
2687 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2688 */
2689 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2690 /** Create both buffer/image and allocation, but don't bind them together.
2691 It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
2692 The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
2693 Otherwise it is ignored.
2694 */
2695 VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
2696 /** Create allocation only if additional device memory required for it, if any, won't exceed
2697 memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2698 */
2699 VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
2700
2701 /** Allocation strategy that chooses smallest possible free range for the
2702 allocation.
2703 */
2704 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2705 /** Allocation strategy that chooses biggest possible free range for the
2706 allocation.
2707 */
2708 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2709 /** Allocation strategy that chooses first suitable free range for the
2710 allocation.
2711
2712 "First" doesn't necessarily means the one with smallest offset in memory,
2713 but rather the one that is easiest and fastest to find.
2714 */
2715 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2716
2717 /** Allocation strategy that tries to minimize memory usage.
2718 */
2719 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2720 /** Allocation strategy that tries to minimize allocation time.
2721 */
2722 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2723 /** Allocation strategy that tries to minimize memory fragmentation.
2724 */
2725 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2726
2727 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2728 */
2729 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2730 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2731 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2732 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2733
2734 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2735 } VmaAllocationCreateFlagBits;
2736 typedef VkFlags VmaAllocationCreateFlags;
2737
2738 typedef struct VmaAllocationCreateInfo
2739 {
2740 /// Use #VmaAllocationCreateFlagBits enum.
2741 VmaAllocationCreateFlags flags;
2742 /** \brief Intended usage of memory.
2743
2744 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2745 If `pool` is not null, this member is ignored.
2746 */
2747 VmaMemoryUsage usage;
2748 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2749
2750 Leave 0 if you specify memory requirements in other way. \n
2751 If `pool` is not null, this member is ignored.*/
2752 VkMemoryPropertyFlags requiredFlags;
2753 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2754
2755 Set to 0 if no additional flags are prefered. \n
2756 If `pool` is not null, this member is ignored. */
2757 VkMemoryPropertyFlags preferredFlags;
2758 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2759
2760 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2761 it meets other requirements specified by this structure, with no further
2762 restrictions on memory type index. \n
2763 If `pool` is not null, this member is ignored.
2764 */
2765 uint32_t memoryTypeBits;
2766 /** \brief Pool that this allocation should be created in.
2767
2768 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2769 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2770 */
2771 VmaPool VMA_NULLABLE pool;
2772 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2773
2774 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2775 null or pointer to a null-terminated string. The string will be then copied to
2776 internal buffer, so it doesn't need to be valid after allocation call.
2777 */
2778 void* VMA_NULLABLE pUserData;
2779 } VmaAllocationCreateInfo;
2780
2781 /**
2782 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2783
2784 This algorithm tries to find a memory type that:
2785
2786 - Is allowed by memoryTypeBits.
2787 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2788 - Matches intended usage.
2789 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2790
2791 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2792 from this function or any other allocating function probably means that your
2793 device doesn't support any memory type with requested features for the specific
2794 type of resource you want to use it for. Please check parameters of your
2795 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2796 */
2797 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2798 VmaAllocator VMA_NOT_NULL allocator,
2799 uint32_t memoryTypeBits,
2800 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2801 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2802
2803 /**
2804 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2805
2806 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2807 It internally creates a temporary, dummy buffer that never has memory bound.
2808 It is just a convenience function, equivalent to calling:
2809
2810 - `vkCreateBuffer`
2811 - `vkGetBufferMemoryRequirements`
2812 - `vmaFindMemoryTypeIndex`
2813 - `vkDestroyBuffer`
2814 */
2815 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2816 VmaAllocator VMA_NOT_NULL allocator,
2817 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2818 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2819 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2820
2821 /**
2822 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2823
2824 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2825 It internally creates a temporary, dummy image that never has memory bound.
2826 It is just a convenience function, equivalent to calling:
2827
2828 - `vkCreateImage`
2829 - `vkGetImageMemoryRequirements`
2830 - `vmaFindMemoryTypeIndex`
2831 - `vkDestroyImage`
2832 */
2833 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2834 VmaAllocator VMA_NOT_NULL allocator,
2835 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2836 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2837 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2838
2839 /// Flags to be passed as VmaPoolCreateInfo::flags.
2840 typedef enum VmaPoolCreateFlagBits {
2841 /** \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.
2842
2843 This is an optional optimization flag.
2844
2845 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2846 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2847 knows exact type of your allocations so it can handle Buffer-Image Granularity
2848 in the optimal way.
2849
2850 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2851 exact type of such allocations is not known, so allocator must be conservative
2852 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2853 (wasted memory). In that case, if you can make sure you always allocate only
2854 buffers and linear images or only optimal images out of this pool, use this flag
2855 to make allocator disregard Buffer-Image Granularity and so make allocations
2856 faster and more optimal.
2857 */
2858 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2859
2860 /** \brief Enables alternative, linear allocation algorithm in this pool.
2861
2862 Specify this flag to enable linear allocation algorithm, which always creates
2863 new allocations after last one and doesn't reuse space from allocations freed in
2864 between. It trades memory consumption for simplified algorithm and data
2865 structure, which has better performance and uses less memory for metadata.
2866
2867 By using this flag, you can achieve behavior of free-at-once, stack,
2868 ring buffer, and double stack. For details, see documentation chapter
2869 \ref linear_algorithm.
2870
2871 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2872
2873 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2874 */
2875 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2876
2877 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2878
2879 It operates on a tree of blocks, each having size that is a power of two and
2880 a half of its parent's size. Comparing to default algorithm, this one provides
2881 faster allocation and deallocation and decreased external fragmentation,
2882 at the expense of more memory wasted (internal fragmentation).
2883
2884 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2885 */
2886 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2887
2888 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2889 */
2890 VMA_POOL_CREATE_ALGORITHM_MASK =
2891 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2892 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2893
2894 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2895 } VmaPoolCreateFlagBits;
2896 typedef VkFlags VmaPoolCreateFlags;
2897
2898 /** \brief Describes parameter of created #VmaPool.
2899 */
2900 typedef struct VmaPoolCreateInfo {
2901 /** \brief Vulkan memory type index to allocate this pool from.
2902 */
2903 uint32_t memoryTypeIndex;
2904 /** \brief Use combination of #VmaPoolCreateFlagBits.
2905 */
2906 VmaPoolCreateFlags flags;
2907 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2908
2909 Specify nonzero to set explicit, constant size of memory blocks used by this
2910 pool.
2911
2912 Leave 0 to use default and let the library manage block sizes automatically.
2913 Sizes of particular blocks may vary.
2914 */
2915 VkDeviceSize blockSize;
2916 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2917
2918 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2919 */
2920 size_t minBlockCount;
2921 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2922
2923 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2924
2925 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2926 throughout whole lifetime of this pool.
2927 */
2928 size_t maxBlockCount;
2929 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2930
2931 This value is used only when you make allocations with
2932 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2933 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2934
2935 For example, if you double-buffer your command buffers, so resources used for
2936 rendering in previous frame may still be in use by the GPU at the moment you
2937 allocate resources needed for the current frame, set this value to 1.
2938
2939 If you want to allow any allocations other than used in the current frame to
2940 become lost, set this value to 0.
2941 */
2942 uint32_t frameInUseCount;
2943 } VmaPoolCreateInfo;
2944
2945 /** \brief Describes parameter of existing #VmaPool.
2946 */
2947 typedef struct VmaPoolStats {
2948 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2949 */
2950 VkDeviceSize size;
2951 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2952 */
2953 VkDeviceSize unusedSize;
2954 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2955 */
2956 size_t allocationCount;
2957 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2958 */
2959 size_t unusedRangeCount;
2960 /** \brief Size of the largest continuous free memory region available for new allocation.
2961
2962 Making a new allocation of that size is not guaranteed to succeed because of
2963 possible additional margin required to respect alignment and buffer/image
2964 granularity.
2965 */
2966 VkDeviceSize unusedRangeSizeMax;
2967 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2968 */
2969 size_t blockCount;
2970 } VmaPoolStats;
2971
2972 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2973
2974 @param allocator Allocator object.
2975 @param pCreateInfo Parameters of pool to create.
2976 @param[out] pPool Handle to created pool.
2977 */
2978 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
2979 VmaAllocator VMA_NOT_NULL allocator,
2980 const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
2981 VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
2982
2983 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2984 */
2985 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
2986 VmaAllocator VMA_NOT_NULL allocator,
2987 VmaPool VMA_NULLABLE pool);
2988
2989 /** \brief Retrieves statistics of existing #VmaPool object.
2990
2991 @param allocator Allocator object.
2992 @param pool Pool object.
2993 @param[out] pPoolStats Statistics of specified pool.
2994 */
2995 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
2996 VmaAllocator VMA_NOT_NULL allocator,
2997 VmaPool VMA_NOT_NULL pool,
2998 VmaPoolStats* VMA_NOT_NULL pPoolStats);
2999
3000 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
3001
3002 @param allocator Allocator object.
3003 @param pool Pool.
3004 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
3005 */
3006 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3007 VmaAllocator VMA_NOT_NULL allocator,
3008 VmaPool VMA_NOT_NULL pool,
3009 size_t* VMA_NULLABLE pLostAllocationCount);
3010
3011 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
3012
3013 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3014 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
3015 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3016
3017 Possible return values:
3018
3019 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
3020 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3021 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3022 `VMA_ASSERT` is also fired in that case.
3023 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3024 */
3025 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3026
3027 /** \brief Retrieves name of a custom pool.
3028
3029 After the call `ppName` is either null or points to an internally-owned null-terminated string
3030 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
3031 destroyed or its name is changed using vmaSetPoolName().
3032 */
3033 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3034 VmaAllocator VMA_NOT_NULL allocator,
3035 VmaPool VMA_NOT_NULL pool,
3036 const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3037
3038 /** \brief Sets name of a custom pool.
3039
3040 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
3041 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
3042 */
3043 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3044 VmaAllocator VMA_NOT_NULL allocator,
3045 VmaPool VMA_NOT_NULL pool,
3046 const char* VMA_NULLABLE pName);
3047
3048 /** \struct VmaAllocation
3049 \brief Represents single memory allocation.
3050
3051 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
3052 plus unique offset.
3053
3054 There are multiple ways to create such object.
3055 You need to fill structure VmaAllocationCreateInfo.
3056 For more information see [Choosing memory type](@ref choosing_memory_type).
3057
3058 Although the library provides convenience functions that create Vulkan buffer or image,
3059 allocate memory for it and bind them together,
3060 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
3061 Allocation object can exist without buffer/image bound,
3062 binding can be done manually by the user, and destruction of it can be done
3063 independently of destruction of the allocation.
3064
3065 The object also remembers its size and some other information.
3066 To retrieve this information, use function vmaGetAllocationInfo() and inspect
3067 returned structure VmaAllocationInfo.
3068
3069 Some kinds allocations can be in lost state.
3070 For more information, see [Lost allocations](@ref lost_allocations).
3071 */
3072 VK_DEFINE_HANDLE(VmaAllocation)
3073
3074 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
3075 */
3076 typedef struct VmaAllocationInfo {
3077 /** \brief Memory type index that this allocation was allocated from.
3078
3079 It never changes.
3080 */
3081 uint32_t memoryType;
3082 /** \brief Handle to Vulkan memory object.
3083
3084 Same memory object can be shared by multiple allocations.
3085
3086 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3087
3088 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
3089 */
3090 VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3091 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
3092
3093 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3094 */
3095 VkDeviceSize offset;
3096 /** \brief Size of this allocation, in bytes.
3097
3098 It never changes, unless allocation is lost.
3099 */
3100 VkDeviceSize size;
3101 /** \brief Pointer to the beginning of this allocation as mapped data.
3102
3103 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
3104 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
3105
3106 It can change after call to vmaMapMemory(), vmaUnmapMemory().
3107 It can also change after call to vmaDefragment() if this allocation is passed to the function.
3108 */
3109 void* VMA_NULLABLE pMappedData;
3110 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
3111
3112 It can change after call to vmaSetAllocationUserData() for this allocation.
3113 */
3114 void* VMA_NULLABLE pUserData;
3115 } VmaAllocationInfo;
3116
3117 /** \brief General purpose memory allocation.
3118
3119 @param[out] pAllocation Handle to allocated memory.
3120 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3121
3122 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3123
3124 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
3125 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
3126 */
3127 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3128 VmaAllocator VMA_NOT_NULL allocator,
3129 const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3130 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3131 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3132 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3133
3134 /** \brief General purpose memory allocation for multiple allocation objects at once.
3135
3136 @param allocator Allocator object.
3137 @param pVkMemoryRequirements Memory requirements for each allocation.
3138 @param pCreateInfo Creation parameters for each alloction.
3139 @param allocationCount Number of allocations to make.
3140 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
3141 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
3142
3143 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3144
3145 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
3146 It is just a general purpose allocation function able to make multiple allocations at once.
3147 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
3148
3149 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
3150 If any allocation fails, all allocations already made within this function call are also freed, so that when
3151 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
3152 */
3153 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3154 VmaAllocator VMA_NOT_NULL allocator,
3155 const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3156 const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3157 size_t allocationCount,
3158 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3159 VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3160
3161 /**
3162 @param[out] pAllocation Handle to allocated memory.
3163 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3164
3165 You should free the memory using vmaFreeMemory().
3166 */
3167 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3168 VmaAllocator VMA_NOT_NULL allocator,
3169 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3170 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3171 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3172 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3173
3174 /// Function similar to vmaAllocateMemoryForBuffer().
3175 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3176 VmaAllocator VMA_NOT_NULL allocator,
3177 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3178 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3179 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3180 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3181
3182 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
3183
3184 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
3185 */
3186 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3187 VmaAllocator VMA_NOT_NULL allocator,
3188 const VmaAllocation VMA_NULLABLE allocation);
3189
3190 /** \brief Frees memory and destroys multiple allocations.
3191
3192 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
3193 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
3194 vmaAllocateMemoryPages() and other functions.
3195 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
3196
3197 Allocations in `pAllocations` array can come from any memory pools and types.
3198 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
3199 */
3200 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3201 VmaAllocator VMA_NOT_NULL allocator,
3202 size_t allocationCount,
3203 const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3204
3205 /** \brief Deprecated.
3206
3207 \deprecated
3208 In version 2.2.0 it used to try to change allocation's size without moving or reallocating it.
3209 In current version it returns `VK_SUCCESS` only if `newSize` equals current allocation's size.
3210 Otherwise returns `VK_ERROR_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed.
3211 */
3212 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3213 VmaAllocator VMA_NOT_NULL allocator,
3214 VmaAllocation VMA_NOT_NULL allocation,
3215 VkDeviceSize newSize);
3216
3217 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
3218
3219 Current paramters of given allocation are returned in `pAllocationInfo`.
3220
3221 This function also atomically "touches" allocation - marks it as used in current frame,
3222 just like vmaTouchAllocation().
3223 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
3224
3225 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
3226 you can avoid calling it too often.
3227
3228 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
3229 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
3230 (e.g. due to defragmentation or allocation becoming lost).
3231 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
3232 */
3233 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3234 VmaAllocator VMA_NOT_NULL allocator,
3235 VmaAllocation VMA_NOT_NULL allocation,
3236 VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3237
3238 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
3239
3240 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3241 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
3242 It then also atomically "touches" the allocation - marks it as used in current frame,
3243 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
3244
3245 If the allocation is in lost state, the function returns `VK_FALSE`.
3246 Memory of such allocation, as well as buffer or image bound to it, should not be used.
3247 Lost allocation and the buffer/image still need to be destroyed.
3248
3249 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3250 this function always returns `VK_TRUE`.
3251 */
3252 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3253 VmaAllocator VMA_NOT_NULL allocator,
3254 VmaAllocation VMA_NOT_NULL allocation);
3255
3256 /** \brief Sets pUserData in given allocation to new value.
3257
3258 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
3259 pUserData must be either null, or pointer to a null-terminated string. The function
3260 makes local copy of the string and sets it as allocation's `pUserData`. String
3261 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
3262 you can free it after this call. String previously pointed by allocation's
3263 pUserData is freed from memory.
3264
3265 If the flag was not used, the value of pointer `pUserData` is just copied to
3266 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
3267 as a pointer, ordinal number or some handle to you own data.
3268 */
3269 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3270 VmaAllocator VMA_NOT_NULL allocator,
3271 VmaAllocation VMA_NOT_NULL allocation,
3272 void* VMA_NULLABLE pUserData);
3273
3274 /** \brief Creates new allocation that is in lost state from the beginning.
3275
3276 It can be useful if you need a dummy, non-null allocation.
3277
3278 You still need to destroy created object using vmaFreeMemory().
3279
3280 Returned allocation is not tied to any specific memory pool or memory type and
3281 not bound to any image or buffer. It has size = 0. It cannot be turned into
3282 a real, non-empty allocation.
3283 */
3284 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3285 VmaAllocator VMA_NOT_NULL allocator,
3286 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3287
3288 /** \brief Maps memory represented by given allocation and returns pointer to it.
3289
3290 Maps memory represented by given allocation to make it accessible to CPU code.
3291 When succeeded, `*ppData` contains pointer to first byte of this memory.
3292 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
3293 correctly offseted to the beginning of region assigned to this particular
3294 allocation.
3295
3296 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
3297 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
3298 multiple times simultaneously, it is safe to call this function on allocations
3299 assigned to the same memory block. Actual Vulkan memory will be mapped on first
3300 mapping and unmapped on last unmapping.
3301
3302 If the function succeeded, you must call vmaUnmapMemory() to unmap the
3303 allocation when mapping is no longer needed or before freeing the allocation, at
3304 the latest.
3305
3306 It also safe to call this function multiple times on the same allocation. You
3307 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
3308
3309 It is also safe to call this function on allocation created with
3310 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
3311 You must still call vmaUnmapMemory() same number of times as you called
3312 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
3313 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
3314
3315 This function fails when used on allocation made in memory type that is not
3316 `HOST_VISIBLE`.
3317
3318 This function always fails when called for allocation that was created with
3319 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
3320 mapped.
3321
3322 This function doesn't automatically flush or invalidate caches.
3323 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3324 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3325 */
3326 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3327 VmaAllocator VMA_NOT_NULL allocator,
3328 VmaAllocation VMA_NOT_NULL allocation,
3329 void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3330
3331 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
3332
3333 For details, see description of vmaMapMemory().
3334
3335 This function doesn't automatically flush or invalidate caches.
3336 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3337 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3338 */
3339 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3340 VmaAllocator VMA_NOT_NULL allocator,
3341 VmaAllocation VMA_NOT_NULL allocation);
3342
3343 /** \brief Flushes memory of given allocation.
3344
3345 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
3346 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
3347 Unmap operation doesn't do that automatically.
3348
3349 - `offset` must be relative to the beginning of allocation.
3350 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3351 - `offset` and `size` don't have to be aligned.
3352 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3353 - If `size` is 0, this call is ignored.
3354 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3355 this call is ignored.
3356
3357 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3358 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3359 Do not pass allocation's offset as `offset`!!!
3360
3361 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3362 called, otherwise `VK_SUCCESS`.
3363 */
3364 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3365 VmaAllocator VMA_NOT_NULL allocator,
3366 VmaAllocation VMA_NOT_NULL allocation,
3367 VkDeviceSize offset,
3368 VkDeviceSize size);
3369
3370 /** \brief Invalidates memory of given allocation.
3371
3372 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
3373 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
3374 Map operation doesn't do that automatically.
3375
3376 - `offset` must be relative to the beginning of allocation.
3377 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3378 - `offset` and `size` don't have to be aligned.
3379 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3380 - If `size` is 0, this call is ignored.
3381 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3382 this call is ignored.
3383
3384 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3385 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3386 Do not pass allocation's offset as `offset`!!!
3387
3388 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
3389 it is called, otherwise `VK_SUCCESS`.
3390 */
3391 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3392 VmaAllocator VMA_NOT_NULL allocator,
3393 VmaAllocation VMA_NOT_NULL allocation,
3394 VkDeviceSize offset,
3395 VkDeviceSize size);
3396
3397 /** \brief Flushes memory of given set of allocations.
3398
3399 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3400 For more information, see documentation of vmaFlushAllocation().
3401
3402 \param allocator
3403 \param allocationCount
3404 \param allocations
3405 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
3406 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
3407
3408 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3409 called, otherwise `VK_SUCCESS`.
3410 */
3411 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3412 VmaAllocator VMA_NOT_NULL allocator,
3413 uint32_t allocationCount,
3414 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3415 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3416 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3417
3418 /** \brief Invalidates memory of given set of allocations.
3419
3420 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3421 For more information, see documentation of vmaInvalidateAllocation().
3422
3423 \param allocator
3424 \param allocationCount
3425 \param allocations
3426 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
3427 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
3428
3429 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
3430 called, otherwise `VK_SUCCESS`.
3431 */
3432 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3433 VmaAllocator VMA_NOT_NULL allocator,
3434 uint32_t allocationCount,
3435 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3436 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3437 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3438
3439 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
3440
3441 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
3442
3443 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3444 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
3445 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3446
3447 Possible return values:
3448
3449 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
3450 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3451 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3452 `VMA_ASSERT` is also fired in that case.
3453 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3454 */
3455 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3456
3457 /** \struct VmaDefragmentationContext
3458 \brief Represents Opaque object that represents started defragmentation process.
3459
3460 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
3461 Call function vmaDefragmentationEnd() to destroy it.
3462 */
3463 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3464
3465 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
3466 typedef enum VmaDefragmentationFlagBits {
3467 VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
3468 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
3469 } VmaDefragmentationFlagBits;
3470 typedef VkFlags VmaDefragmentationFlags;
3471
3472 /** \brief Parameters for defragmentation.
3473
3474 To be used with function vmaDefragmentationBegin().
3475 */
3476 typedef struct VmaDefragmentationInfo2 {
3477 /** \brief Reserved for future use. Should be 0.
3478 */
3479 VmaDefragmentationFlags flags;
3480 /** \brief Number of allocations in `pAllocations` array.
3481 */
3482 uint32_t allocationCount;
3483 /** \brief Pointer to array of allocations that can be defragmented.
3484
3485 The array should have `allocationCount` elements.
3486 The array should not contain nulls.
3487 Elements in the array should be unique - same allocation cannot occur twice.
3488 It is safe to pass allocations that are in the lost state - they are ignored.
3489 All allocations not present in this array are considered non-moveable during this defragmentation.
3490 */
3491 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3492 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
3493
3494 The array should have `allocationCount` elements.
3495 You can pass null if you are not interested in this information.
3496 */
3497 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3498 /** \brief Numer of pools in `pPools` array.
3499 */
3500 uint32_t poolCount;
3501 /** \brief Either null or pointer to array of pools to be defragmented.
3502
3503 All the allocations in the specified pools can be moved during defragmentation
3504 and there is no way to check if they were really moved as in `pAllocationsChanged`,
3505 so you must query all the allocations in all these pools for new `VkDeviceMemory`
3506 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
3507 and images bound to them.
3508
3509 The array should have `poolCount` elements.
3510 The array should not contain nulls.
3511 Elements in the array should be unique - same pool cannot occur twice.
3512
3513 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
3514 It might be more efficient.
3515 */
3516 const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3517 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
3518
3519 `VK_WHOLE_SIZE` means no limit.
3520 */
3521 VkDeviceSize maxCpuBytesToMove;
3522 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
3523
3524 `UINT32_MAX` means no limit.
3525 */
3526 uint32_t maxCpuAllocationsToMove;
3527 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
3528
3529 `VK_WHOLE_SIZE` means no limit.
3530 */
3531 VkDeviceSize maxGpuBytesToMove;
3532 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
3533
3534 `UINT32_MAX` means no limit.
3535 */
3536 uint32_t maxGpuAllocationsToMove;
3537 /** \brief Optional. Command buffer where GPU copy commands will be posted.
3538
3539 If not null, it must be a valid command buffer handle that supports Transfer queue type.
3540 It must be in the recording state and outside of a render pass instance.
3541 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
3542
3543 Passing null means that only CPU defragmentation will be performed.
3544 */
3545 VkCommandBuffer VMA_NULLABLE commandBuffer;
3546 } VmaDefragmentationInfo2;
3547
3548 typedef struct VmaDefragmentationPassMoveInfo {
3549 VmaAllocation VMA_NOT_NULL allocation;
3550 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3551 VkDeviceSize offset;
3552 } VmaDefragmentationPassMoveInfo;
3553
3554 /** \brief Parameters for incremental defragmentation steps.
3555
3556 To be used with function vmaBeginDefragmentationPass().
3557 */
3558 typedef struct VmaDefragmentationPassInfo {
3559 uint32_t moveCount;
3560 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3561 } VmaDefragmentationPassInfo;
3562
3563 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
3564
3565 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3566 */
3567 typedef struct VmaDefragmentationInfo {
3568 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
3569
3570 Default is `VK_WHOLE_SIZE`, which means no limit.
3571 */
3572 VkDeviceSize maxBytesToMove;
3573 /** \brief Maximum number of allocations that can be moved to different place.
3574
3575 Default is `UINT32_MAX`, which means no limit.
3576 */
3577 uint32_t maxAllocationsToMove;
3578 } VmaDefragmentationInfo;
3579
3580 /** \brief Statistics returned by function vmaDefragment(). */
3581 typedef struct VmaDefragmentationStats {
3582 /// Total number of bytes that have been copied while moving allocations to different places.
3583 VkDeviceSize bytesMoved;
3584 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
3585 VkDeviceSize bytesFreed;
3586 /// Number of allocations that have been moved to different places.
3587 uint32_t allocationsMoved;
3588 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
3589 uint32_t deviceMemoryBlocksFreed;
3590 } VmaDefragmentationStats;
3591
3592 /** \brief Begins defragmentation process.
3593
3594 @param allocator Allocator object.
3595 @param pInfo Structure filled with parameters of defragmentation.
3596 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
3597 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
3598 @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.
3599
3600 Use this function instead of old, deprecated vmaDefragment().
3601
3602 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
3603
3604 - You should not use any of allocations passed as `pInfo->pAllocations` or
3605 any allocations that belong to pools passed as `pInfo->pPools`,
3606 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
3607 their data.
3608 - Some mutexes protecting internal data structures may be locked, so trying to
3609 make or free any allocations, bind buffers or images, map memory, or launch
3610 another simultaneous defragmentation in between may cause stall (when done on
3611 another thread) or deadlock (when done on the same thread), unless you are
3612 100% sure that defragmented allocations are in different pools.
3613 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
3614 They become valid after call to vmaDefragmentationEnd().
3615 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
3616 and make sure it finished execution before calling vmaDefragmentationEnd().
3617
3618 For more information and important limitations regarding defragmentation, see documentation chapter:
3619 [Defragmentation](@ref defragmentation).
3620 */
3621 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3622 VmaAllocator VMA_NOT_NULL allocator,
3623 const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3624 VmaDefragmentationStats* VMA_NULLABLE pStats,
3625 VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3626
3627 /** \brief Ends defragmentation process.
3628
3629 Use this function to finish defragmentation started by vmaDefragmentationBegin().
3630 It is safe to pass `context == null`. The function then does nothing.
3631 */
3632 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3633 VmaAllocator VMA_NOT_NULL allocator,
3634 VmaDefragmentationContext VMA_NULLABLE context);
3635
3636 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3637 VmaAllocator VMA_NOT_NULL allocator,
3638 VmaDefragmentationContext VMA_NULLABLE context,
3639 VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3640 );
3641 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3642 VmaAllocator VMA_NOT_NULL allocator,
3643 VmaDefragmentationContext VMA_NULLABLE context
3644 );
3645
3646 /** \brief Deprecated. Compacts memory by moving allocations.
3647
3648 @param pAllocations Array of allocations that can be moved during this compation.
3649 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
3650 @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.
3651 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
3652 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
3653 @return `VK_SUCCESS` if completed, negative error code in case of error.
3654
3655 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3656
3657 This function works by moving allocations to different places (different
3658 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
3659 usage. Only allocations that are in `pAllocations` array can be moved. All other
3660 allocations are considered nonmovable in this call. Basic rules:
3661
3662 - Only allocations made in memory types that have
3663 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
3664 flags can be compacted. You may pass other allocations but it makes no sense -
3665 these will never be moved.
3666 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
3667 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
3668 passed to this function that come from such pools are ignored.
3669 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
3670 created as dedicated allocations for any other reason are also ignored.
3671 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
3672 flag can be compacted. If not persistently mapped, memory will be mapped
3673 temporarily inside this function if needed.
3674 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
3675
3676 The function also frees empty `VkDeviceMemory` blocks.
3677
3678 Warning: This function may be time-consuming, so you shouldn't call it too often
3679 (like after every resource creation/destruction).
3680 You can call it on special occasions (like when reloading a game level or
3681 when you just destroyed a lot of objects). Calling it every frame may be OK, but
3682 you should measure that on your platform.
3683
3684 For more information, see [Defragmentation](@ref defragmentation) chapter.
3685 */
3686 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3687 VmaAllocator VMA_NOT_NULL allocator,
3688 const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3689 size_t allocationCount,
3690 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3691 const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3692 VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3693
3694 /** \brief Binds buffer to allocation.
3695
3696 Binds specified buffer to region of memory represented by specified allocation.
3697 Gets `VkDeviceMemory` handle and offset from the allocation.
3698 If you want to create a buffer, allocate memory for it and bind them together separately,
3699 you should use this function for binding instead of standard `vkBindBufferMemory()`,
3700 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3701 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3702 (which is illegal in Vulkan).
3703
3704 It is recommended to use function vmaCreateBuffer() instead of this one.
3705 */
3706 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3707 VmaAllocator VMA_NOT_NULL allocator,
3708 VmaAllocation VMA_NOT_NULL allocation,
3709 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3710
3711 /** \brief Binds buffer to allocation with additional parameters.
3712
3713 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3714 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
3715
3716 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
3717
3718 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3719 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3720 */
3721 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3722 VmaAllocator VMA_NOT_NULL allocator,
3723 VmaAllocation VMA_NOT_NULL allocation,
3724 VkDeviceSize allocationLocalOffset,
3725 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3726 const void* VMA_NULLABLE pNext);
3727
3728 /** \brief Binds image to allocation.
3729
3730 Binds specified image to region of memory represented by specified allocation.
3731 Gets `VkDeviceMemory` handle and offset from the allocation.
3732 If you want to create an image, allocate memory for it and bind them together separately,
3733 you should use this function for binding instead of standard `vkBindImageMemory()`,
3734 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3735 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3736 (which is illegal in Vulkan).
3737
3738 It is recommended to use function vmaCreateImage() instead of this one.
3739 */
3740 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3741 VmaAllocator VMA_NOT_NULL allocator,
3742 VmaAllocation VMA_NOT_NULL allocation,
3743 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3744
3745 /** \brief Binds image to allocation with additional parameters.
3746
3747 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3748 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
3749
3750 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
3751
3752 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3753 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3754 */
3755 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3756 VmaAllocator VMA_NOT_NULL allocator,
3757 VmaAllocation VMA_NOT_NULL allocation,
3758 VkDeviceSize allocationLocalOffset,
3759 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3760 const void* VMA_NULLABLE pNext);
3761
3762 /**
3763 @param[out] pBuffer Buffer that was created.
3764 @param[out] pAllocation Allocation that was created.
3765 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3766
3767 This function automatically:
3768
3769 -# Creates buffer.
3770 -# Allocates appropriate memory for it.
3771 -# Binds the buffer with the memory.
3772
3773 If any of these operations fail, buffer and allocation are not created,
3774 returned value is negative error code, *pBuffer and *pAllocation are null.
3775
3776 If the function succeeded, you must destroy both buffer and allocation when you
3777 no longer need them using either convenience function vmaDestroyBuffer() or
3778 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3779
3780 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3781 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3782 it requires or prefers the new buffer to have dedicated allocation. If yes,
3783 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3784 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3785 allocation for this buffer, just like when using
3786 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3787 */
3788 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3789 VmaAllocator VMA_NOT_NULL allocator,
3790 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3791 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3792 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3793 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3794 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3795
3796 /** \brief Destroys Vulkan buffer and frees allocated memory.
3797
3798 This is just a convenience function equivalent to:
3799
3800 \code
3801 vkDestroyBuffer(device, buffer, allocationCallbacks);
3802 vmaFreeMemory(allocator, allocation);
3803 \endcode
3804
3805 It it safe to pass null as buffer and/or allocation.
3806 */
3807 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3808 VmaAllocator VMA_NOT_NULL allocator,
3809 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3810 VmaAllocation VMA_NULLABLE allocation);
3811
3812 /// Function similar to vmaCreateBuffer().
3813 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3814 VmaAllocator VMA_NOT_NULL allocator,
3815 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3816 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3817 VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3818 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3819 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3820
3821 /** \brief Destroys Vulkan image and frees allocated memory.
3822
3823 This is just a convenience function equivalent to:
3824
3825 \code
3826 vkDestroyImage(device, image, allocationCallbacks);
3827 vmaFreeMemory(allocator, allocation);
3828 \endcode
3829
3830 It it safe to pass null as image and/or allocation.
3831 */
3832 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3833 VmaAllocator VMA_NOT_NULL allocator,
3834 VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3835 VmaAllocation VMA_NULLABLE allocation);
3836
3837 #ifdef __cplusplus
3838 }
3839 #endif
3840
3841 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3842
3843 // For Visual Studio IntelliSense.
3844 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3845 #define VMA_IMPLEMENTATION
3846 #endif
3847
3848 #ifdef VMA_IMPLEMENTATION
3849 #undef VMA_IMPLEMENTATION
3850
3851 #include <cstdint>
3852 #include <cstdlib>
3853 #include <cstring>
3854 #include <utility>
3855
3856 /*******************************************************************************
3857 CONFIGURATION SECTION
3858
3859 Define some of these macros before each #include of this header or change them
3860 here if you need other then default behavior depending on your environment.
3861 */
3862
3863 /*
3864 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3865 internally, like:
3866
3867 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3868 */
3869 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3870 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3871 #endif
3872
3873 /*
3874 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3875 internally, like:
3876
3877 vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3878 */
3879 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3880 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3881 #endif
3882
3883 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3884 //#define VMA_USE_STL_CONTAINERS 1
3885
3886 /* Set this macro to 1 to make the library including and using STL containers:
3887 std::pair, std::vector, std::list, std::unordered_map.
3888
3889 Set it to 0 or undefined to make the library using its own implementation of
3890 the containers.
3891 */
3892 #if VMA_USE_STL_CONTAINERS
3893 #define VMA_USE_STL_VECTOR 1
3894 #define VMA_USE_STL_UNORDERED_MAP 1
3895 #define VMA_USE_STL_LIST 1
3896 #endif
3897
3898 #ifndef VMA_USE_STL_SHARED_MUTEX
3899 // Compiler conforms to C++17.
3900 #if __cplusplus >= 201703L
3901 #define VMA_USE_STL_SHARED_MUTEX 1
3902 // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
3903 // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
3904 // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
3905 #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
3906 #define VMA_USE_STL_SHARED_MUTEX 1
3907 #else
3908 #define VMA_USE_STL_SHARED_MUTEX 0
3909 #endif
3910 #endif
3911
3912 /*
3913 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
3914 Library has its own container implementation.
3915 */
3916 #if VMA_USE_STL_VECTOR
3917 #include <vector>
3918 #endif
3919
3920 #if VMA_USE_STL_UNORDERED_MAP
3921 #include <unordered_map>
3922 #endif
3923
3924 #if VMA_USE_STL_LIST
3925 #include <list>
3926 #endif
3927
3928 /*
3929 Following headers are used in this CONFIGURATION section only, so feel free to
3930 remove them if not needed.
3931 */
3932 #include <cassert> // for assert
3933 #include <algorithm> // for min, max
3934 #include <mutex>
3935
3936 #ifndef VMA_NULL
3937 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3938 #define VMA_NULL nullptr
3939 #endif
3940
3941 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3942 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3943 void *aligned_alloc(size_t alignment, size_t size)
3944 {
3945 // alignment must be >= sizeof(void*)
3946 if(alignment < sizeof(void*))
3947 {
3948 alignment = sizeof(void*);
3949 }
3950
3951 return memalign(alignment, size);
3952 }
3953 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
3954 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3955 void *aligned_alloc(size_t alignment, size_t size)
3956 {
3957 // alignment must be >= sizeof(void*)
3958 if(alignment < sizeof(void*))
3959 {
3960 alignment = sizeof(void*);
3961 }
3962
3963 void *pointer;
3964 if(posix_memalign(&pointer, alignment, size) == 0)
3965 return pointer;
3966 return VMA_NULL;
3967 }
3968 #endif
3969
3970 // If your compiler is not compatible with C++11 and definition of
3971 // aligned_alloc() function is missing, uncommeting following line may help:
3972
3973 //#include <malloc.h>
3974
3975 // Normal assert to check for programmer's errors, especially in Debug configuration.
3976 #ifndef VMA_ASSERT
3977 #ifdef NDEBUG
3978 #define VMA_ASSERT(expr)
3979 #else
3980 #define VMA_ASSERT(expr) assert(expr)
3981 #endif
3982 #endif
3983
3984 // Assert that will be called very often, like inside data structures e.g. operator[].
3985 // Making it non-empty can make program slow.
3986 #ifndef VMA_HEAVY_ASSERT
3987 #ifdef NDEBUG
3988 #define VMA_HEAVY_ASSERT(expr)
3989 #else
3990 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3991 #endif
3992 #endif
3993
3994 #ifndef VMA_ALIGN_OF
3995 #define VMA_ALIGN_OF(type) (__alignof(type))
3996 #endif
3997
3998 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
3999 #if defined(_WIN32)
4000 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
4001 #else
4002 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
4003 #endif
4004 #endif
4005
4006 #ifndef VMA_SYSTEM_FREE
4007 #if defined(_WIN32)
4008 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
4009 #else
4010 #define VMA_SYSTEM_FREE(ptr) free(ptr)
4011 #endif
4012 #endif
4013
4014 #ifndef VMA_MIN
4015 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4016 #endif
4017
4018 #ifndef VMA_MAX
4019 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4020 #endif
4021
4022 #ifndef VMA_SWAP
4023 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4024 #endif
4025
4026 #ifndef VMA_SORT
4027 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4028 #endif
4029
4030 #ifndef VMA_DEBUG_LOG
4031 #define VMA_DEBUG_LOG(format, ...)
4032 /*
4033 #define VMA_DEBUG_LOG(format, ...) do { \
4034 printf(format, __VA_ARGS__); \
4035 printf("\n"); \
4036 } while(false)
4037 */
4038 #endif
4039
4040 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4041 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)4042 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4043 {
4044 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4045 }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)4046 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4047 {
4048 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4049 }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)4050 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4051 {
4052 snprintf(outStr, strLen, "%p", ptr);
4053 }
4054 #endif
4055
4056 #ifndef VMA_MUTEX
4057 class VmaMutex
4058 {
4059 public:
Lock()4060 void Lock() { m_Mutex.lock(); }
Unlock()4061 void Unlock() { m_Mutex.unlock(); }
TryLock()4062 bool TryLock() { return m_Mutex.try_lock(); }
4063 private:
4064 std::mutex m_Mutex;
4065 };
4066 #define VMA_MUTEX VmaMutex
4067 #endif
4068
4069 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4070 #ifndef VMA_RW_MUTEX
4071 #if VMA_USE_STL_SHARED_MUTEX
4072 // Use std::shared_mutex from C++17.
4073 #include <shared_mutex>
4074 class VmaRWMutex
4075 {
4076 public:
LockRead()4077 void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()4078 void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()4079 bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()4080 void LockWrite() { m_Mutex.lock(); }
UnlockWrite()4081 void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()4082 bool TryLockWrite() { return m_Mutex.try_lock(); }
4083 private:
4084 std::shared_mutex m_Mutex;
4085 };
4086 #define VMA_RW_MUTEX VmaRWMutex
4087 #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4088 // Use SRWLOCK from WinAPI.
4089 // Minimum supported client = Windows Vista, server = Windows Server 2008.
4090 class VmaRWMutex
4091 {
4092 public:
VmaRWMutex()4093 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()4094 void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()4095 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()4096 bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()4097 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()4098 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()4099 bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4100 private:
4101 SRWLOCK m_Lock;
4102 };
4103 #define VMA_RW_MUTEX VmaRWMutex
4104 #else
4105 // Less efficient fallback: Use normal mutex.
4106 class VmaRWMutex
4107 {
4108 public:
LockRead()4109 void LockRead() { m_Mutex.Lock(); }
UnlockRead()4110 void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()4111 bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()4112 void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()4113 void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()4114 bool TryLockWrite() { return m_Mutex.TryLock(); }
4115 private:
4116 VMA_MUTEX m_Mutex;
4117 };
4118 #define VMA_RW_MUTEX VmaRWMutex
4119 #endif // #if VMA_USE_STL_SHARED_MUTEX
4120 #endif // #ifndef VMA_RW_MUTEX
4121
4122 /*
4123 If providing your own implementation, you need to implement a subset of std::atomic.
4124 */
4125 #ifndef VMA_ATOMIC_UINT32
4126 #include <atomic>
4127 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4128 #endif
4129
4130 #ifndef VMA_ATOMIC_UINT64
4131 #include <atomic>
4132 #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4133 #endif
4134
4135 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4136 /**
4137 Every allocation will have its own memory block.
4138 Define to 1 for debugging purposes only.
4139 */
4140 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4141 #endif
4142
4143 #ifndef VMA_DEBUG_ALIGNMENT
4144 /**
4145 Minimum alignment of all allocations, in bytes.
4146 Set to more than 1 for debugging purposes only. Must be power of two.
4147 */
4148 #define VMA_DEBUG_ALIGNMENT (1)
4149 #endif
4150
4151 #ifndef VMA_DEBUG_MARGIN
4152 /**
4153 Minimum margin before and after every allocation, in bytes.
4154 Set nonzero for debugging purposes only.
4155 */
4156 #define VMA_DEBUG_MARGIN (0)
4157 #endif
4158
4159 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4160 /**
4161 Define this macro to 1 to automatically fill new allocations and destroyed
4162 allocations with some bit pattern.
4163 */
4164 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4165 #endif
4166
4167 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4168 /**
4169 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
4170 enable writing magic value to the margin before and after every allocation and
4171 validating it, so that memory corruptions (out-of-bounds writes) are detected.
4172 */
4173 #define VMA_DEBUG_DETECT_CORRUPTION (0)
4174 #endif
4175
4176 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4177 /**
4178 Set this to 1 for debugging purposes only, to enable single mutex protecting all
4179 entry calls to the library. Can be useful for debugging multithreading issues.
4180 */
4181 #define VMA_DEBUG_GLOBAL_MUTEX (0)
4182 #endif
4183
4184 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4185 /**
4186 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
4187 Set to more than 1 for debugging purposes only. Must be power of two.
4188 */
4189 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4190 #endif
4191
4192 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4193 /// Maximum size of a memory heap in Vulkan to consider it "small".
4194 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4195 #endif
4196
4197 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4198 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
4199 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4200 #endif
4201
4202 #ifndef VMA_CLASS_NO_COPY
4203 #define VMA_CLASS_NO_COPY(className) \
4204 private: \
4205 className(const className&) = delete; \
4206 className& operator=(const className&) = delete;
4207 #endif
4208
4209 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4210
4211 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4212 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4213
4214 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4215 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4216
4217 /*******************************************************************************
4218 END OF CONFIGURATION
4219 */
4220
4221 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4222
4223 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4224 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4225 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4226
4227 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4228
4229 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4230 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4231
4232 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)4233 static inline uint32_t VmaCountBitsSet(uint32_t v)
4234 {
4235 uint32_t c = v - ((v >> 1) & 0x55555555);
4236 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4237 c = ((c >> 4) + c) & 0x0F0F0F0F;
4238 c = ((c >> 8) + c) & 0x00FF00FF;
4239 c = ((c >> 16) + c) & 0x0000FFFF;
4240 return c;
4241 }
4242
4243 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4244 // Use types like uint32_t, uint64_t as T.
4245 template <typename T>
VmaAlignUp(T val,T align)4246 static inline T VmaAlignUp(T val, T align)
4247 {
4248 return (val + align - 1) / align * align;
4249 }
4250 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4251 // Use types like uint32_t, uint64_t as T.
4252 template <typename T>
VmaAlignDown(T val,T align)4253 static inline T VmaAlignDown(T val, T align)
4254 {
4255 return val / align * align;
4256 }
4257
4258 // Division with mathematical rounding to nearest number.
4259 template <typename T>
VmaRoundDiv(T x,T y)4260 static inline T VmaRoundDiv(T x, T y)
4261 {
4262 return (x + (y / (T)2)) / y;
4263 }
4264
4265 /*
4266 Returns true if given number is a power of two.
4267 T must be unsigned integer number or signed integer but always nonnegative.
4268 For 0 returns true.
4269 */
4270 template <typename T>
VmaIsPow2(T x)4271 inline bool VmaIsPow2(T x)
4272 {
4273 return (x & (x-1)) == 0;
4274 }
4275
4276 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)4277 static inline uint32_t VmaNextPow2(uint32_t v)
4278 {
4279 v--;
4280 v |= v >> 1;
4281 v |= v >> 2;
4282 v |= v >> 4;
4283 v |= v >> 8;
4284 v |= v >> 16;
4285 v++;
4286 return v;
4287 }
VmaNextPow2(uint64_t v)4288 static inline uint64_t VmaNextPow2(uint64_t v)
4289 {
4290 v--;
4291 v |= v >> 1;
4292 v |= v >> 2;
4293 v |= v >> 4;
4294 v |= v >> 8;
4295 v |= v >> 16;
4296 v |= v >> 32;
4297 v++;
4298 return v;
4299 }
4300
4301 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)4302 static inline uint32_t VmaPrevPow2(uint32_t v)
4303 {
4304 v |= v >> 1;
4305 v |= v >> 2;
4306 v |= v >> 4;
4307 v |= v >> 8;
4308 v |= v >> 16;
4309 v = v ^ (v >> 1);
4310 return v;
4311 }
VmaPrevPow2(uint64_t v)4312 static inline uint64_t VmaPrevPow2(uint64_t v)
4313 {
4314 v |= v >> 1;
4315 v |= v >> 2;
4316 v |= v >> 4;
4317 v |= v >> 8;
4318 v |= v >> 16;
4319 v |= v >> 32;
4320 v = v ^ (v >> 1);
4321 return v;
4322 }
4323
VmaStrIsEmpty(const char * pStr)4324 static inline bool VmaStrIsEmpty(const char* pStr)
4325 {
4326 return pStr == VMA_NULL || *pStr == '\0';
4327 }
4328
4329 #if VMA_STATS_STRING_ENABLED
4330
VmaAlgorithmToStr(uint32_t algorithm)4331 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4332 {
4333 switch(algorithm)
4334 {
4335 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
4336 return "Linear";
4337 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
4338 return "Buddy";
4339 case 0:
4340 return "Default";
4341 default:
4342 VMA_ASSERT(0);
4343 return "";
4344 }
4345 }
4346
4347 #endif // #if VMA_STATS_STRING_ENABLED
4348
4349 #ifndef VMA_SORT
4350
4351 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)4352 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4353 {
4354 Iterator centerValue = end; --centerValue;
4355 Iterator insertIndex = beg;
4356 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4357 {
4358 if(cmp(*memTypeIndex, *centerValue))
4359 {
4360 if(insertIndex != memTypeIndex)
4361 {
4362 VMA_SWAP(*memTypeIndex, *insertIndex);
4363 }
4364 ++insertIndex;
4365 }
4366 }
4367 if(insertIndex != centerValue)
4368 {
4369 VMA_SWAP(*insertIndex, *centerValue);
4370 }
4371 return insertIndex;
4372 }
4373
4374 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)4375 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4376 {
4377 if(beg < end)
4378 {
4379 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4380 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4381 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4382 }
4383 }
4384
4385 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4386
4387 #endif // #ifndef VMA_SORT
4388
4389 /*
4390 Returns true if two memory blocks occupy overlapping pages.
4391 ResourceA must be in less memory offset than ResourceB.
4392
4393 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4394 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4395 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)4396 static inline bool VmaBlocksOnSamePage(
4397 VkDeviceSize resourceAOffset,
4398 VkDeviceSize resourceASize,
4399 VkDeviceSize resourceBOffset,
4400 VkDeviceSize pageSize)
4401 {
4402 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4403 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4404 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4405 VkDeviceSize resourceBStart = resourceBOffset;
4406 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4407 return resourceAEndPage == resourceBStartPage;
4408 }
4409
4410 enum VmaSuballocationType
4411 {
4412 VMA_SUBALLOCATION_TYPE_FREE = 0,
4413 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4414 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4415 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4416 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4417 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4418 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4419 };
4420
4421 /*
4422 Returns true if given suballocation types could conflict and must respect
4423 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4424 or linear image and another one is optimal image. If type is unknown, behave
4425 conservatively.
4426 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)4427 static inline bool VmaIsBufferImageGranularityConflict(
4428 VmaSuballocationType suballocType1,
4429 VmaSuballocationType suballocType2)
4430 {
4431 if(suballocType1 > suballocType2)
4432 {
4433 VMA_SWAP(suballocType1, suballocType2);
4434 }
4435
4436 switch(suballocType1)
4437 {
4438 case VMA_SUBALLOCATION_TYPE_FREE:
4439 return false;
4440 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4441 return true;
4442 case VMA_SUBALLOCATION_TYPE_BUFFER:
4443 return
4444 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4445 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4446 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4447 return
4448 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4449 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4450 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4451 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4452 return
4453 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4454 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4455 return false;
4456 default:
4457 VMA_ASSERT(0);
4458 return true;
4459 }
4460 }
4461
VmaWriteMagicValue(void * pData,VkDeviceSize offset)4462 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4463 {
4464 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4465 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4466 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4467 for(size_t i = 0; i < numberCount; ++i, ++pDst)
4468 {
4469 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4470 }
4471 #else
4472 // no-op
4473 #endif
4474 }
4475
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)4476 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4477 {
4478 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4479 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4480 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4481 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4482 {
4483 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4484 {
4485 return false;
4486 }
4487 }
4488 #endif
4489 return true;
4490 }
4491
4492 /*
4493 Fills structure with parameters of an example buffer to be used for transfers
4494 during GPU memory defragmentation.
4495 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)4496 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4497 {
4498 memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4499 outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4500 outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4501 outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4502 }
4503
4504 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4505 struct VmaMutexLock
4506 {
VMA_CLASS_NO_COPYVmaMutexLock4507 VMA_CLASS_NO_COPY(VmaMutexLock)
4508 public:
4509 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4510 m_pMutex(useMutex ? &mutex : VMA_NULL)
4511 { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock4512 ~VmaMutexLock()
4513 { if(m_pMutex) { m_pMutex->Unlock(); } }
4514 private:
4515 VMA_MUTEX* m_pMutex;
4516 };
4517
4518 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4519 struct VmaMutexLockRead
4520 {
VMA_CLASS_NO_COPYVmaMutexLockRead4521 VMA_CLASS_NO_COPY(VmaMutexLockRead)
4522 public:
4523 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4524 m_pMutex(useMutex ? &mutex : VMA_NULL)
4525 { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead4526 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4527 private:
4528 VMA_RW_MUTEX* m_pMutex;
4529 };
4530
4531 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4532 struct VmaMutexLockWrite
4533 {
VMA_CLASS_NO_COPYVmaMutexLockWrite4534 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4535 public:
4536 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4537 m_pMutex(useMutex ? &mutex : VMA_NULL)
4538 { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite4539 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4540 private:
4541 VMA_RW_MUTEX* m_pMutex;
4542 };
4543
4544 #if VMA_DEBUG_GLOBAL_MUTEX
4545 static VMA_MUTEX gDebugGlobalMutex;
4546 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4547 #else
4548 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4549 #endif
4550
4551 // Minimum size of a free suballocation to register it in the free suballocation collection.
4552 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4553
4554 /*
4555 Performs binary search and returns iterator to first element that is greater or
4556 equal to (key), according to comparison (cmp).
4557
4558 Cmp should return true if first argument is less than second argument.
4559
4560 Returned value is the found element, if present in the collection or place where
4561 new element with value (key) should be inserted.
4562 */
4563 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)4564 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4565 {
4566 size_t down = 0, up = (end - beg);
4567 while(down < up)
4568 {
4569 const size_t mid = (down + up) / 2;
4570 if(cmp(*(beg+mid), key))
4571 {
4572 down = mid + 1;
4573 }
4574 else
4575 {
4576 up = mid;
4577 }
4578 }
4579 return beg + down;
4580 }
4581
4582 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)4583 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4584 {
4585 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4586 beg, end, value, cmp);
4587 if(it == end ||
4588 (!cmp(*it, value) && !cmp(value, *it)))
4589 {
4590 return it;
4591 }
4592 return end;
4593 }
4594
4595 /*
4596 Returns true if all pointers in the array are not-null and unique.
4597 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4598 T must be pointer type, e.g. VmaAllocation, VmaPool.
4599 */
4600 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)4601 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4602 {
4603 for(uint32_t i = 0; i < count; ++i)
4604 {
4605 const T iPtr = arr[i];
4606 if(iPtr == VMA_NULL)
4607 {
4608 return false;
4609 }
4610 for(uint32_t j = i + 1; j < count; ++j)
4611 {
4612 if(iPtr == arr[j])
4613 {
4614 return false;
4615 }
4616 }
4617 }
4618 return true;
4619 }
4620
4621 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)4622 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4623 {
4624 newStruct->pNext = mainStruct->pNext;
4625 mainStruct->pNext = newStruct;
4626 }
4627
4628 ////////////////////////////////////////////////////////////////////////////////
4629 // Memory allocation
4630
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)4631 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4632 {
4633 if((pAllocationCallbacks != VMA_NULL) &&
4634 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4635 {
4636 return (*pAllocationCallbacks->pfnAllocation)(
4637 pAllocationCallbacks->pUserData,
4638 size,
4639 alignment,
4640 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4641 }
4642 else
4643 {
4644 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4645 }
4646 }
4647
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)4648 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4649 {
4650 if((pAllocationCallbacks != VMA_NULL) &&
4651 (pAllocationCallbacks->pfnFree != VMA_NULL))
4652 {
4653 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4654 }
4655 else
4656 {
4657 VMA_SYSTEM_FREE(ptr);
4658 }
4659 }
4660
4661 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)4662 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4663 {
4664 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4665 }
4666
4667 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)4668 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4669 {
4670 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4671 }
4672
4673 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4674
4675 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4676
4677 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)4678 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4679 {
4680 ptr->~T();
4681 VmaFree(pAllocationCallbacks, ptr);
4682 }
4683
4684 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)4685 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4686 {
4687 if(ptr != VMA_NULL)
4688 {
4689 for(size_t i = count; i--; )
4690 {
4691 ptr[i].~T();
4692 }
4693 VmaFree(pAllocationCallbacks, ptr);
4694 }
4695 }
4696
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)4697 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4698 {
4699 if(srcStr != VMA_NULL)
4700 {
4701 const size_t len = strlen(srcStr);
4702 char* const result = vma_new_array(allocs, char, len + 1);
4703 memcpy(result, srcStr, len + 1);
4704 return result;
4705 }
4706 else
4707 {
4708 return VMA_NULL;
4709 }
4710 }
4711
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)4712 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4713 {
4714 if(str != VMA_NULL)
4715 {
4716 const size_t len = strlen(str);
4717 vma_delete_array(allocs, str, len + 1);
4718 }
4719 }
4720
4721 // STL-compatible allocator.
4722 template<typename T>
4723 class VmaStlAllocator
4724 {
4725 public:
4726 const VkAllocationCallbacks* const m_pCallbacks;
4727 typedef T value_type;
4728
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)4729 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)4730 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4731
allocate(size_t n)4732 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)4733 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4734
4735 template<typename U>
4736 bool operator==(const VmaStlAllocator<U>& rhs) const
4737 {
4738 return m_pCallbacks == rhs.m_pCallbacks;
4739 }
4740 template<typename U>
4741 bool operator!=(const VmaStlAllocator<U>& rhs) const
4742 {
4743 return m_pCallbacks != rhs.m_pCallbacks;
4744 }
4745
4746 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4747 };
4748
4749 #if VMA_USE_STL_VECTOR
4750
4751 #define VmaVector std::vector
4752
4753 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)4754 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4755 {
4756 vec.insert(vec.begin() + index, item);
4757 }
4758
4759 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)4760 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4761 {
4762 vec.erase(vec.begin() + index);
4763 }
4764
4765 #else // #if VMA_USE_STL_VECTOR
4766
4767 /* Class with interface compatible with subset of std::vector.
4768 T must be POD because constructors and destructors are not called and memcpy is
4769 used for these objects. */
4770 template<typename T, typename AllocatorT>
4771 class VmaVector
4772 {
4773 public:
4774 typedef T value_type;
4775
VmaVector(const AllocatorT & allocator)4776 VmaVector(const AllocatorT& allocator) :
4777 m_Allocator(allocator),
4778 m_pArray(VMA_NULL),
4779 m_Count(0),
4780 m_Capacity(0)
4781 {
4782 }
4783
VmaVector(size_t count,const AllocatorT & allocator)4784 VmaVector(size_t count, const AllocatorT& allocator) :
4785 m_Allocator(allocator),
4786 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4787 m_Count(count),
4788 m_Capacity(count)
4789 {
4790 }
4791
4792 // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4793 // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)4794 VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4795 : VmaVector(count, allocator) {}
4796
VmaVector(const VmaVector<T,AllocatorT> & src)4797 VmaVector(const VmaVector<T, AllocatorT>& src) :
4798 m_Allocator(src.m_Allocator),
4799 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4800 m_Count(src.m_Count),
4801 m_Capacity(src.m_Count)
4802 {
4803 if(m_Count != 0)
4804 {
4805 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4806 }
4807 }
4808
~VmaVector()4809 ~VmaVector()
4810 {
4811 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4812 }
4813
4814 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4815 {
4816 if(&rhs != this)
4817 {
4818 resize(rhs.m_Count);
4819 if(m_Count != 0)
4820 {
4821 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4822 }
4823 }
4824 return *this;
4825 }
4826
empty()4827 bool empty() const { return m_Count == 0; }
size()4828 size_t size() const { return m_Count; }
data()4829 T* data() { return m_pArray; }
data()4830 const T* data() const { return m_pArray; }
4831
4832 T& operator[](size_t index)
4833 {
4834 VMA_HEAVY_ASSERT(index < m_Count);
4835 return m_pArray[index];
4836 }
4837 const T& operator[](size_t index) const
4838 {
4839 VMA_HEAVY_ASSERT(index < m_Count);
4840 return m_pArray[index];
4841 }
4842
front()4843 T& front()
4844 {
4845 VMA_HEAVY_ASSERT(m_Count > 0);
4846 return m_pArray[0];
4847 }
front()4848 const T& front() const
4849 {
4850 VMA_HEAVY_ASSERT(m_Count > 0);
4851 return m_pArray[0];
4852 }
back()4853 T& back()
4854 {
4855 VMA_HEAVY_ASSERT(m_Count > 0);
4856 return m_pArray[m_Count - 1];
4857 }
back()4858 const T& back() const
4859 {
4860 VMA_HEAVY_ASSERT(m_Count > 0);
4861 return m_pArray[m_Count - 1];
4862 }
4863
4864 void reserve(size_t newCapacity, bool freeMemory = false)
4865 {
4866 newCapacity = VMA_MAX(newCapacity, m_Count);
4867
4868 if((newCapacity < m_Capacity) && !freeMemory)
4869 {
4870 newCapacity = m_Capacity;
4871 }
4872
4873 if(newCapacity != m_Capacity)
4874 {
4875 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4876 if(m_Count != 0)
4877 {
4878 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4879 }
4880 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4881 m_Capacity = newCapacity;
4882 m_pArray = newArray;
4883 }
4884 }
4885
4886 void resize(size_t newCount, bool freeMemory = false)
4887 {
4888 size_t newCapacity = m_Capacity;
4889 if(newCount > m_Capacity)
4890 {
4891 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4892 }
4893 else if(freeMemory)
4894 {
4895 newCapacity = newCount;
4896 }
4897
4898 if(newCapacity != m_Capacity)
4899 {
4900 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4901 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4902 if(elementsToCopy != 0)
4903 {
4904 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4905 }
4906 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4907 m_Capacity = newCapacity;
4908 m_pArray = newArray;
4909 }
4910
4911 m_Count = newCount;
4912 }
4913
4914 void clear(bool freeMemory = false)
4915 {
4916 resize(0, freeMemory);
4917 }
4918
insert(size_t index,const T & src)4919 void insert(size_t index, const T& src)
4920 {
4921 VMA_HEAVY_ASSERT(index <= m_Count);
4922 const size_t oldCount = size();
4923 resize(oldCount + 1);
4924 if(index < oldCount)
4925 {
4926 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4927 }
4928 m_pArray[index] = src;
4929 }
4930
remove(size_t index)4931 void remove(size_t index)
4932 {
4933 VMA_HEAVY_ASSERT(index < m_Count);
4934 const size_t oldCount = size();
4935 if(index < oldCount - 1)
4936 {
4937 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4938 }
4939 resize(oldCount - 1);
4940 }
4941
push_back(const T & src)4942 void push_back(const T& src)
4943 {
4944 const size_t newIndex = size();
4945 resize(newIndex + 1);
4946 m_pArray[newIndex] = src;
4947 }
4948
pop_back()4949 void pop_back()
4950 {
4951 VMA_HEAVY_ASSERT(m_Count > 0);
4952 resize(size() - 1);
4953 }
4954
push_front(const T & src)4955 void push_front(const T& src)
4956 {
4957 insert(0, src);
4958 }
4959
pop_front()4960 void pop_front()
4961 {
4962 VMA_HEAVY_ASSERT(m_Count > 0);
4963 remove(0);
4964 }
4965
4966 typedef T* iterator;
4967
begin()4968 iterator begin() { return m_pArray; }
end()4969 iterator end() { return m_pArray + m_Count; }
4970
4971 private:
4972 AllocatorT m_Allocator;
4973 T* m_pArray;
4974 size_t m_Count;
4975 size_t m_Capacity;
4976 };
4977
4978 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4979 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4980 {
4981 vec.insert(index, item);
4982 }
4983
4984 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4985 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4986 {
4987 vec.remove(index);
4988 }
4989
4990 #endif // #if VMA_USE_STL_VECTOR
4991
4992 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)4993 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4994 {
4995 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4996 vector.data(),
4997 vector.data() + vector.size(),
4998 value,
4999 CmpLess()) - vector.data();
5000 VmaVectorInsert(vector, indexToInsert, value);
5001 return indexToInsert;
5002 }
5003
5004 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)5005 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5006 {
5007 CmpLess comparator;
5008 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5009 vector.begin(),
5010 vector.end(),
5011 value,
5012 comparator);
5013 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5014 {
5015 size_t indexToRemove = it - vector.begin();
5016 VmaVectorRemove(vector, indexToRemove);
5017 return true;
5018 }
5019 return false;
5020 }
5021
5022 ////////////////////////////////////////////////////////////////////////////////
5023 // class VmaSmallVector
5024
5025 /*
5026 This is a vector (a variable-sized array), optimized for the case when the array is small.
5027
5028 It contains some number of elements in-place, which allows it to avoid heap allocation
5029 when the actual number of elements is below that threshold. This allows normal "small"
5030 cases to be fast without losing generality for large inputs.
5031 */
5032
5033 template<typename T, typename AllocatorT, size_t N>
5034 class VmaSmallVector
5035 {
5036 public:
5037 typedef T value_type;
5038
VmaSmallVector(const AllocatorT & allocator)5039 VmaSmallVector(const AllocatorT& allocator) :
5040 m_Count(0),
5041 m_DynamicArray(allocator)
5042 {
5043 }
VmaSmallVector(size_t count,const AllocatorT & allocator)5044 VmaSmallVector(size_t count, const AllocatorT& allocator) :
5045 m_Count(count),
5046 m_DynamicArray(count > N ? count : 0, allocator)
5047 {
5048 }
5049 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5050 VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5051 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5052 VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5053
empty()5054 bool empty() const { return m_Count == 0; }
size()5055 size_t size() const { return m_Count; }
data()5056 T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
data()5057 const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5058
5059 T& operator[](size_t index)
5060 {
5061 VMA_HEAVY_ASSERT(index < m_Count);
5062 return data()[index];
5063 }
5064 const T& operator[](size_t index) const
5065 {
5066 VMA_HEAVY_ASSERT(index < m_Count);
5067 return data()[index];
5068 }
5069
front()5070 T& front()
5071 {
5072 VMA_HEAVY_ASSERT(m_Count > 0);
5073 return data()[0];
5074 }
front()5075 const T& front() const
5076 {
5077 VMA_HEAVY_ASSERT(m_Count > 0);
5078 return data()[0];
5079 }
back()5080 T& back()
5081 {
5082 VMA_HEAVY_ASSERT(m_Count > 0);
5083 return data()[m_Count - 1];
5084 }
back()5085 const T& back() const
5086 {
5087 VMA_HEAVY_ASSERT(m_Count > 0);
5088 return data()[m_Count - 1];
5089 }
5090
5091 void resize(size_t newCount, bool freeMemory = false)
5092 {
5093 if(newCount > N && m_Count > N)
5094 {
5095 // Any direction, staying in m_DynamicArray
5096 m_DynamicArray.resize(newCount, freeMemory);
5097 }
5098 else if(newCount > N && m_Count <= N)
5099 {
5100 // Growing, moving from m_StaticArray to m_DynamicArray
5101 m_DynamicArray.resize(newCount, freeMemory);
5102 if(m_Count > 0)
5103 {
5104 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5105 }
5106 }
5107 else if(newCount <= N && m_Count > N)
5108 {
5109 // Shrinking, moving from m_DynamicArray to m_StaticArray
5110 if(newCount > 0)
5111 {
5112 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5113 }
5114 m_DynamicArray.resize(0, freeMemory);
5115 }
5116 else
5117 {
5118 // Any direction, staying in m_StaticArray - nothing to do here
5119 }
5120 m_Count = newCount;
5121 }
5122
5123 void clear(bool freeMemory = false)
5124 {
5125 m_DynamicArray.clear(freeMemory);
5126 m_Count = 0;
5127 }
5128
insert(size_t index,const T & src)5129 void insert(size_t index, const T& src)
5130 {
5131 VMA_HEAVY_ASSERT(index <= m_Count);
5132 const size_t oldCount = size();
5133 resize(oldCount + 1);
5134 T* const dataPtr = data();
5135 if(index < oldCount)
5136 {
5137 // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5138 memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5139 }
5140 dataPtr[index] = src;
5141 }
5142
remove(size_t index)5143 void remove(size_t index)
5144 {
5145 VMA_HEAVY_ASSERT(index < m_Count);
5146 const size_t oldCount = size();
5147 if(index < oldCount - 1)
5148 {
5149 // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5150 T* const dataPtr = data();
5151 memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5152 }
5153 resize(oldCount - 1);
5154 }
5155
push_back(const T & src)5156 void push_back(const T& src)
5157 {
5158 const size_t newIndex = size();
5159 resize(newIndex + 1);
5160 data()[newIndex] = src;
5161 }
5162
pop_back()5163 void pop_back()
5164 {
5165 VMA_HEAVY_ASSERT(m_Count > 0);
5166 resize(size() - 1);
5167 }
5168
push_front(const T & src)5169 void push_front(const T& src)
5170 {
5171 insert(0, src);
5172 }
5173
pop_front()5174 void pop_front()
5175 {
5176 VMA_HEAVY_ASSERT(m_Count > 0);
5177 remove(0);
5178 }
5179
5180 typedef T* iterator;
5181
begin()5182 iterator begin() { return data(); }
end()5183 iterator end() { return data() + m_Count; }
5184
5185 private:
5186 size_t m_Count;
5187 T m_StaticArray[N]; // Used when m_Size <= N
5188 VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5189 };
5190
5191 ////////////////////////////////////////////////////////////////////////////////
5192 // class VmaPoolAllocator
5193
5194 /*
5195 Allocator for objects of type T using a list of arrays (pools) to speed up
5196 allocation. Number of elements that can be allocated is not bounded because
5197 allocator can create multiple blocks.
5198 */
5199 template<typename T>
5200 class VmaPoolAllocator
5201 {
5202 VMA_CLASS_NO_COPY(VmaPoolAllocator)
5203 public:
5204 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5205 ~VmaPoolAllocator();
5206 template<typename... Types> T* Alloc(Types... args);
5207 void Free(T* ptr);
5208
5209 private:
5210 union Item
5211 {
5212 uint32_t NextFreeIndex;
5213 alignas(T) char Value[sizeof(T)];
5214 };
5215
5216 struct ItemBlock
5217 {
5218 Item* pItems;
5219 uint32_t Capacity;
5220 uint32_t FirstFreeIndex;
5221 };
5222
5223 const VkAllocationCallbacks* m_pAllocationCallbacks;
5224 const uint32_t m_FirstBlockCapacity;
5225 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5226
5227 ItemBlock& CreateNewBlock();
5228 };
5229
5230 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)5231 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5232 m_pAllocationCallbacks(pAllocationCallbacks),
5233 m_FirstBlockCapacity(firstBlockCapacity),
5234 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5235 {
5236 VMA_ASSERT(m_FirstBlockCapacity > 1);
5237 }
5238
5239 template<typename T>
~VmaPoolAllocator()5240 VmaPoolAllocator<T>::~VmaPoolAllocator()
5241 {
5242 for(size_t i = m_ItemBlocks.size(); i--; )
5243 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5244 m_ItemBlocks.clear();
5245 }
5246
5247 template<typename T>
Alloc(Types...args)5248 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5249 {
5250 for(size_t i = m_ItemBlocks.size(); i--; )
5251 {
5252 ItemBlock& block = m_ItemBlocks[i];
5253 // This block has some free items: Use first one.
5254 if(block.FirstFreeIndex != UINT32_MAX)
5255 {
5256 Item* const pItem = &block.pItems[block.FirstFreeIndex];
5257 block.FirstFreeIndex = pItem->NextFreeIndex;
5258 T* result = (T*)&pItem->Value;
5259 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5260 return result;
5261 }
5262 }
5263
5264 // No block has free item: Create new one and use it.
5265 ItemBlock& newBlock = CreateNewBlock();
5266 Item* const pItem = &newBlock.pItems[0];
5267 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5268 T* result = (T*)&pItem->Value;
5269 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5270 return result;
5271 }
5272
5273 template<typename T>
Free(T * ptr)5274 void VmaPoolAllocator<T>::Free(T* ptr)
5275 {
5276 // Search all memory blocks to find ptr.
5277 for(size_t i = m_ItemBlocks.size(); i--; )
5278 {
5279 ItemBlock& block = m_ItemBlocks[i];
5280
5281 // Casting to union.
5282 Item* pItemPtr;
5283 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5284
5285 // Check if pItemPtr is in address range of this block.
5286 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5287 {
5288 ptr->~T(); // Explicit destructor call.
5289 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5290 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5291 block.FirstFreeIndex = index;
5292 return;
5293 }
5294 }
5295 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5296 }
5297
5298 template<typename T>
CreateNewBlock()5299 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5300 {
5301 const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5302 m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5303
5304 const ItemBlock newBlock = {
5305 vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5306 newBlockCapacity,
5307 0 };
5308
5309 m_ItemBlocks.push_back(newBlock);
5310
5311 // Setup singly-linked list of all free items in this block.
5312 for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5313 newBlock.pItems[i].NextFreeIndex = i + 1;
5314 newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5315 return m_ItemBlocks.back();
5316 }
5317
5318 ////////////////////////////////////////////////////////////////////////////////
5319 // class VmaRawList, VmaList
5320
5321 #if VMA_USE_STL_LIST
5322
5323 #define VmaList std::list
5324
5325 #else // #if VMA_USE_STL_LIST
5326
5327 template<typename T>
5328 struct VmaListItem
5329 {
5330 VmaListItem* pPrev;
5331 VmaListItem* pNext;
5332 T Value;
5333 };
5334
5335 // Doubly linked list.
5336 template<typename T>
5337 class VmaRawList
5338 {
5339 VMA_CLASS_NO_COPY(VmaRawList)
5340 public:
5341 typedef VmaListItem<T> ItemType;
5342
5343 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5344 ~VmaRawList();
5345 void Clear();
5346
GetCount()5347 size_t GetCount() const { return m_Count; }
IsEmpty()5348 bool IsEmpty() const { return m_Count == 0; }
5349
Front()5350 ItemType* Front() { return m_pFront; }
Front()5351 const ItemType* Front() const { return m_pFront; }
Back()5352 ItemType* Back() { return m_pBack; }
Back()5353 const ItemType* Back() const { return m_pBack; }
5354
5355 ItemType* PushBack();
5356 ItemType* PushFront();
5357 ItemType* PushBack(const T& value);
5358 ItemType* PushFront(const T& value);
5359 void PopBack();
5360 void PopFront();
5361
5362 // Item can be null - it means PushBack.
5363 ItemType* InsertBefore(ItemType* pItem);
5364 // Item can be null - it means PushFront.
5365 ItemType* InsertAfter(ItemType* pItem);
5366
5367 ItemType* InsertBefore(ItemType* pItem, const T& value);
5368 ItemType* InsertAfter(ItemType* pItem, const T& value);
5369
5370 void Remove(ItemType* pItem);
5371
5372 private:
5373 const VkAllocationCallbacks* const m_pAllocationCallbacks;
5374 VmaPoolAllocator<ItemType> m_ItemAllocator;
5375 ItemType* m_pFront;
5376 ItemType* m_pBack;
5377 size_t m_Count;
5378 };
5379
5380 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)5381 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5382 m_pAllocationCallbacks(pAllocationCallbacks),
5383 m_ItemAllocator(pAllocationCallbacks, 128),
5384 m_pFront(VMA_NULL),
5385 m_pBack(VMA_NULL),
5386 m_Count(0)
5387 {
5388 }
5389
5390 template<typename T>
~VmaRawList()5391 VmaRawList<T>::~VmaRawList()
5392 {
5393 // Intentionally not calling Clear, because that would be unnecessary
5394 // computations to return all items to m_ItemAllocator as free.
5395 }
5396
5397 template<typename T>
Clear()5398 void VmaRawList<T>::Clear()
5399 {
5400 if(IsEmpty() == false)
5401 {
5402 ItemType* pItem = m_pBack;
5403 while(pItem != VMA_NULL)
5404 {
5405 ItemType* const pPrevItem = pItem->pPrev;
5406 m_ItemAllocator.Free(pItem);
5407 pItem = pPrevItem;
5408 }
5409 m_pFront = VMA_NULL;
5410 m_pBack = VMA_NULL;
5411 m_Count = 0;
5412 }
5413 }
5414
5415 template<typename T>
PushBack()5416 VmaListItem<T>* VmaRawList<T>::PushBack()
5417 {
5418 ItemType* const pNewItem = m_ItemAllocator.Alloc();
5419 pNewItem->pNext = VMA_NULL;
5420 if(IsEmpty())
5421 {
5422 pNewItem->pPrev = VMA_NULL;
5423 m_pFront = pNewItem;
5424 m_pBack = pNewItem;
5425 m_Count = 1;
5426 }
5427 else
5428 {
5429 pNewItem->pPrev = m_pBack;
5430 m_pBack->pNext = pNewItem;
5431 m_pBack = pNewItem;
5432 ++m_Count;
5433 }
5434 return pNewItem;
5435 }
5436
5437 template<typename T>
PushFront()5438 VmaListItem<T>* VmaRawList<T>::PushFront()
5439 {
5440 ItemType* const pNewItem = m_ItemAllocator.Alloc();
5441 pNewItem->pPrev = VMA_NULL;
5442 if(IsEmpty())
5443 {
5444 pNewItem->pNext = VMA_NULL;
5445 m_pFront = pNewItem;
5446 m_pBack = pNewItem;
5447 m_Count = 1;
5448 }
5449 else
5450 {
5451 pNewItem->pNext = m_pFront;
5452 m_pFront->pPrev = pNewItem;
5453 m_pFront = pNewItem;
5454 ++m_Count;
5455 }
5456 return pNewItem;
5457 }
5458
5459 template<typename T>
PushBack(const T & value)5460 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5461 {
5462 ItemType* const pNewItem = PushBack();
5463 pNewItem->Value = value;
5464 return pNewItem;
5465 }
5466
5467 template<typename T>
PushFront(const T & value)5468 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5469 {
5470 ItemType* const pNewItem = PushFront();
5471 pNewItem->Value = value;
5472 return pNewItem;
5473 }
5474
5475 template<typename T>
PopBack()5476 void VmaRawList<T>::PopBack()
5477 {
5478 VMA_HEAVY_ASSERT(m_Count > 0);
5479 ItemType* const pBackItem = m_pBack;
5480 ItemType* const pPrevItem = pBackItem->pPrev;
5481 if(pPrevItem != VMA_NULL)
5482 {
5483 pPrevItem->pNext = VMA_NULL;
5484 }
5485 m_pBack = pPrevItem;
5486 m_ItemAllocator.Free(pBackItem);
5487 --m_Count;
5488 }
5489
5490 template<typename T>
PopFront()5491 void VmaRawList<T>::PopFront()
5492 {
5493 VMA_HEAVY_ASSERT(m_Count > 0);
5494 ItemType* const pFrontItem = m_pFront;
5495 ItemType* const pNextItem = pFrontItem->pNext;
5496 if(pNextItem != VMA_NULL)
5497 {
5498 pNextItem->pPrev = VMA_NULL;
5499 }
5500 m_pFront = pNextItem;
5501 m_ItemAllocator.Free(pFrontItem);
5502 --m_Count;
5503 }
5504
5505 template<typename T>
Remove(ItemType * pItem)5506 void VmaRawList<T>::Remove(ItemType* pItem)
5507 {
5508 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5509 VMA_HEAVY_ASSERT(m_Count > 0);
5510
5511 if(pItem->pPrev != VMA_NULL)
5512 {
5513 pItem->pPrev->pNext = pItem->pNext;
5514 }
5515 else
5516 {
5517 VMA_HEAVY_ASSERT(m_pFront == pItem);
5518 m_pFront = pItem->pNext;
5519 }
5520
5521 if(pItem->pNext != VMA_NULL)
5522 {
5523 pItem->pNext->pPrev = pItem->pPrev;
5524 }
5525 else
5526 {
5527 VMA_HEAVY_ASSERT(m_pBack == pItem);
5528 m_pBack = pItem->pPrev;
5529 }
5530
5531 m_ItemAllocator.Free(pItem);
5532 --m_Count;
5533 }
5534
5535 template<typename T>
InsertBefore(ItemType * pItem)5536 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5537 {
5538 if(pItem != VMA_NULL)
5539 {
5540 ItemType* const prevItem = pItem->pPrev;
5541 ItemType* const newItem = m_ItemAllocator.Alloc();
5542 newItem->pPrev = prevItem;
5543 newItem->pNext = pItem;
5544 pItem->pPrev = newItem;
5545 if(prevItem != VMA_NULL)
5546 {
5547 prevItem->pNext = newItem;
5548 }
5549 else
5550 {
5551 VMA_HEAVY_ASSERT(m_pFront == pItem);
5552 m_pFront = newItem;
5553 }
5554 ++m_Count;
5555 return newItem;
5556 }
5557 else
5558 return PushBack();
5559 }
5560
5561 template<typename T>
InsertAfter(ItemType * pItem)5562 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5563 {
5564 if(pItem != VMA_NULL)
5565 {
5566 ItemType* const nextItem = pItem->pNext;
5567 ItemType* const newItem = m_ItemAllocator.Alloc();
5568 newItem->pNext = nextItem;
5569 newItem->pPrev = pItem;
5570 pItem->pNext = newItem;
5571 if(nextItem != VMA_NULL)
5572 {
5573 nextItem->pPrev = newItem;
5574 }
5575 else
5576 {
5577 VMA_HEAVY_ASSERT(m_pBack == pItem);
5578 m_pBack = newItem;
5579 }
5580 ++m_Count;
5581 return newItem;
5582 }
5583 else
5584 return PushFront();
5585 }
5586
5587 template<typename T>
InsertBefore(ItemType * pItem,const T & value)5588 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5589 {
5590 ItemType* const newItem = InsertBefore(pItem);
5591 newItem->Value = value;
5592 return newItem;
5593 }
5594
5595 template<typename T>
InsertAfter(ItemType * pItem,const T & value)5596 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5597 {
5598 ItemType* const newItem = InsertAfter(pItem);
5599 newItem->Value = value;
5600 return newItem;
5601 }
5602
5603 template<typename T, typename AllocatorT>
5604 class VmaList
5605 {
VMA_CLASS_NO_COPY(VmaList)5606 VMA_CLASS_NO_COPY(VmaList)
5607 public:
5608 class iterator
5609 {
5610 public:
5611 iterator() :
5612 m_pList(VMA_NULL),
5613 m_pItem(VMA_NULL)
5614 {
5615 }
5616
5617 T& operator*() const
5618 {
5619 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5620 return m_pItem->Value;
5621 }
5622 T* operator->() const
5623 {
5624 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5625 return &m_pItem->Value;
5626 }
5627
5628 iterator& operator++()
5629 {
5630 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5631 m_pItem = m_pItem->pNext;
5632 return *this;
5633 }
5634 iterator& operator--()
5635 {
5636 if(m_pItem != VMA_NULL)
5637 {
5638 m_pItem = m_pItem->pPrev;
5639 }
5640 else
5641 {
5642 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5643 m_pItem = m_pList->Back();
5644 }
5645 return *this;
5646 }
5647
5648 iterator operator++(int)
5649 {
5650 iterator result = *this;
5651 ++*this;
5652 return result;
5653 }
5654 iterator operator--(int)
5655 {
5656 iterator result = *this;
5657 --*this;
5658 return result;
5659 }
5660
5661 bool operator==(const iterator& rhs) const
5662 {
5663 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5664 return m_pItem == rhs.m_pItem;
5665 }
5666 bool operator!=(const iterator& rhs) const
5667 {
5668 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5669 return m_pItem != rhs.m_pItem;
5670 }
5671
5672 private:
5673 VmaRawList<T>* m_pList;
5674 VmaListItem<T>* m_pItem;
5675
5676 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5677 m_pList(pList),
5678 m_pItem(pItem)
5679 {
5680 }
5681
5682 friend class VmaList<T, AllocatorT>;
5683 };
5684
5685 class const_iterator
5686 {
5687 public:
const_iterator()5688 const_iterator() :
5689 m_pList(VMA_NULL),
5690 m_pItem(VMA_NULL)
5691 {
5692 }
5693
const_iterator(const iterator & src)5694 const_iterator(const iterator& src) :
5695 m_pList(src.m_pList),
5696 m_pItem(src.m_pItem)
5697 {
5698 }
5699
5700 const T& operator*() const
5701 {
5702 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5703 return m_pItem->Value;
5704 }
5705 const T* operator->() const
5706 {
5707 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5708 return &m_pItem->Value;
5709 }
5710
5711 const_iterator& operator++()
5712 {
5713 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5714 m_pItem = m_pItem->pNext;
5715 return *this;
5716 }
5717 const_iterator& operator--()
5718 {
5719 if(m_pItem != VMA_NULL)
5720 {
5721 m_pItem = m_pItem->pPrev;
5722 }
5723 else
5724 {
5725 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5726 m_pItem = m_pList->Back();
5727 }
5728 return *this;
5729 }
5730
5731 const_iterator operator++(int)
5732 {
5733 const_iterator result = *this;
5734 ++*this;
5735 return result;
5736 }
5737 const_iterator operator--(int)
5738 {
5739 const_iterator result = *this;
5740 --*this;
5741 return result;
5742 }
5743
5744 bool operator==(const const_iterator& rhs) const
5745 {
5746 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5747 return m_pItem == rhs.m_pItem;
5748 }
5749 bool operator!=(const const_iterator& rhs) const
5750 {
5751 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5752 return m_pItem != rhs.m_pItem;
5753 }
5754
5755 private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)5756 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5757 m_pList(pList),
5758 m_pItem(pItem)
5759 {
5760 }
5761
5762 const VmaRawList<T>* m_pList;
5763 const VmaListItem<T>* m_pItem;
5764
5765 friend class VmaList<T, AllocatorT>;
5766 };
5767
VmaList(const AllocatorT & allocator)5768 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5769
empty()5770 bool empty() const { return m_RawList.IsEmpty(); }
size()5771 size_t size() const { return m_RawList.GetCount(); }
5772
begin()5773 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()5774 iterator end() { return iterator(&m_RawList, VMA_NULL); }
5775
cbegin()5776 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()5777 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5778
clear()5779 void clear() { m_RawList.Clear(); }
push_back(const T & value)5780 void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)5781 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)5782 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5783
5784 private:
5785 VmaRawList<T> m_RawList;
5786 };
5787
5788 #endif // #if VMA_USE_STL_LIST
5789
5790 ////////////////////////////////////////////////////////////////////////////////
5791 // class VmaMap
5792
5793 // Unused in this version.
5794 #if 0
5795
5796 #if VMA_USE_STL_UNORDERED_MAP
5797
5798 #define VmaPair std::pair
5799
5800 #define VMA_MAP_TYPE(KeyT, ValueT) \
5801 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5802
5803 #else // #if VMA_USE_STL_UNORDERED_MAP
5804
5805 template<typename T1, typename T2>
5806 struct VmaPair
5807 {
5808 T1 first;
5809 T2 second;
5810
5811 VmaPair() : first(), second() { }
5812 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5813 };
5814
5815 /* Class compatible with subset of interface of std::unordered_map.
5816 KeyT, ValueT must be POD because they will be stored in VmaVector.
5817 */
5818 template<typename KeyT, typename ValueT>
5819 class VmaMap
5820 {
5821 public:
5822 typedef VmaPair<KeyT, ValueT> PairType;
5823 typedef PairType* iterator;
5824
5825 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5826
5827 iterator begin() { return m_Vector.begin(); }
5828 iterator end() { return m_Vector.end(); }
5829
5830 void insert(const PairType& pair);
5831 iterator find(const KeyT& key);
5832 void erase(iterator it);
5833
5834 private:
5835 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5836 };
5837
5838 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5839
5840 template<typename FirstT, typename SecondT>
5841 struct VmaPairFirstLess
5842 {
5843 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5844 {
5845 return lhs.first < rhs.first;
5846 }
5847 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5848 {
5849 return lhs.first < rhsFirst;
5850 }
5851 };
5852
5853 template<typename KeyT, typename ValueT>
5854 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5855 {
5856 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5857 m_Vector.data(),
5858 m_Vector.data() + m_Vector.size(),
5859 pair,
5860 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5861 VmaVectorInsert(m_Vector, indexToInsert, pair);
5862 }
5863
5864 template<typename KeyT, typename ValueT>
5865 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5866 {
5867 PairType* it = VmaBinaryFindFirstNotLess(
5868 m_Vector.data(),
5869 m_Vector.data() + m_Vector.size(),
5870 key,
5871 VmaPairFirstLess<KeyT, ValueT>());
5872 if((it != m_Vector.end()) && (it->first == key))
5873 {
5874 return it;
5875 }
5876 else
5877 {
5878 return m_Vector.end();
5879 }
5880 }
5881
5882 template<typename KeyT, typename ValueT>
5883 void VmaMap<KeyT, ValueT>::erase(iterator it)
5884 {
5885 VmaVectorRemove(m_Vector, it - m_Vector.begin());
5886 }
5887
5888 #endif // #if VMA_USE_STL_UNORDERED_MAP
5889
5890 #endif // #if 0
5891
5892 ////////////////////////////////////////////////////////////////////////////////
5893
5894 class VmaDeviceMemoryBlock;
5895
5896 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
5897
5898 struct VmaAllocation_T
5899 {
5900 private:
5901 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
5902
5903 enum FLAGS
5904 {
5905 FLAG_USER_DATA_STRING = 0x01,
5906 };
5907
5908 public:
5909 enum ALLOCATION_TYPE
5910 {
5911 ALLOCATION_TYPE_NONE,
5912 ALLOCATION_TYPE_BLOCK,
5913 ALLOCATION_TYPE_DEDICATED,
5914 };
5915
5916 /*
5917 This struct is allocated using VmaPoolAllocator.
5918 */
5919
VmaAllocation_TVmaAllocation_T5920 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
5921 m_Alignment{1},
5922 m_Size{0},
5923 m_pUserData{VMA_NULL},
5924 m_LastUseFrameIndex{currentFrameIndex},
5925 m_MemoryTypeIndex{0},
5926 m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
5927 m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
5928 m_MapCount{0},
5929 m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0}
5930 {
5931 #if VMA_STATS_STRING_ENABLED
5932 m_CreationFrameIndex = currentFrameIndex;
5933 m_BufferImageUsage = 0;
5934 #endif
5935 }
5936
~VmaAllocation_TVmaAllocation_T5937 ~VmaAllocation_T()
5938 {
5939 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
5940
5941 // Check if owned string was freed.
5942 VMA_ASSERT(m_pUserData == VMA_NULL);
5943 }
5944
InitBlockAllocationVmaAllocation_T5945 void InitBlockAllocation(
5946 VmaDeviceMemoryBlock* block,
5947 VkDeviceSize offset,
5948 VkDeviceSize alignment,
5949 VkDeviceSize size,
5950 uint32_t memoryTypeIndex,
5951 VmaSuballocationType suballocationType,
5952 bool mapped,
5953 bool canBecomeLost)
5954 {
5955 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5956 VMA_ASSERT(block != VMA_NULL);
5957 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5958 m_Alignment = alignment;
5959 m_Size = size;
5960 m_MemoryTypeIndex = memoryTypeIndex;
5961 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5962 m_SuballocationType = (uint8_t)suballocationType;
5963 m_BlockAllocation.m_Block = block;
5964 m_BlockAllocation.m_Offset = offset;
5965 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
5966 }
5967
InitLostVmaAllocation_T5968 void InitLost()
5969 {
5970 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5971 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
5972 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
5973 m_MemoryTypeIndex = 0;
5974 m_BlockAllocation.m_Block = VMA_NULL;
5975 m_BlockAllocation.m_Offset = 0;
5976 m_BlockAllocation.m_CanBecomeLost = true;
5977 }
5978
5979 void ChangeBlockAllocation(
5980 VmaAllocator hAllocator,
5981 VmaDeviceMemoryBlock* block,
5982 VkDeviceSize offset);
5983
5984 void ChangeOffset(VkDeviceSize newOffset);
5985
5986 // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T5987 void InitDedicatedAllocation(
5988 uint32_t memoryTypeIndex,
5989 VkDeviceMemory hMemory,
5990 VmaSuballocationType suballocationType,
5991 void* pMappedData,
5992 VkDeviceSize size)
5993 {
5994 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5995 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
5996 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
5997 m_Alignment = 0;
5998 m_Size = size;
5999 m_MemoryTypeIndex = memoryTypeIndex;
6000 m_SuballocationType = (uint8_t)suballocationType;
6001 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6002 m_DedicatedAllocation.m_hMemory = hMemory;
6003 m_DedicatedAllocation.m_pMappedData = pMappedData;
6004 }
6005
GetTypeVmaAllocation_T6006 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T6007 VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T6008 VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T6009 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T6010 void* GetUserData() const { return m_pUserData; }
6011 void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T6012 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6013
GetBlockVmaAllocation_T6014 VmaDeviceMemoryBlock* GetBlock() const
6015 {
6016 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6017 return m_BlockAllocation.m_Block;
6018 }
6019 VkDeviceSize GetOffset() const;
6020 VkDeviceMemory GetMemory() const;
GetMemoryTypeIndexVmaAllocation_T6021 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T6022 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6023 void* GetMappedData() const;
6024 bool CanBecomeLost() const;
6025
GetLastUseFrameIndexVmaAllocation_T6026 uint32_t GetLastUseFrameIndex() const
6027 {
6028 return m_LastUseFrameIndex.load();
6029 }
CompareExchangeLastUseFrameIndexVmaAllocation_T6030 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6031 {
6032 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6033 }
6034 /*
6035 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6036 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6037 - Else, returns false.
6038
6039 If hAllocation is already lost, assert - you should not call it then.
6040 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6041 */
6042 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6043
DedicatedAllocCalcStatsInfoVmaAllocation_T6044 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6045 {
6046 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6047 outInfo.blockCount = 1;
6048 outInfo.allocationCount = 1;
6049 outInfo.unusedRangeCount = 0;
6050 outInfo.usedBytes = m_Size;
6051 outInfo.unusedBytes = 0;
6052 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6053 outInfo.unusedRangeSizeMin = UINT64_MAX;
6054 outInfo.unusedRangeSizeMax = 0;
6055 }
6056
6057 void BlockAllocMap();
6058 void BlockAllocUnmap();
6059 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6060 void DedicatedAllocUnmap(VmaAllocator hAllocator);
6061
6062 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T6063 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T6064 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6065
InitBufferImageUsageVmaAllocation_T6066 void InitBufferImageUsage(uint32_t bufferImageUsage)
6067 {
6068 VMA_ASSERT(m_BufferImageUsage == 0);
6069 m_BufferImageUsage = bufferImageUsage;
6070 }
6071
6072 void PrintParameters(class VmaJsonWriter& json) const;
6073 #endif
6074
6075 private:
6076 VkDeviceSize m_Alignment;
6077 VkDeviceSize m_Size;
6078 void* m_pUserData;
6079 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6080 uint32_t m_MemoryTypeIndex;
6081 uint8_t m_Type; // ALLOCATION_TYPE
6082 uint8_t m_SuballocationType; // VmaSuballocationType
6083 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6084 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6085 uint8_t m_MapCount;
6086 uint8_t m_Flags; // enum FLAGS
6087
6088 // Allocation out of VmaDeviceMemoryBlock.
6089 struct BlockAllocation
6090 {
6091 VmaDeviceMemoryBlock* m_Block;
6092 VkDeviceSize m_Offset;
6093 bool m_CanBecomeLost;
6094 };
6095
6096 // Allocation for an object that has its own private VkDeviceMemory.
6097 struct DedicatedAllocation
6098 {
6099 VkDeviceMemory m_hMemory;
6100 void* m_pMappedData; // Not null means memory is mapped.
6101 };
6102
6103 union
6104 {
6105 // Allocation out of VmaDeviceMemoryBlock.
6106 BlockAllocation m_BlockAllocation;
6107 // Allocation for an object that has its own private VkDeviceMemory.
6108 DedicatedAllocation m_DedicatedAllocation;
6109 };
6110
6111 #if VMA_STATS_STRING_ENABLED
6112 uint32_t m_CreationFrameIndex;
6113 uint32_t m_BufferImageUsage; // 0 if unknown.
6114 #endif
6115
6116 void FreeUserDataString(VmaAllocator hAllocator);
6117 };
6118
6119 /*
6120 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6121 allocated memory block or free.
6122 */
6123 struct VmaSuballocation
6124 {
6125 VkDeviceSize offset;
6126 VkDeviceSize size;
6127 VmaAllocation hAllocation;
6128 VmaSuballocationType type;
6129 };
6130
6131 // Comparator for offsets.
6132 struct VmaSuballocationOffsetLess
6133 {
operatorVmaSuballocationOffsetLess6134 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6135 {
6136 return lhs.offset < rhs.offset;
6137 }
6138 };
6139 struct VmaSuballocationOffsetGreater
6140 {
operatorVmaSuballocationOffsetGreater6141 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6142 {
6143 return lhs.offset > rhs.offset;
6144 }
6145 };
6146
6147 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6148
6149 // Cost of one additional allocation lost, as equivalent in bytes.
6150 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6151
6152 enum class VmaAllocationRequestType
6153 {
6154 Normal,
6155 // Used by "Linear" algorithm.
6156 UpperAddress,
6157 EndOf1st,
6158 EndOf2nd,
6159 };
6160
6161 /*
6162 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6163
6164 If canMakeOtherLost was false:
6165 - item points to a FREE suballocation.
6166 - itemsToMakeLostCount is 0.
6167
6168 If canMakeOtherLost was true:
6169 - item points to first of sequence of suballocations, which are either FREE,
6170 or point to VmaAllocations that can become lost.
6171 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6172 the requested allocation to succeed.
6173 */
6174 struct VmaAllocationRequest
6175 {
6176 VkDeviceSize offset;
6177 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6178 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6179 VmaSuballocationList::iterator item;
6180 size_t itemsToMakeLostCount;
6181 void* customData;
6182 VmaAllocationRequestType type;
6183
CalcCostVmaAllocationRequest6184 VkDeviceSize CalcCost() const
6185 {
6186 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6187 }
6188 };
6189
6190 /*
6191 Data structure used for bookkeeping of allocations and unused ranges of memory
6192 in a single VkDeviceMemory block.
6193 */
6194 class VmaBlockMetadata
6195 {
6196 public:
6197 VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()6198 virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)6199 virtual void Init(VkDeviceSize size) { m_Size = size; }
6200
6201 // Validates all data structures inside this object. If not valid, returns false.
6202 virtual bool Validate() const = 0;
GetSize()6203 VkDeviceSize GetSize() const { return m_Size; }
6204 virtual size_t GetAllocationCount() const = 0;
6205 virtual VkDeviceSize GetSumFreeSize() const = 0;
6206 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6207 // Returns true if this block is empty - contains only single free suballocation.
6208 virtual bool IsEmpty() const = 0;
6209
6210 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6211 // Shouldn't modify blockCount.
6212 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6213
6214 #if VMA_STATS_STRING_ENABLED
6215 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6216 #endif
6217
6218 // Tries to find a place for suballocation with given parameters inside this block.
6219 // If succeeded, fills pAllocationRequest and returns true.
6220 // If failed, returns false.
6221 virtual bool CreateAllocationRequest(
6222 uint32_t currentFrameIndex,
6223 uint32_t frameInUseCount,
6224 VkDeviceSize bufferImageGranularity,
6225 VkDeviceSize allocSize,
6226 VkDeviceSize allocAlignment,
6227 bool upperAddress,
6228 VmaSuballocationType allocType,
6229 bool canMakeOtherLost,
6230 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6231 uint32_t strategy,
6232 VmaAllocationRequest* pAllocationRequest) = 0;
6233
6234 virtual bool MakeRequestedAllocationsLost(
6235 uint32_t currentFrameIndex,
6236 uint32_t frameInUseCount,
6237 VmaAllocationRequest* pAllocationRequest) = 0;
6238
6239 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6240
6241 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6242
6243 // Makes actual allocation based on request. Request must already be checked and valid.
6244 virtual void Alloc(
6245 const VmaAllocationRequest& request,
6246 VmaSuballocationType type,
6247 VkDeviceSize allocSize,
6248 VmaAllocation hAllocation) = 0;
6249
6250 // Frees suballocation assigned to given memory region.
6251 virtual void Free(const VmaAllocation allocation) = 0;
6252 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6253
6254 protected:
GetAllocationCallbacks()6255 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6256
6257 #if VMA_STATS_STRING_ENABLED
6258 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6259 VkDeviceSize unusedBytes,
6260 size_t allocationCount,
6261 size_t unusedRangeCount) const;
6262 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6263 VkDeviceSize offset,
6264 VmaAllocation hAllocation) const;
6265 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6266 VkDeviceSize offset,
6267 VkDeviceSize size) const;
6268 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6269 #endif
6270
6271 private:
6272 VkDeviceSize m_Size;
6273 const VkAllocationCallbacks* m_pAllocationCallbacks;
6274 };
6275
6276 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6277 VMA_ASSERT(0 && "Validation failed: " #cond); \
6278 return false; \
6279 } } while(false)
6280
6281 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6282 {
6283 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6284 public:
6285 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6286 virtual ~VmaBlockMetadata_Generic();
6287 virtual void Init(VkDeviceSize size);
6288
6289 virtual bool Validate() const;
GetAllocationCount()6290 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()6291 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6292 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6293 virtual bool IsEmpty() const;
6294
6295 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6296 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6297
6298 #if VMA_STATS_STRING_ENABLED
6299 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6300 #endif
6301
6302 virtual bool CreateAllocationRequest(
6303 uint32_t currentFrameIndex,
6304 uint32_t frameInUseCount,
6305 VkDeviceSize bufferImageGranularity,
6306 VkDeviceSize allocSize,
6307 VkDeviceSize allocAlignment,
6308 bool upperAddress,
6309 VmaSuballocationType allocType,
6310 bool canMakeOtherLost,
6311 uint32_t strategy,
6312 VmaAllocationRequest* pAllocationRequest);
6313
6314 virtual bool MakeRequestedAllocationsLost(
6315 uint32_t currentFrameIndex,
6316 uint32_t frameInUseCount,
6317 VmaAllocationRequest* pAllocationRequest);
6318
6319 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6320
6321 virtual VkResult CheckCorruption(const void* pBlockData);
6322
6323 virtual void Alloc(
6324 const VmaAllocationRequest& request,
6325 VmaSuballocationType type,
6326 VkDeviceSize allocSize,
6327 VmaAllocation hAllocation);
6328
6329 virtual void Free(const VmaAllocation allocation);
6330 virtual void FreeAtOffset(VkDeviceSize offset);
6331
6332 ////////////////////////////////////////////////////////////////////////////////
6333 // For defragmentation
6334
6335 bool IsBufferImageGranularityConflictPossible(
6336 VkDeviceSize bufferImageGranularity,
6337 VmaSuballocationType& inOutPrevSuballocType) const;
6338
6339 private:
6340 friend class VmaDefragmentationAlgorithm_Generic;
6341 friend class VmaDefragmentationAlgorithm_Fast;
6342
6343 uint32_t m_FreeCount;
6344 VkDeviceSize m_SumFreeSize;
6345 VmaSuballocationList m_Suballocations;
6346 // Suballocations that are free and have size greater than certain threshold.
6347 // Sorted by size, ascending.
6348 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6349
6350 bool ValidateFreeSuballocationList() const;
6351
6352 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6353 // If yes, fills pOffset and returns true. If no, returns false.
6354 bool CheckAllocation(
6355 uint32_t currentFrameIndex,
6356 uint32_t frameInUseCount,
6357 VkDeviceSize bufferImageGranularity,
6358 VkDeviceSize allocSize,
6359 VkDeviceSize allocAlignment,
6360 VmaSuballocationType allocType,
6361 VmaSuballocationList::const_iterator suballocItem,
6362 bool canMakeOtherLost,
6363 VkDeviceSize* pOffset,
6364 size_t* itemsToMakeLostCount,
6365 VkDeviceSize* pSumFreeSize,
6366 VkDeviceSize* pSumItemSize) const;
6367 // Given free suballocation, it merges it with following one, which must also be free.
6368 void MergeFreeWithNext(VmaSuballocationList::iterator item);
6369 // Releases given suballocation, making it free.
6370 // Merges it with adjacent free suballocations if applicable.
6371 // Returns iterator to new free suballocation at this place.
6372 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6373 // Given free suballocation, it inserts it into sorted list of
6374 // m_FreeSuballocationsBySize if it's suitable.
6375 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6376 // Given free suballocation, it removes it from sorted list of
6377 // m_FreeSuballocationsBySize if it's suitable.
6378 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6379 };
6380
6381 /*
6382 Allocations and their references in internal data structure look like this:
6383
6384 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6385
6386 0 +-------+
6387 | |
6388 | |
6389 | |
6390 +-------+
6391 | Alloc | 1st[m_1stNullItemsBeginCount]
6392 +-------+
6393 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6394 +-------+
6395 | ... |
6396 +-------+
6397 | Alloc | 1st[1st.size() - 1]
6398 +-------+
6399 | |
6400 | |
6401 | |
6402 GetSize() +-------+
6403
6404 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6405
6406 0 +-------+
6407 | Alloc | 2nd[0]
6408 +-------+
6409 | Alloc | 2nd[1]
6410 +-------+
6411 | ... |
6412 +-------+
6413 | Alloc | 2nd[2nd.size() - 1]
6414 +-------+
6415 | |
6416 | |
6417 | |
6418 +-------+
6419 | Alloc | 1st[m_1stNullItemsBeginCount]
6420 +-------+
6421 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6422 +-------+
6423 | ... |
6424 +-------+
6425 | Alloc | 1st[1st.size() - 1]
6426 +-------+
6427 | |
6428 GetSize() +-------+
6429
6430 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6431
6432 0 +-------+
6433 | |
6434 | |
6435 | |
6436 +-------+
6437 | Alloc | 1st[m_1stNullItemsBeginCount]
6438 +-------+
6439 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6440 +-------+
6441 | ... |
6442 +-------+
6443 | Alloc | 1st[1st.size() - 1]
6444 +-------+
6445 | |
6446 | |
6447 | |
6448 +-------+
6449 | Alloc | 2nd[2nd.size() - 1]
6450 +-------+
6451 | ... |
6452 +-------+
6453 | Alloc | 2nd[1]
6454 +-------+
6455 | Alloc | 2nd[0]
6456 GetSize() +-------+
6457
6458 */
6459 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6460 {
6461 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6462 public:
6463 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6464 virtual ~VmaBlockMetadata_Linear();
6465 virtual void Init(VkDeviceSize size);
6466
6467 virtual bool Validate() const;
6468 virtual size_t GetAllocationCount() const;
GetSumFreeSize()6469 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6470 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6471 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6472
6473 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6474 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6475
6476 #if VMA_STATS_STRING_ENABLED
6477 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6478 #endif
6479
6480 virtual bool CreateAllocationRequest(
6481 uint32_t currentFrameIndex,
6482 uint32_t frameInUseCount,
6483 VkDeviceSize bufferImageGranularity,
6484 VkDeviceSize allocSize,
6485 VkDeviceSize allocAlignment,
6486 bool upperAddress,
6487 VmaSuballocationType allocType,
6488 bool canMakeOtherLost,
6489 uint32_t strategy,
6490 VmaAllocationRequest* pAllocationRequest);
6491
6492 virtual bool MakeRequestedAllocationsLost(
6493 uint32_t currentFrameIndex,
6494 uint32_t frameInUseCount,
6495 VmaAllocationRequest* pAllocationRequest);
6496
6497 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6498
6499 virtual VkResult CheckCorruption(const void* pBlockData);
6500
6501 virtual void Alloc(
6502 const VmaAllocationRequest& request,
6503 VmaSuballocationType type,
6504 VkDeviceSize allocSize,
6505 VmaAllocation hAllocation);
6506
6507 virtual void Free(const VmaAllocation allocation);
6508 virtual void FreeAtOffset(VkDeviceSize offset);
6509
6510 private:
6511 /*
6512 There are two suballocation vectors, used in ping-pong way.
6513 The one with index m_1stVectorIndex is called 1st.
6514 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6515 2nd can be non-empty only when 1st is not empty.
6516 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6517 */
6518 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6519
6520 enum SECOND_VECTOR_MODE
6521 {
6522 SECOND_VECTOR_EMPTY,
6523 /*
6524 Suballocations in 2nd vector are created later than the ones in 1st, but they
6525 all have smaller offset.
6526 */
6527 SECOND_VECTOR_RING_BUFFER,
6528 /*
6529 Suballocations in 2nd vector are upper side of double stack.
6530 They all have offsets higher than those in 1st vector.
6531 Top of this stack means smaller offsets, but higher indices in this vector.
6532 */
6533 SECOND_VECTOR_DOUBLE_STACK,
6534 };
6535
6536 VkDeviceSize m_SumFreeSize;
6537 SuballocationVectorType m_Suballocations0, m_Suballocations1;
6538 uint32_t m_1stVectorIndex;
6539 SECOND_VECTOR_MODE m_2ndVectorMode;
6540
AccessSuballocations1st()6541 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6542 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()6543 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6544 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6545
6546 // Number of items in 1st vector with hAllocation = null at the beginning.
6547 size_t m_1stNullItemsBeginCount;
6548 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6549 size_t m_1stNullItemsMiddleCount;
6550 // Number of items in 2nd vector with hAllocation = null.
6551 size_t m_2ndNullItemsCount;
6552
6553 bool ShouldCompact1st() const;
6554 void CleanupAfterFree();
6555
6556 bool CreateAllocationRequest_LowerAddress(
6557 uint32_t currentFrameIndex,
6558 uint32_t frameInUseCount,
6559 VkDeviceSize bufferImageGranularity,
6560 VkDeviceSize allocSize,
6561 VkDeviceSize allocAlignment,
6562 VmaSuballocationType allocType,
6563 bool canMakeOtherLost,
6564 uint32_t strategy,
6565 VmaAllocationRequest* pAllocationRequest);
6566 bool CreateAllocationRequest_UpperAddress(
6567 uint32_t currentFrameIndex,
6568 uint32_t frameInUseCount,
6569 VkDeviceSize bufferImageGranularity,
6570 VkDeviceSize allocSize,
6571 VkDeviceSize allocAlignment,
6572 VmaSuballocationType allocType,
6573 bool canMakeOtherLost,
6574 uint32_t strategy,
6575 VmaAllocationRequest* pAllocationRequest);
6576 };
6577
6578 /*
6579 - GetSize() is the original size of allocated memory block.
6580 - m_UsableSize is this size aligned down to a power of two.
6581 All allocations and calculations happen relative to m_UsableSize.
6582 - GetUnusableSize() is the difference between them.
6583 It is repoted as separate, unused range, not available for allocations.
6584
6585 Node at level 0 has size = m_UsableSize.
6586 Each next level contains nodes with size 2 times smaller than current level.
6587 m_LevelCount is the maximum number of levels to use in the current object.
6588 */
6589 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6590 {
6591 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6592 public:
6593 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6594 virtual ~VmaBlockMetadata_Buddy();
6595 virtual void Init(VkDeviceSize size);
6596
6597 virtual bool Validate() const;
GetAllocationCount()6598 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()6599 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6600 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6601 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6602
6603 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6604 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6605
6606 #if VMA_STATS_STRING_ENABLED
6607 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6608 #endif
6609
6610 virtual bool CreateAllocationRequest(
6611 uint32_t currentFrameIndex,
6612 uint32_t frameInUseCount,
6613 VkDeviceSize bufferImageGranularity,
6614 VkDeviceSize allocSize,
6615 VkDeviceSize allocAlignment,
6616 bool upperAddress,
6617 VmaSuballocationType allocType,
6618 bool canMakeOtherLost,
6619 uint32_t strategy,
6620 VmaAllocationRequest* pAllocationRequest);
6621
6622 virtual bool MakeRequestedAllocationsLost(
6623 uint32_t currentFrameIndex,
6624 uint32_t frameInUseCount,
6625 VmaAllocationRequest* pAllocationRequest);
6626
6627 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6628
CheckCorruption(const void * pBlockData)6629 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6630
6631 virtual void Alloc(
6632 const VmaAllocationRequest& request,
6633 VmaSuballocationType type,
6634 VkDeviceSize allocSize,
6635 VmaAllocation hAllocation);
6636
Free(const VmaAllocation allocation)6637 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)6638 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6639
6640 private:
6641 static const VkDeviceSize MIN_NODE_SIZE = 32;
6642 static const size_t MAX_LEVELS = 30;
6643
6644 struct ValidationContext
6645 {
6646 size_t calculatedAllocationCount;
6647 size_t calculatedFreeCount;
6648 VkDeviceSize calculatedSumFreeSize;
6649
ValidationContextValidationContext6650 ValidationContext() :
6651 calculatedAllocationCount(0),
6652 calculatedFreeCount(0),
6653 calculatedSumFreeSize(0) { }
6654 };
6655
6656 struct Node
6657 {
6658 VkDeviceSize offset;
6659 enum TYPE
6660 {
6661 TYPE_FREE,
6662 TYPE_ALLOCATION,
6663 TYPE_SPLIT,
6664 TYPE_COUNT
6665 } type;
6666 Node* parent;
6667 Node* buddy;
6668
6669 union
6670 {
6671 struct
6672 {
6673 Node* prev;
6674 Node* next;
6675 } free;
6676 struct
6677 {
6678 VmaAllocation alloc;
6679 } allocation;
6680 struct
6681 {
6682 Node* leftChild;
6683 } split;
6684 };
6685 };
6686
6687 // Size of the memory block aligned down to a power of two.
6688 VkDeviceSize m_UsableSize;
6689 uint32_t m_LevelCount;
6690
6691 Node* m_Root;
6692 struct {
6693 Node* front;
6694 Node* back;
6695 } m_FreeList[MAX_LEVELS];
6696 // Number of nodes in the tree with type == TYPE_ALLOCATION.
6697 size_t m_AllocationCount;
6698 // Number of nodes in the tree with type == TYPE_FREE.
6699 size_t m_FreeCount;
6700 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6701 VkDeviceSize m_SumFreeSize;
6702
GetUnusableSize()6703 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6704 void DeleteNode(Node* node);
6705 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6706 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)6707 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6708 // Alloc passed just for validation. Can be null.
6709 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6710 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6711 // Adds node to the front of FreeList at given level.
6712 // node->type must be FREE.
6713 // node->free.prev, next can be undefined.
6714 void AddToFreeListFront(uint32_t level, Node* node);
6715 // Removes node from FreeList at given level.
6716 // node->type must be FREE.
6717 // node->free.prev, next stay untouched.
6718 void RemoveFromFreeList(uint32_t level, Node* node);
6719
6720 #if VMA_STATS_STRING_ENABLED
6721 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6722 #endif
6723 };
6724
6725 /*
6726 Represents a single block of device memory (`VkDeviceMemory`) with all the
6727 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6728
6729 Thread-safety: This class must be externally synchronized.
6730 */
6731 class VmaDeviceMemoryBlock
6732 {
6733 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6734 public:
6735 VmaBlockMetadata* m_pMetadata;
6736
6737 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6738
~VmaDeviceMemoryBlock()6739 ~VmaDeviceMemoryBlock()
6740 {
6741 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6742 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6743 }
6744
6745 // Always call after construction.
6746 void Init(
6747 VmaAllocator hAllocator,
6748 VmaPool hParentPool,
6749 uint32_t newMemoryTypeIndex,
6750 VkDeviceMemory newMemory,
6751 VkDeviceSize newSize,
6752 uint32_t id,
6753 uint32_t algorithm);
6754 // Always call before destruction.
6755 void Destroy(VmaAllocator allocator);
6756
GetParentPool()6757 VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()6758 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()6759 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()6760 uint32_t GetId() const { return m_Id; }
GetMappedData()6761 void* GetMappedData() const { return m_pMappedData; }
6762
6763 // Validates all data structures inside this object. If not valid, returns false.
6764 bool Validate() const;
6765
6766 VkResult CheckCorruption(VmaAllocator hAllocator);
6767
6768 // ppData can be null.
6769 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6770 void Unmap(VmaAllocator hAllocator, uint32_t count);
6771
6772 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6773 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6774
6775 VkResult BindBufferMemory(
6776 const VmaAllocator hAllocator,
6777 const VmaAllocation hAllocation,
6778 VkDeviceSize allocationLocalOffset,
6779 VkBuffer hBuffer,
6780 const void* pNext);
6781 VkResult BindImageMemory(
6782 const VmaAllocator hAllocator,
6783 const VmaAllocation hAllocation,
6784 VkDeviceSize allocationLocalOffset,
6785 VkImage hImage,
6786 const void* pNext);
6787
6788 private:
6789 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6790 uint32_t m_MemoryTypeIndex;
6791 uint32_t m_Id;
6792 VkDeviceMemory m_hMemory;
6793
6794 /*
6795 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6796 Also protects m_MapCount, m_pMappedData.
6797 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6798 */
6799 VMA_MUTEX m_Mutex;
6800 uint32_t m_MapCount;
6801 void* m_pMappedData;
6802 };
6803
6804 struct VmaPointerLess
6805 {
operatorVmaPointerLess6806 bool operator()(const void* lhs, const void* rhs) const
6807 {
6808 return lhs < rhs;
6809 }
6810 };
6811
6812 struct VmaDefragmentationMove
6813 {
6814 size_t srcBlockIndex;
6815 size_t dstBlockIndex;
6816 VkDeviceSize srcOffset;
6817 VkDeviceSize dstOffset;
6818 VkDeviceSize size;
6819 VmaAllocation hAllocation;
6820 VmaDeviceMemoryBlock* pSrcBlock;
6821 VmaDeviceMemoryBlock* pDstBlock;
6822 };
6823
6824 class VmaDefragmentationAlgorithm;
6825
6826 /*
6827 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6828 Vulkan memory type.
6829
6830 Synchronized internally with a mutex.
6831 */
6832 struct VmaBlockVector
6833 {
6834 VMA_CLASS_NO_COPY(VmaBlockVector)
6835 public:
6836 VmaBlockVector(
6837 VmaAllocator hAllocator,
6838 VmaPool hParentPool,
6839 uint32_t memoryTypeIndex,
6840 VkDeviceSize preferredBlockSize,
6841 size_t minBlockCount,
6842 size_t maxBlockCount,
6843 VkDeviceSize bufferImageGranularity,
6844 uint32_t frameInUseCount,
6845 bool explicitBlockSize,
6846 uint32_t algorithm);
6847 ~VmaBlockVector();
6848
6849 VkResult CreateMinBlocks();
6850
GetAllocatorVmaBlockVector6851 VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector6852 VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector6853 bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector6854 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector6855 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector6856 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector6857 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector6858 uint32_t GetAlgorithm() const { return m_Algorithm; }
6859
6860 void GetPoolStats(VmaPoolStats* pStats);
6861
6862 bool IsEmpty();
6863 bool IsCorruptionDetectionEnabled() const;
6864
6865 VkResult Allocate(
6866 uint32_t currentFrameIndex,
6867 VkDeviceSize size,
6868 VkDeviceSize alignment,
6869 const VmaAllocationCreateInfo& createInfo,
6870 VmaSuballocationType suballocType,
6871 size_t allocationCount,
6872 VmaAllocation* pAllocations);
6873
6874 void Free(const VmaAllocation hAllocation);
6875
6876 // Adds statistics of this BlockVector to pStats.
6877 void AddStats(VmaStats* pStats);
6878
6879 #if VMA_STATS_STRING_ENABLED
6880 void PrintDetailedMap(class VmaJsonWriter& json);
6881 #endif
6882
6883 void MakePoolAllocationsLost(
6884 uint32_t currentFrameIndex,
6885 size_t* pLostAllocationCount);
6886 VkResult CheckCorruption();
6887
6888 // Saves results in pCtx->res.
6889 void Defragment(
6890 class VmaBlockVectorDefragmentationContext* pCtx,
6891 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6892 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6893 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6894 VkCommandBuffer commandBuffer);
6895 void DefragmentationEnd(
6896 class VmaBlockVectorDefragmentationContext* pCtx,
6897 uint32_t flags,
6898 VmaDefragmentationStats* pStats);
6899
6900 uint32_t ProcessDefragmentations(
6901 class VmaBlockVectorDefragmentationContext *pCtx,
6902 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6903
6904 void CommitDefragmentations(
6905 class VmaBlockVectorDefragmentationContext *pCtx,
6906 VmaDefragmentationStats* pStats);
6907
6908 ////////////////////////////////////////////////////////////////////////////////
6909 // To be used only while the m_Mutex is locked. Used during defragmentation.
6910
GetBlockCountVmaBlockVector6911 size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector6912 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6913 size_t CalcAllocationCount() const;
6914 bool IsBufferImageGranularityConflictPossible() const;
6915
6916 private:
6917 friend class VmaDefragmentationAlgorithm_Generic;
6918
6919 const VmaAllocator m_hAllocator;
6920 const VmaPool m_hParentPool;
6921 const uint32_t m_MemoryTypeIndex;
6922 const VkDeviceSize m_PreferredBlockSize;
6923 const size_t m_MinBlockCount;
6924 const size_t m_MaxBlockCount;
6925 const VkDeviceSize m_BufferImageGranularity;
6926 const uint32_t m_FrameInUseCount;
6927 const bool m_ExplicitBlockSize;
6928 const uint32_t m_Algorithm;
6929 VMA_RW_MUTEX m_Mutex;
6930
6931 /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
6932 a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
6933 bool m_HasEmptyBlock;
6934 // Incrementally sorted by sumFreeSize, ascending.
6935 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
6936 uint32_t m_NextBlockId;
6937
6938 VkDeviceSize CalcMaxBlockSize() const;
6939
6940 // Finds and removes given block from vector.
6941 void Remove(VmaDeviceMemoryBlock* pBlock);
6942
6943 // Performs single step in sorting m_Blocks. They may not be fully sorted
6944 // after this call.
6945 void IncrementallySortBlocks();
6946
6947 VkResult AllocatePage(
6948 uint32_t currentFrameIndex,
6949 VkDeviceSize size,
6950 VkDeviceSize alignment,
6951 const VmaAllocationCreateInfo& createInfo,
6952 VmaSuballocationType suballocType,
6953 VmaAllocation* pAllocation);
6954
6955 // To be used only without CAN_MAKE_OTHER_LOST flag.
6956 VkResult AllocateFromBlock(
6957 VmaDeviceMemoryBlock* pBlock,
6958 uint32_t currentFrameIndex,
6959 VkDeviceSize size,
6960 VkDeviceSize alignment,
6961 VmaAllocationCreateFlags allocFlags,
6962 void* pUserData,
6963 VmaSuballocationType suballocType,
6964 uint32_t strategy,
6965 VmaAllocation* pAllocation);
6966
6967 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
6968
6969 // Saves result to pCtx->res.
6970 void ApplyDefragmentationMovesCpu(
6971 class VmaBlockVectorDefragmentationContext* pDefragCtx,
6972 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
6973 // Saves result to pCtx->res.
6974 void ApplyDefragmentationMovesGpu(
6975 class VmaBlockVectorDefragmentationContext* pDefragCtx,
6976 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6977 VkCommandBuffer commandBuffer);
6978
6979 /*
6980 Used during defragmentation. pDefragmentationStats is optional. It's in/out
6981 - updated with new data.
6982 */
6983 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
6984
6985 void UpdateHasEmptyBlock();
6986 };
6987
6988 struct VmaPool_T
6989 {
6990 VMA_CLASS_NO_COPY(VmaPool_T)
6991 public:
6992 VmaBlockVector m_BlockVector;
6993
6994 VmaPool_T(
6995 VmaAllocator hAllocator,
6996 const VmaPoolCreateInfo& createInfo,
6997 VkDeviceSize preferredBlockSize);
6998 ~VmaPool_T();
6999
GetIdVmaPool_T7000 uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T7001 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7002
GetNameVmaPool_T7003 const char* GetName() const { return m_Name; }
7004 void SetName(const char* pName);
7005
7006 #if VMA_STATS_STRING_ENABLED
7007 //void PrintDetailedMap(class VmaStringBuilder& sb);
7008 #endif
7009
7010 private:
7011 uint32_t m_Id;
7012 char* m_Name;
7013 };
7014
7015 /*
7016 Performs defragmentation:
7017
7018 - Updates `pBlockVector->m_pMetadata`.
7019 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7020 - Does not move actual data, only returns requested moves as `moves`.
7021 */
7022 class VmaDefragmentationAlgorithm
7023 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)7024 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7025 public:
7026 VmaDefragmentationAlgorithm(
7027 VmaAllocator hAllocator,
7028 VmaBlockVector* pBlockVector,
7029 uint32_t currentFrameIndex) :
7030 m_hAllocator(hAllocator),
7031 m_pBlockVector(pBlockVector),
7032 m_CurrentFrameIndex(currentFrameIndex)
7033 {
7034 }
~VmaDefragmentationAlgorithm()7035 virtual ~VmaDefragmentationAlgorithm()
7036 {
7037 }
7038
7039 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7040 virtual void AddAll() = 0;
7041
7042 virtual VkResult Defragment(
7043 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7044 VkDeviceSize maxBytesToMove,
7045 uint32_t maxAllocationsToMove,
7046 VmaDefragmentationFlags flags) = 0;
7047
7048 virtual VkDeviceSize GetBytesMoved() const = 0;
7049 virtual uint32_t GetAllocationsMoved() const = 0;
7050
7051 protected:
7052 VmaAllocator const m_hAllocator;
7053 VmaBlockVector* const m_pBlockVector;
7054 const uint32_t m_CurrentFrameIndex;
7055
7056 struct AllocationInfo
7057 {
7058 VmaAllocation m_hAllocation;
7059 VkBool32* m_pChanged;
7060
AllocationInfoAllocationInfo7061 AllocationInfo() :
7062 m_hAllocation(VK_NULL_HANDLE),
7063 m_pChanged(VMA_NULL)
7064 {
7065 }
AllocationInfoAllocationInfo7066 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7067 m_hAllocation(hAlloc),
7068 m_pChanged(pChanged)
7069 {
7070 }
7071 };
7072 };
7073
7074 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7075 {
7076 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7077 public:
7078 VmaDefragmentationAlgorithm_Generic(
7079 VmaAllocator hAllocator,
7080 VmaBlockVector* pBlockVector,
7081 uint32_t currentFrameIndex,
7082 bool overlappingMoveSupported);
7083 virtual ~VmaDefragmentationAlgorithm_Generic();
7084
7085 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7086 virtual void AddAll() { m_AllAllocations = true; }
7087
7088 virtual VkResult Defragment(
7089 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7090 VkDeviceSize maxBytesToMove,
7091 uint32_t maxAllocationsToMove,
7092 VmaDefragmentationFlags flags);
7093
GetBytesMoved()7094 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7095 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7096
7097 private:
7098 uint32_t m_AllocationCount;
7099 bool m_AllAllocations;
7100
7101 VkDeviceSize m_BytesMoved;
7102 uint32_t m_AllocationsMoved;
7103
7104 struct AllocationInfoSizeGreater
7105 {
operatorAllocationInfoSizeGreater7106 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7107 {
7108 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7109 }
7110 };
7111
7112 struct AllocationInfoOffsetGreater
7113 {
operatorAllocationInfoOffsetGreater7114 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7115 {
7116 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7117 }
7118 };
7119
7120 struct BlockInfo
7121 {
7122 size_t m_OriginalBlockIndex;
7123 VmaDeviceMemoryBlock* m_pBlock;
7124 bool m_HasNonMovableAllocations;
7125 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7126
BlockInfoBlockInfo7127 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7128 m_OriginalBlockIndex(SIZE_MAX),
7129 m_pBlock(VMA_NULL),
7130 m_HasNonMovableAllocations(true),
7131 m_Allocations(pAllocationCallbacks)
7132 {
7133 }
7134
CalcHasNonMovableAllocationsBlockInfo7135 void CalcHasNonMovableAllocations()
7136 {
7137 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7138 const size_t defragmentAllocCount = m_Allocations.size();
7139 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7140 }
7141
SortAllocationsBySizeDescendingBlockInfo7142 void SortAllocationsBySizeDescending()
7143 {
7144 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7145 }
7146
SortAllocationsByOffsetDescendingBlockInfo7147 void SortAllocationsByOffsetDescending()
7148 {
7149 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7150 }
7151 };
7152
7153 struct BlockPointerLess
7154 {
operatorBlockPointerLess7155 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7156 {
7157 return pLhsBlockInfo->m_pBlock < pRhsBlock;
7158 }
operatorBlockPointerLess7159 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7160 {
7161 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7162 }
7163 };
7164
7165 // 1. Blocks with some non-movable allocations go first.
7166 // 2. Blocks with smaller sumFreeSize go first.
7167 struct BlockInfoCompareMoveDestination
7168 {
operatorBlockInfoCompareMoveDestination7169 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7170 {
7171 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7172 {
7173 return true;
7174 }
7175 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7176 {
7177 return false;
7178 }
7179 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7180 {
7181 return true;
7182 }
7183 return false;
7184 }
7185 };
7186
7187 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7188 BlockInfoVector m_Blocks;
7189
7190 VkResult DefragmentRound(
7191 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7192 VkDeviceSize maxBytesToMove,
7193 uint32_t maxAllocationsToMove,
7194 bool freeOldAllocations);
7195
7196 size_t CalcBlocksWithNonMovableCount() const;
7197
7198 static bool MoveMakesSense(
7199 size_t dstBlockIndex, VkDeviceSize dstOffset,
7200 size_t srcBlockIndex, VkDeviceSize srcOffset);
7201 };
7202
7203 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7204 {
7205 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7206 public:
7207 VmaDefragmentationAlgorithm_Fast(
7208 VmaAllocator hAllocator,
7209 VmaBlockVector* pBlockVector,
7210 uint32_t currentFrameIndex,
7211 bool overlappingMoveSupported);
7212 virtual ~VmaDefragmentationAlgorithm_Fast();
7213
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7214 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()7215 virtual void AddAll() { m_AllAllocations = true; }
7216
7217 virtual VkResult Defragment(
7218 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7219 VkDeviceSize maxBytesToMove,
7220 uint32_t maxAllocationsToMove,
7221 VmaDefragmentationFlags flags);
7222
GetBytesMoved()7223 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7224 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7225
7226 private:
7227 struct BlockInfo
7228 {
7229 size_t origBlockIndex;
7230 };
7231
7232 class FreeSpaceDatabase
7233 {
7234 public:
FreeSpaceDatabase()7235 FreeSpaceDatabase()
7236 {
7237 FreeSpace s = {};
7238 s.blockInfoIndex = SIZE_MAX;
7239 for(size_t i = 0; i < MAX_COUNT; ++i)
7240 {
7241 m_FreeSpaces[i] = s;
7242 }
7243 }
7244
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)7245 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7246 {
7247 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7248 {
7249 return;
7250 }
7251
7252 // Find first invalid or the smallest structure.
7253 size_t bestIndex = SIZE_MAX;
7254 for(size_t i = 0; i < MAX_COUNT; ++i)
7255 {
7256 // Empty structure.
7257 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7258 {
7259 bestIndex = i;
7260 break;
7261 }
7262 if(m_FreeSpaces[i].size < size &&
7263 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7264 {
7265 bestIndex = i;
7266 }
7267 }
7268
7269 if(bestIndex != SIZE_MAX)
7270 {
7271 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7272 m_FreeSpaces[bestIndex].offset = offset;
7273 m_FreeSpaces[bestIndex].size = size;
7274 }
7275 }
7276
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)7277 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7278 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7279 {
7280 size_t bestIndex = SIZE_MAX;
7281 VkDeviceSize bestFreeSpaceAfter = 0;
7282 for(size_t i = 0; i < MAX_COUNT; ++i)
7283 {
7284 // Structure is valid.
7285 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7286 {
7287 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7288 // Allocation fits into this structure.
7289 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7290 {
7291 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7292 (dstOffset + size);
7293 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7294 {
7295 bestIndex = i;
7296 bestFreeSpaceAfter = freeSpaceAfter;
7297 }
7298 }
7299 }
7300 }
7301
7302 if(bestIndex != SIZE_MAX)
7303 {
7304 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7305 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7306
7307 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7308 {
7309 // Leave this structure for remaining empty space.
7310 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7311 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7312 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7313 }
7314 else
7315 {
7316 // This structure becomes invalid.
7317 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7318 }
7319
7320 return true;
7321 }
7322
7323 return false;
7324 }
7325
7326 private:
7327 static const size_t MAX_COUNT = 4;
7328
7329 struct FreeSpace
7330 {
7331 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7332 VkDeviceSize offset;
7333 VkDeviceSize size;
7334 } m_FreeSpaces[MAX_COUNT];
7335 };
7336
7337 const bool m_OverlappingMoveSupported;
7338
7339 uint32_t m_AllocationCount;
7340 bool m_AllAllocations;
7341
7342 VkDeviceSize m_BytesMoved;
7343 uint32_t m_AllocationsMoved;
7344
7345 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7346
7347 void PreprocessMetadata();
7348 void PostprocessMetadata();
7349 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7350 };
7351
7352 struct VmaBlockDefragmentationContext
7353 {
7354 enum BLOCK_FLAG
7355 {
7356 BLOCK_FLAG_USED = 0x00000001,
7357 };
7358 uint32_t flags;
7359 VkBuffer hBuffer;
7360 };
7361
7362 class VmaBlockVectorDefragmentationContext
7363 {
7364 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7365 public:
7366 VkResult res;
7367 bool mutexLocked;
7368 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7369 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7370 uint32_t defragmentationMovesProcessed;
7371 uint32_t defragmentationMovesCommitted;
7372 bool hasDefragmentationPlan;
7373
7374 VmaBlockVectorDefragmentationContext(
7375 VmaAllocator hAllocator,
7376 VmaPool hCustomPool, // Optional.
7377 VmaBlockVector* pBlockVector,
7378 uint32_t currFrameIndex);
7379 ~VmaBlockVectorDefragmentationContext();
7380
GetCustomPool()7381 VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()7382 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()7383 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7384
7385 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7386 void AddAll() { m_AllAllocations = true; }
7387
7388 void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7389
7390 private:
7391 const VmaAllocator m_hAllocator;
7392 // Null if not from custom pool.
7393 const VmaPool m_hCustomPool;
7394 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7395 VmaBlockVector* const m_pBlockVector;
7396 const uint32_t m_CurrFrameIndex;
7397 // Owner of this object.
7398 VmaDefragmentationAlgorithm* m_pAlgorithm;
7399
7400 struct AllocInfo
7401 {
7402 VmaAllocation hAlloc;
7403 VkBool32* pChanged;
7404 };
7405 // Used between constructor and Begin.
7406 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7407 bool m_AllAllocations;
7408 };
7409
7410 struct VmaDefragmentationContext_T
7411 {
7412 private:
7413 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7414 public:
7415 VmaDefragmentationContext_T(
7416 VmaAllocator hAllocator,
7417 uint32_t currFrameIndex,
7418 uint32_t flags,
7419 VmaDefragmentationStats* pStats);
7420 ~VmaDefragmentationContext_T();
7421
7422 void AddPools(uint32_t poolCount, const VmaPool* pPools);
7423 void AddAllocations(
7424 uint32_t allocationCount,
7425 const VmaAllocation* pAllocations,
7426 VkBool32* pAllocationsChanged);
7427
7428 /*
7429 Returns:
7430 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7431 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7432 - Negative value if error occured and object can be destroyed immediately.
7433 */
7434 VkResult Defragment(
7435 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7436 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7437 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7438
7439 VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7440 VkResult DefragmentPassEnd();
7441
7442 private:
7443 const VmaAllocator m_hAllocator;
7444 const uint32_t m_CurrFrameIndex;
7445 const uint32_t m_Flags;
7446 VmaDefragmentationStats* const m_pStats;
7447
7448 VkDeviceSize m_MaxCpuBytesToMove;
7449 uint32_t m_MaxCpuAllocationsToMove;
7450 VkDeviceSize m_MaxGpuBytesToMove;
7451 uint32_t m_MaxGpuAllocationsToMove;
7452
7453 // Owner of these objects.
7454 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7455 // Owner of these objects.
7456 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7457 };
7458
7459 #if VMA_RECORDING_ENABLED
7460
7461 class VmaRecorder
7462 {
7463 public:
7464 VmaRecorder();
7465 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7466 void WriteConfiguration(
7467 const VkPhysicalDeviceProperties& devProps,
7468 const VkPhysicalDeviceMemoryProperties& memProps,
7469 uint32_t vulkanApiVersion,
7470 bool dedicatedAllocationExtensionEnabled,
7471 bool bindMemory2ExtensionEnabled,
7472 bool memoryBudgetExtensionEnabled,
7473 bool deviceCoherentMemoryExtensionEnabled);
7474 ~VmaRecorder();
7475
7476 void RecordCreateAllocator(uint32_t frameIndex);
7477 void RecordDestroyAllocator(uint32_t frameIndex);
7478 void RecordCreatePool(uint32_t frameIndex,
7479 const VmaPoolCreateInfo& createInfo,
7480 VmaPool pool);
7481 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7482 void RecordAllocateMemory(uint32_t frameIndex,
7483 const VkMemoryRequirements& vkMemReq,
7484 const VmaAllocationCreateInfo& createInfo,
7485 VmaAllocation allocation);
7486 void RecordAllocateMemoryPages(uint32_t frameIndex,
7487 const VkMemoryRequirements& vkMemReq,
7488 const VmaAllocationCreateInfo& createInfo,
7489 uint64_t allocationCount,
7490 const VmaAllocation* pAllocations);
7491 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7492 const VkMemoryRequirements& vkMemReq,
7493 bool requiresDedicatedAllocation,
7494 bool prefersDedicatedAllocation,
7495 const VmaAllocationCreateInfo& createInfo,
7496 VmaAllocation allocation);
7497 void RecordAllocateMemoryForImage(uint32_t frameIndex,
7498 const VkMemoryRequirements& vkMemReq,
7499 bool requiresDedicatedAllocation,
7500 bool prefersDedicatedAllocation,
7501 const VmaAllocationCreateInfo& createInfo,
7502 VmaAllocation allocation);
7503 void RecordFreeMemory(uint32_t frameIndex,
7504 VmaAllocation allocation);
7505 void RecordFreeMemoryPages(uint32_t frameIndex,
7506 uint64_t allocationCount,
7507 const VmaAllocation* pAllocations);
7508 void RecordSetAllocationUserData(uint32_t frameIndex,
7509 VmaAllocation allocation,
7510 const void* pUserData);
7511 void RecordCreateLostAllocation(uint32_t frameIndex,
7512 VmaAllocation allocation);
7513 void RecordMapMemory(uint32_t frameIndex,
7514 VmaAllocation allocation);
7515 void RecordUnmapMemory(uint32_t frameIndex,
7516 VmaAllocation allocation);
7517 void RecordFlushAllocation(uint32_t frameIndex,
7518 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7519 void RecordInvalidateAllocation(uint32_t frameIndex,
7520 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7521 void RecordCreateBuffer(uint32_t frameIndex,
7522 const VkBufferCreateInfo& bufCreateInfo,
7523 const VmaAllocationCreateInfo& allocCreateInfo,
7524 VmaAllocation allocation);
7525 void RecordCreateImage(uint32_t frameIndex,
7526 const VkImageCreateInfo& imageCreateInfo,
7527 const VmaAllocationCreateInfo& allocCreateInfo,
7528 VmaAllocation allocation);
7529 void RecordDestroyBuffer(uint32_t frameIndex,
7530 VmaAllocation allocation);
7531 void RecordDestroyImage(uint32_t frameIndex,
7532 VmaAllocation allocation);
7533 void RecordTouchAllocation(uint32_t frameIndex,
7534 VmaAllocation allocation);
7535 void RecordGetAllocationInfo(uint32_t frameIndex,
7536 VmaAllocation allocation);
7537 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7538 VmaPool pool);
7539 void RecordDefragmentationBegin(uint32_t frameIndex,
7540 const VmaDefragmentationInfo2& info,
7541 VmaDefragmentationContext ctx);
7542 void RecordDefragmentationEnd(uint32_t frameIndex,
7543 VmaDefragmentationContext ctx);
7544 void RecordSetPoolName(uint32_t frameIndex,
7545 VmaPool pool,
7546 const char* name);
7547
7548 private:
7549 struct CallParams
7550 {
7551 uint32_t threadId;
7552 double time;
7553 };
7554
7555 class UserDataString
7556 {
7557 public:
7558 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()7559 const char* GetString() const { return m_Str; }
7560
7561 private:
7562 char m_PtrStr[17];
7563 const char* m_Str;
7564 };
7565
7566 bool m_UseMutex;
7567 VmaRecordFlags m_Flags;
7568 FILE* m_File;
7569 VMA_MUTEX m_FileMutex;
7570 std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7571
7572 void GetBasicParams(CallParams& outParams);
7573
7574 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7575 template<typename T>
PrintPointerList(uint64_t count,const T * pItems)7576 void PrintPointerList(uint64_t count, const T* pItems)
7577 {
7578 if(count)
7579 {
7580 fprintf(m_File, "%p", pItems[0]);
7581 for(uint64_t i = 1; i < count; ++i)
7582 {
7583 fprintf(m_File, " %p", pItems[i]);
7584 }
7585 }
7586 }
7587
7588 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7589 void Flush();
7590 };
7591
7592 #endif // #if VMA_RECORDING_ENABLED
7593
7594 /*
7595 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7596 */
7597 class VmaAllocationObjectAllocator
7598 {
7599 VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7600 public:
7601 VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7602
7603 template<typename... Types> VmaAllocation Allocate(Types... args);
7604 void Free(VmaAllocation hAlloc);
7605
7606 private:
7607 VMA_MUTEX m_Mutex;
7608 VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7609 };
7610
7611 struct VmaCurrentBudgetData
7612 {
7613 VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7614 VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7615
7616 #if VMA_MEMORY_BUDGET
7617 VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7618 VMA_RW_MUTEX m_BudgetMutex;
7619 uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7620 uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7621 uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7622 #endif // #if VMA_MEMORY_BUDGET
7623
VmaCurrentBudgetDataVmaCurrentBudgetData7624 VmaCurrentBudgetData()
7625 {
7626 for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7627 {
7628 m_BlockBytes[heapIndex] = 0;
7629 m_AllocationBytes[heapIndex] = 0;
7630 #if VMA_MEMORY_BUDGET
7631 m_VulkanUsage[heapIndex] = 0;
7632 m_VulkanBudget[heapIndex] = 0;
7633 m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7634 #endif
7635 }
7636
7637 #if VMA_MEMORY_BUDGET
7638 m_OperationsSinceBudgetFetch = 0;
7639 #endif
7640 }
7641
AddAllocationVmaCurrentBudgetData7642 void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7643 {
7644 m_AllocationBytes[heapIndex] += allocationSize;
7645 #if VMA_MEMORY_BUDGET
7646 ++m_OperationsSinceBudgetFetch;
7647 #endif
7648 }
7649
RemoveAllocationVmaCurrentBudgetData7650 void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7651 {
7652 VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7653 m_AllocationBytes[heapIndex] -= allocationSize;
7654 #if VMA_MEMORY_BUDGET
7655 ++m_OperationsSinceBudgetFetch;
7656 #endif
7657 }
7658 };
7659
7660 // Main allocator object.
7661 struct VmaAllocator_T
7662 {
7663 VMA_CLASS_NO_COPY(VmaAllocator_T)
7664 public:
7665 bool m_UseMutex;
7666 uint32_t m_VulkanApiVersion;
7667 bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7668 bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7669 bool m_UseExtMemoryBudget;
7670 bool m_UseAmdDeviceCoherentMemory;
7671 bool m_UseKhrBufferDeviceAddress;
7672 VkDevice m_hDevice;
7673 VkInstance m_hInstance;
7674 bool m_AllocationCallbacksSpecified;
7675 VkAllocationCallbacks m_AllocationCallbacks;
7676 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7677 VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7678
7679 // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7680 uint32_t m_HeapSizeLimitMask;
7681
7682 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7683 VkPhysicalDeviceMemoryProperties m_MemProps;
7684
7685 // Default pools.
7686 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7687
7688 // Each vector is sorted by memory (handle value).
7689 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7690 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7691 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7692
7693 VmaCurrentBudgetData m_Budget;
7694
7695 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7696 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7697 ~VmaAllocator_T();
7698
GetAllocationCallbacksVmaAllocator_T7699 const VkAllocationCallbacks* GetAllocationCallbacks() const
7700 {
7701 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7702 }
GetVulkanFunctionsVmaAllocator_T7703 const VmaVulkanFunctions& GetVulkanFunctions() const
7704 {
7705 return m_VulkanFunctions;
7706 }
7707
GetPhysicalDeviceVmaAllocator_T7708 VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7709
GetBufferImageGranularityVmaAllocator_T7710 VkDeviceSize GetBufferImageGranularity() const
7711 {
7712 return VMA_MAX(
7713 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7714 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7715 }
7716
GetMemoryHeapCountVmaAllocator_T7717 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T7718 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7719
MemoryTypeIndexToHeapIndexVmaAllocator_T7720 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7721 {
7722 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7723 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7724 }
7725 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T7726 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7727 {
7728 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7729 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7730 }
7731 // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T7732 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7733 {
7734 return IsMemoryTypeNonCoherent(memTypeIndex) ?
7735 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7736 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7737 }
7738
IsIntegratedGpuVmaAllocator_T7739 bool IsIntegratedGpu() const
7740 {
7741 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7742 }
7743
GetGlobalMemoryTypeBitsVmaAllocator_T7744 uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7745
7746 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T7747 VmaRecorder* GetRecorder() const { return m_pRecorder; }
7748 #endif
7749
7750 void GetBufferMemoryRequirements(
7751 VkBuffer hBuffer,
7752 VkMemoryRequirements& memReq,
7753 bool& requiresDedicatedAllocation,
7754 bool& prefersDedicatedAllocation) const;
7755 void GetImageMemoryRequirements(
7756 VkImage hImage,
7757 VkMemoryRequirements& memReq,
7758 bool& requiresDedicatedAllocation,
7759 bool& prefersDedicatedAllocation) const;
7760
7761 // Main allocation function.
7762 VkResult AllocateMemory(
7763 const VkMemoryRequirements& vkMemReq,
7764 bool requiresDedicatedAllocation,
7765 bool prefersDedicatedAllocation,
7766 VkBuffer dedicatedBuffer,
7767 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7768 VkImage dedicatedImage,
7769 const VmaAllocationCreateInfo& createInfo,
7770 VmaSuballocationType suballocType,
7771 size_t allocationCount,
7772 VmaAllocation* pAllocations);
7773
7774 // Main deallocation function.
7775 void FreeMemory(
7776 size_t allocationCount,
7777 const VmaAllocation* pAllocations);
7778
7779 VkResult ResizeAllocation(
7780 const VmaAllocation alloc,
7781 VkDeviceSize newSize);
7782
7783 void CalculateStats(VmaStats* pStats);
7784
7785 void GetBudget(
7786 VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7787
7788 #if VMA_STATS_STRING_ENABLED
7789 void PrintDetailedMap(class VmaJsonWriter& json);
7790 #endif
7791
7792 VkResult DefragmentationBegin(
7793 const VmaDefragmentationInfo2& info,
7794 VmaDefragmentationStats* pStats,
7795 VmaDefragmentationContext* pContext);
7796 VkResult DefragmentationEnd(
7797 VmaDefragmentationContext context);
7798
7799 VkResult DefragmentationPassBegin(
7800 VmaDefragmentationPassInfo* pInfo,
7801 VmaDefragmentationContext context);
7802 VkResult DefragmentationPassEnd(
7803 VmaDefragmentationContext context);
7804
7805 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7806 bool TouchAllocation(VmaAllocation hAllocation);
7807
7808 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7809 void DestroyPool(VmaPool pool);
7810 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7811
7812 void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T7813 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7814
7815 void MakePoolAllocationsLost(
7816 VmaPool hPool,
7817 size_t* pLostAllocationCount);
7818 VkResult CheckPoolCorruption(VmaPool hPool);
7819 VkResult CheckCorruption(uint32_t memoryTypeBits);
7820
7821 void CreateLostAllocation(VmaAllocation* pAllocation);
7822
7823 // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7824 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7825 // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7826 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7827 // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7828 VkResult BindVulkanBuffer(
7829 VkDeviceMemory memory,
7830 VkDeviceSize memoryOffset,
7831 VkBuffer buffer,
7832 const void* pNext);
7833 // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7834 VkResult BindVulkanImage(
7835 VkDeviceMemory memory,
7836 VkDeviceSize memoryOffset,
7837 VkImage image,
7838 const void* pNext);
7839
7840 VkResult Map(VmaAllocation hAllocation, void** ppData);
7841 void Unmap(VmaAllocation hAllocation);
7842
7843 VkResult BindBufferMemory(
7844 VmaAllocation hAllocation,
7845 VkDeviceSize allocationLocalOffset,
7846 VkBuffer hBuffer,
7847 const void* pNext);
7848 VkResult BindImageMemory(
7849 VmaAllocation hAllocation,
7850 VkDeviceSize allocationLocalOffset,
7851 VkImage hImage,
7852 const void* pNext);
7853
7854 VkResult FlushOrInvalidateAllocation(
7855 VmaAllocation hAllocation,
7856 VkDeviceSize offset, VkDeviceSize size,
7857 VMA_CACHE_OPERATION op);
7858 VkResult FlushOrInvalidateAllocations(
7859 uint32_t allocationCount,
7860 const VmaAllocation* allocations,
7861 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7862 VMA_CACHE_OPERATION op);
7863
7864 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7865
7866 /*
7867 Returns bit mask of memory types that can support defragmentation on GPU as
7868 they support creation of required buffer for copy operations.
7869 */
7870 uint32_t GetGpuDefragmentationMemoryTypeBits();
7871
7872 private:
7873 VkDeviceSize m_PreferredLargeHeapBlockSize;
7874
7875 VkPhysicalDevice m_PhysicalDevice;
7876 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7877 VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7878
7879 VMA_RW_MUTEX m_PoolsMutex;
7880 // Protected by m_PoolsMutex. Sorted by pointer value.
7881 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7882 uint32_t m_NextPoolId;
7883
7884 VmaVulkanFunctions m_VulkanFunctions;
7885
7886 // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7887 uint32_t m_GlobalMemoryTypeBits;
7888
7889 #if VMA_RECORDING_ENABLED
7890 VmaRecorder* m_pRecorder;
7891 #endif
7892
7893 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7894
7895 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7896 void ImportVulkanFunctions_Static();
7897 #endif
7898
7899 void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
7900
7901 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
7902 void ImportVulkanFunctions_Dynamic();
7903 #endif
7904
7905 void ValidateVulkanFunctions();
7906
7907 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
7908
7909 VkResult AllocateMemoryOfType(
7910 VkDeviceSize size,
7911 VkDeviceSize alignment,
7912 bool dedicatedAllocation,
7913 VkBuffer dedicatedBuffer,
7914 VkBufferUsageFlags dedicatedBufferUsage,
7915 VkImage dedicatedImage,
7916 const VmaAllocationCreateInfo& createInfo,
7917 uint32_t memTypeIndex,
7918 VmaSuballocationType suballocType,
7919 size_t allocationCount,
7920 VmaAllocation* pAllocations);
7921
7922 // Helper function only to be used inside AllocateDedicatedMemory.
7923 VkResult AllocateDedicatedMemoryPage(
7924 VkDeviceSize size,
7925 VmaSuballocationType suballocType,
7926 uint32_t memTypeIndex,
7927 const VkMemoryAllocateInfo& allocInfo,
7928 bool map,
7929 bool isUserDataString,
7930 void* pUserData,
7931 VmaAllocation* pAllocation);
7932
7933 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
7934 VkResult AllocateDedicatedMemory(
7935 VkDeviceSize size,
7936 VmaSuballocationType suballocType,
7937 uint32_t memTypeIndex,
7938 bool withinBudget,
7939 bool map,
7940 bool isUserDataString,
7941 void* pUserData,
7942 VkBuffer dedicatedBuffer,
7943 VkBufferUsageFlags dedicatedBufferUsage,
7944 VkImage dedicatedImage,
7945 size_t allocationCount,
7946 VmaAllocation* pAllocations);
7947
7948 void FreeDedicatedMemory(const VmaAllocation allocation);
7949
7950 /*
7951 Calculates and returns bit mask of memory types that can support defragmentation
7952 on GPU as they support creation of required buffer for copy operations.
7953 */
7954 uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
7955
7956 uint32_t CalculateGlobalMemoryTypeBits() const;
7957
7958 bool GetFlushOrInvalidateRange(
7959 VmaAllocation allocation,
7960 VkDeviceSize offset, VkDeviceSize size,
7961 VkMappedMemoryRange& outRange) const;
7962
7963 #if VMA_MEMORY_BUDGET
7964 void UpdateVulkanBudget();
7965 #endif // #if VMA_MEMORY_BUDGET
7966 };
7967
7968 ////////////////////////////////////////////////////////////////////////////////
7969 // Memory allocation #2 after VmaAllocator_T definition
7970
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)7971 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
7972 {
7973 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
7974 }
7975
VmaFree(VmaAllocator hAllocator,void * ptr)7976 static void VmaFree(VmaAllocator hAllocator, void* ptr)
7977 {
7978 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
7979 }
7980
7981 template<typename T>
VmaAllocate(VmaAllocator hAllocator)7982 static T* VmaAllocate(VmaAllocator hAllocator)
7983 {
7984 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
7985 }
7986
7987 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)7988 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
7989 {
7990 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
7991 }
7992
7993 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)7994 static void vma_delete(VmaAllocator hAllocator, T* ptr)
7995 {
7996 if(ptr != VMA_NULL)
7997 {
7998 ptr->~T();
7999 VmaFree(hAllocator, ptr);
8000 }
8001 }
8002
8003 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)8004 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8005 {
8006 if(ptr != VMA_NULL)
8007 {
8008 for(size_t i = count; i--; )
8009 ptr[i].~T();
8010 VmaFree(hAllocator, ptr);
8011 }
8012 }
8013
8014 ////////////////////////////////////////////////////////////////////////////////
8015 // VmaStringBuilder
8016
8017 #if VMA_STATS_STRING_ENABLED
8018
8019 class VmaStringBuilder
8020 {
8021 public:
VmaStringBuilder(VmaAllocator alloc)8022 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()8023 size_t GetLength() const { return m_Data.size(); }
GetData()8024 const char* GetData() const { return m_Data.data(); }
8025
Add(char ch)8026 void Add(char ch) { m_Data.push_back(ch); }
8027 void Add(const char* pStr);
AddNewLine()8028 void AddNewLine() { Add('\n'); }
8029 void AddNumber(uint32_t num);
8030 void AddNumber(uint64_t num);
8031 void AddPointer(const void* ptr);
8032
8033 private:
8034 VmaVector< char, VmaStlAllocator<char> > m_Data;
8035 };
8036
Add(const char * pStr)8037 void VmaStringBuilder::Add(const char* pStr)
8038 {
8039 const size_t strLen = strlen(pStr);
8040 if(strLen > 0)
8041 {
8042 const size_t oldCount = m_Data.size();
8043 m_Data.resize(oldCount + strLen);
8044 memcpy(m_Data.data() + oldCount, pStr, strLen);
8045 }
8046 }
8047
AddNumber(uint32_t num)8048 void VmaStringBuilder::AddNumber(uint32_t num)
8049 {
8050 char buf[11];
8051 buf[10] = '\0';
8052 char *p = &buf[10];
8053 do
8054 {
8055 *--p = '0' + (num % 10);
8056 num /= 10;
8057 }
8058 while(num);
8059 Add(p);
8060 }
8061
AddNumber(uint64_t num)8062 void VmaStringBuilder::AddNumber(uint64_t num)
8063 {
8064 char buf[21];
8065 buf[20] = '\0';
8066 char *p = &buf[20];
8067 do
8068 {
8069 *--p = '0' + (num % 10);
8070 num /= 10;
8071 }
8072 while(num);
8073 Add(p);
8074 }
8075
AddPointer(const void * ptr)8076 void VmaStringBuilder::AddPointer(const void* ptr)
8077 {
8078 char buf[21];
8079 VmaPtrToStr(buf, sizeof(buf), ptr);
8080 Add(buf);
8081 }
8082
8083 #endif // #if VMA_STATS_STRING_ENABLED
8084
8085 ////////////////////////////////////////////////////////////////////////////////
8086 // VmaJsonWriter
8087
8088 #if VMA_STATS_STRING_ENABLED
8089
8090 class VmaJsonWriter
8091 {
8092 VMA_CLASS_NO_COPY(VmaJsonWriter)
8093 public:
8094 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8095 ~VmaJsonWriter();
8096
8097 void BeginObject(bool singleLine = false);
8098 void EndObject();
8099
8100 void BeginArray(bool singleLine = false);
8101 void EndArray();
8102
8103 void WriteString(const char* pStr);
8104 void BeginString(const char* pStr = VMA_NULL);
8105 void ContinueString(const char* pStr);
8106 void ContinueString(uint32_t n);
8107 void ContinueString(uint64_t n);
8108 void ContinueString_Pointer(const void* ptr);
8109 void EndString(const char* pStr = VMA_NULL);
8110
8111 void WriteNumber(uint32_t n);
8112 void WriteNumber(uint64_t n);
8113 void WriteBool(bool b);
8114 void WriteNull();
8115
8116 private:
8117 static const char* const INDENT;
8118
8119 enum COLLECTION_TYPE
8120 {
8121 COLLECTION_TYPE_OBJECT,
8122 COLLECTION_TYPE_ARRAY,
8123 };
8124 struct StackItem
8125 {
8126 COLLECTION_TYPE type;
8127 uint32_t valueCount;
8128 bool singleLineMode;
8129 };
8130
8131 VmaStringBuilder& m_SB;
8132 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8133 bool m_InsideString;
8134
8135 void BeginValue(bool isString);
8136 void WriteIndent(bool oneLess = false);
8137 };
8138
8139 const char* const VmaJsonWriter::INDENT = " ";
8140
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)8141 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8142 m_SB(sb),
8143 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8144 m_InsideString(false)
8145 {
8146 }
8147
~VmaJsonWriter()8148 VmaJsonWriter::~VmaJsonWriter()
8149 {
8150 VMA_ASSERT(!m_InsideString);
8151 VMA_ASSERT(m_Stack.empty());
8152 }
8153
BeginObject(bool singleLine)8154 void VmaJsonWriter::BeginObject(bool singleLine)
8155 {
8156 VMA_ASSERT(!m_InsideString);
8157
8158 BeginValue(false);
8159 m_SB.Add('{');
8160
8161 StackItem item;
8162 item.type = COLLECTION_TYPE_OBJECT;
8163 item.valueCount = 0;
8164 item.singleLineMode = singleLine;
8165 m_Stack.push_back(item);
8166 }
8167
EndObject()8168 void VmaJsonWriter::EndObject()
8169 {
8170 VMA_ASSERT(!m_InsideString);
8171
8172 WriteIndent(true);
8173 m_SB.Add('}');
8174
8175 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8176 m_Stack.pop_back();
8177 }
8178
BeginArray(bool singleLine)8179 void VmaJsonWriter::BeginArray(bool singleLine)
8180 {
8181 VMA_ASSERT(!m_InsideString);
8182
8183 BeginValue(false);
8184 m_SB.Add('[');
8185
8186 StackItem item;
8187 item.type = COLLECTION_TYPE_ARRAY;
8188 item.valueCount = 0;
8189 item.singleLineMode = singleLine;
8190 m_Stack.push_back(item);
8191 }
8192
EndArray()8193 void VmaJsonWriter::EndArray()
8194 {
8195 VMA_ASSERT(!m_InsideString);
8196
8197 WriteIndent(true);
8198 m_SB.Add(']');
8199
8200 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8201 m_Stack.pop_back();
8202 }
8203
WriteString(const char * pStr)8204 void VmaJsonWriter::WriteString(const char* pStr)
8205 {
8206 BeginString(pStr);
8207 EndString();
8208 }
8209
BeginString(const char * pStr)8210 void VmaJsonWriter::BeginString(const char* pStr)
8211 {
8212 VMA_ASSERT(!m_InsideString);
8213
8214 BeginValue(true);
8215 m_SB.Add('"');
8216 m_InsideString = true;
8217 if(pStr != VMA_NULL && pStr[0] != '\0')
8218 {
8219 ContinueString(pStr);
8220 }
8221 }
8222
ContinueString(const char * pStr)8223 void VmaJsonWriter::ContinueString(const char* pStr)
8224 {
8225 VMA_ASSERT(m_InsideString);
8226
8227 const size_t strLen = strlen(pStr);
8228 for(size_t i = 0; i < strLen; ++i)
8229 {
8230 char ch = pStr[i];
8231 if(ch == '\\')
8232 {
8233 m_SB.Add("\\\\");
8234 }
8235 else if(ch == '"')
8236 {
8237 m_SB.Add("\\\"");
8238 }
8239 else if(ch >= 32)
8240 {
8241 m_SB.Add(ch);
8242 }
8243 else switch(ch)
8244 {
8245 case '\b':
8246 m_SB.Add("\\b");
8247 break;
8248 case '\f':
8249 m_SB.Add("\\f");
8250 break;
8251 case '\n':
8252 m_SB.Add("\\n");
8253 break;
8254 case '\r':
8255 m_SB.Add("\\r");
8256 break;
8257 case '\t':
8258 m_SB.Add("\\t");
8259 break;
8260 default:
8261 VMA_ASSERT(0 && "Character not currently supported.");
8262 break;
8263 }
8264 }
8265 }
8266
ContinueString(uint32_t n)8267 void VmaJsonWriter::ContinueString(uint32_t n)
8268 {
8269 VMA_ASSERT(m_InsideString);
8270 m_SB.AddNumber(n);
8271 }
8272
ContinueString(uint64_t n)8273 void VmaJsonWriter::ContinueString(uint64_t n)
8274 {
8275 VMA_ASSERT(m_InsideString);
8276 m_SB.AddNumber(n);
8277 }
8278
ContinueString_Pointer(const void * ptr)8279 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8280 {
8281 VMA_ASSERT(m_InsideString);
8282 m_SB.AddPointer(ptr);
8283 }
8284
EndString(const char * pStr)8285 void VmaJsonWriter::EndString(const char* pStr)
8286 {
8287 VMA_ASSERT(m_InsideString);
8288 if(pStr != VMA_NULL && pStr[0] != '\0')
8289 {
8290 ContinueString(pStr);
8291 }
8292 m_SB.Add('"');
8293 m_InsideString = false;
8294 }
8295
WriteNumber(uint32_t n)8296 void VmaJsonWriter::WriteNumber(uint32_t n)
8297 {
8298 VMA_ASSERT(!m_InsideString);
8299 BeginValue(false);
8300 m_SB.AddNumber(n);
8301 }
8302
WriteNumber(uint64_t n)8303 void VmaJsonWriter::WriteNumber(uint64_t n)
8304 {
8305 VMA_ASSERT(!m_InsideString);
8306 BeginValue(false);
8307 m_SB.AddNumber(n);
8308 }
8309
WriteBool(bool b)8310 void VmaJsonWriter::WriteBool(bool b)
8311 {
8312 VMA_ASSERT(!m_InsideString);
8313 BeginValue(false);
8314 m_SB.Add(b ? "true" : "false");
8315 }
8316
WriteNull()8317 void VmaJsonWriter::WriteNull()
8318 {
8319 VMA_ASSERT(!m_InsideString);
8320 BeginValue(false);
8321 m_SB.Add("null");
8322 }
8323
BeginValue(bool isString)8324 void VmaJsonWriter::BeginValue(bool isString)
8325 {
8326 if(!m_Stack.empty())
8327 {
8328 StackItem& currItem = m_Stack.back();
8329 if(currItem.type == COLLECTION_TYPE_OBJECT &&
8330 currItem.valueCount % 2 == 0)
8331 {
8332 VMA_ASSERT(isString);
8333 }
8334
8335 if(currItem.type == COLLECTION_TYPE_OBJECT &&
8336 currItem.valueCount % 2 != 0)
8337 {
8338 m_SB.Add(": ");
8339 }
8340 else if(currItem.valueCount > 0)
8341 {
8342 m_SB.Add(", ");
8343 WriteIndent();
8344 }
8345 else
8346 {
8347 WriteIndent();
8348 }
8349 ++currItem.valueCount;
8350 }
8351 }
8352
WriteIndent(bool oneLess)8353 void VmaJsonWriter::WriteIndent(bool oneLess)
8354 {
8355 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8356 {
8357 m_SB.AddNewLine();
8358
8359 size_t count = m_Stack.size();
8360 if(count > 0 && oneLess)
8361 {
8362 --count;
8363 }
8364 for(size_t i = 0; i < count; ++i)
8365 {
8366 m_SB.Add(INDENT);
8367 }
8368 }
8369 }
8370
8371 #endif // #if VMA_STATS_STRING_ENABLED
8372
8373 ////////////////////////////////////////////////////////////////////////////////
8374
SetUserData(VmaAllocator hAllocator,void * pUserData)8375 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8376 {
8377 if(IsUserDataString())
8378 {
8379 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8380
8381 FreeUserDataString(hAllocator);
8382
8383 if(pUserData != VMA_NULL)
8384 {
8385 m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8386 }
8387 }
8388 else
8389 {
8390 m_pUserData = pUserData;
8391 }
8392 }
8393
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)8394 void VmaAllocation_T::ChangeBlockAllocation(
8395 VmaAllocator hAllocator,
8396 VmaDeviceMemoryBlock* block,
8397 VkDeviceSize offset)
8398 {
8399 VMA_ASSERT(block != VMA_NULL);
8400 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8401
8402 // Move mapping reference counter from old block to new block.
8403 if(block != m_BlockAllocation.m_Block)
8404 {
8405 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8406 if(IsPersistentMap())
8407 ++mapRefCount;
8408 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8409 block->Map(hAllocator, mapRefCount, VMA_NULL);
8410 }
8411
8412 m_BlockAllocation.m_Block = block;
8413 m_BlockAllocation.m_Offset = offset;
8414 }
8415
ChangeOffset(VkDeviceSize newOffset)8416 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8417 {
8418 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8419 m_BlockAllocation.m_Offset = newOffset;
8420 }
8421
GetOffset()8422 VkDeviceSize VmaAllocation_T::GetOffset() const
8423 {
8424 switch(m_Type)
8425 {
8426 case ALLOCATION_TYPE_BLOCK:
8427 return m_BlockAllocation.m_Offset;
8428 case ALLOCATION_TYPE_DEDICATED:
8429 return 0;
8430 default:
8431 VMA_ASSERT(0);
8432 return 0;
8433 }
8434 }
8435
GetMemory()8436 VkDeviceMemory VmaAllocation_T::GetMemory() const
8437 {
8438 switch(m_Type)
8439 {
8440 case ALLOCATION_TYPE_BLOCK:
8441 return m_BlockAllocation.m_Block->GetDeviceMemory();
8442 case ALLOCATION_TYPE_DEDICATED:
8443 return m_DedicatedAllocation.m_hMemory;
8444 default:
8445 VMA_ASSERT(0);
8446 return VK_NULL_HANDLE;
8447 }
8448 }
8449
GetMappedData()8450 void* VmaAllocation_T::GetMappedData() const
8451 {
8452 switch(m_Type)
8453 {
8454 case ALLOCATION_TYPE_BLOCK:
8455 if(m_MapCount != 0)
8456 {
8457 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8458 VMA_ASSERT(pBlockData != VMA_NULL);
8459 return (char*)pBlockData + m_BlockAllocation.m_Offset;
8460 }
8461 else
8462 {
8463 return VMA_NULL;
8464 }
8465 break;
8466 case ALLOCATION_TYPE_DEDICATED:
8467 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8468 return m_DedicatedAllocation.m_pMappedData;
8469 default:
8470 VMA_ASSERT(0);
8471 return VMA_NULL;
8472 }
8473 }
8474
CanBecomeLost()8475 bool VmaAllocation_T::CanBecomeLost() const
8476 {
8477 switch(m_Type)
8478 {
8479 case ALLOCATION_TYPE_BLOCK:
8480 return m_BlockAllocation.m_CanBecomeLost;
8481 case ALLOCATION_TYPE_DEDICATED:
8482 return false;
8483 default:
8484 VMA_ASSERT(0);
8485 return false;
8486 }
8487 }
8488
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8489 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8490 {
8491 VMA_ASSERT(CanBecomeLost());
8492
8493 /*
8494 Warning: This is a carefully designed algorithm.
8495 Do not modify unless you really know what you're doing :)
8496 */
8497 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8498 for(;;)
8499 {
8500 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8501 {
8502 VMA_ASSERT(0);
8503 return false;
8504 }
8505 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8506 {
8507 return false;
8508 }
8509 else // Last use time earlier than current time.
8510 {
8511 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8512 {
8513 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8514 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8515 return true;
8516 }
8517 }
8518 }
8519 }
8520
8521 #if VMA_STATS_STRING_ENABLED
8522
8523 // Correspond to values of enum VmaSuballocationType.
8524 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8525 "FREE",
8526 "UNKNOWN",
8527 "BUFFER",
8528 "IMAGE_UNKNOWN",
8529 "IMAGE_LINEAR",
8530 "IMAGE_OPTIMAL",
8531 };
8532
PrintParameters(class VmaJsonWriter & json)8533 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8534 {
8535 json.WriteString("Type");
8536 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8537
8538 json.WriteString("Size");
8539 json.WriteNumber(m_Size);
8540
8541 if(m_pUserData != VMA_NULL)
8542 {
8543 json.WriteString("UserData");
8544 if(IsUserDataString())
8545 {
8546 json.WriteString((const char*)m_pUserData);
8547 }
8548 else
8549 {
8550 json.BeginString();
8551 json.ContinueString_Pointer(m_pUserData);
8552 json.EndString();
8553 }
8554 }
8555
8556 json.WriteString("CreationFrameIndex");
8557 json.WriteNumber(m_CreationFrameIndex);
8558
8559 json.WriteString("LastUseFrameIndex");
8560 json.WriteNumber(GetLastUseFrameIndex());
8561
8562 if(m_BufferImageUsage != 0)
8563 {
8564 json.WriteString("Usage");
8565 json.WriteNumber(m_BufferImageUsage);
8566 }
8567 }
8568
8569 #endif
8570
FreeUserDataString(VmaAllocator hAllocator)8571 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8572 {
8573 VMA_ASSERT(IsUserDataString());
8574 VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8575 m_pUserData = VMA_NULL;
8576 }
8577
BlockAllocMap()8578 void VmaAllocation_T::BlockAllocMap()
8579 {
8580 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8581
8582 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8583 {
8584 ++m_MapCount;
8585 }
8586 else
8587 {
8588 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8589 }
8590 }
8591
BlockAllocUnmap()8592 void VmaAllocation_T::BlockAllocUnmap()
8593 {
8594 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8595
8596 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8597 {
8598 --m_MapCount;
8599 }
8600 else
8601 {
8602 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8603 }
8604 }
8605
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)8606 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8607 {
8608 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8609
8610 if(m_MapCount != 0)
8611 {
8612 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8613 {
8614 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8615 *ppData = m_DedicatedAllocation.m_pMappedData;
8616 ++m_MapCount;
8617 return VK_SUCCESS;
8618 }
8619 else
8620 {
8621 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8622 return VK_ERROR_MEMORY_MAP_FAILED;
8623 }
8624 }
8625 else
8626 {
8627 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8628 hAllocator->m_hDevice,
8629 m_DedicatedAllocation.m_hMemory,
8630 0, // offset
8631 VK_WHOLE_SIZE,
8632 0, // flags
8633 ppData);
8634 if(result == VK_SUCCESS)
8635 {
8636 m_DedicatedAllocation.m_pMappedData = *ppData;
8637 m_MapCount = 1;
8638 }
8639 return result;
8640 }
8641 }
8642
DedicatedAllocUnmap(VmaAllocator hAllocator)8643 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8644 {
8645 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8646
8647 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8648 {
8649 --m_MapCount;
8650 if(m_MapCount == 0)
8651 {
8652 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8653 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8654 hAllocator->m_hDevice,
8655 m_DedicatedAllocation.m_hMemory);
8656 }
8657 }
8658 else
8659 {
8660 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8661 }
8662 }
8663
8664 #if VMA_STATS_STRING_ENABLED
8665
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)8666 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8667 {
8668 json.BeginObject();
8669
8670 json.WriteString("Blocks");
8671 json.WriteNumber(stat.blockCount);
8672
8673 json.WriteString("Allocations");
8674 json.WriteNumber(stat.allocationCount);
8675
8676 json.WriteString("UnusedRanges");
8677 json.WriteNumber(stat.unusedRangeCount);
8678
8679 json.WriteString("UsedBytes");
8680 json.WriteNumber(stat.usedBytes);
8681
8682 json.WriteString("UnusedBytes");
8683 json.WriteNumber(stat.unusedBytes);
8684
8685 if(stat.allocationCount > 1)
8686 {
8687 json.WriteString("AllocationSize");
8688 json.BeginObject(true);
8689 json.WriteString("Min");
8690 json.WriteNumber(stat.allocationSizeMin);
8691 json.WriteString("Avg");
8692 json.WriteNumber(stat.allocationSizeAvg);
8693 json.WriteString("Max");
8694 json.WriteNumber(stat.allocationSizeMax);
8695 json.EndObject();
8696 }
8697
8698 if(stat.unusedRangeCount > 1)
8699 {
8700 json.WriteString("UnusedRangeSize");
8701 json.BeginObject(true);
8702 json.WriteString("Min");
8703 json.WriteNumber(stat.unusedRangeSizeMin);
8704 json.WriteString("Avg");
8705 json.WriteNumber(stat.unusedRangeSizeAvg);
8706 json.WriteString("Max");
8707 json.WriteNumber(stat.unusedRangeSizeMax);
8708 json.EndObject();
8709 }
8710
8711 json.EndObject();
8712 }
8713
8714 #endif // #if VMA_STATS_STRING_ENABLED
8715
8716 struct VmaSuballocationItemSizeLess
8717 {
operatorVmaSuballocationItemSizeLess8718 bool operator()(
8719 const VmaSuballocationList::iterator lhs,
8720 const VmaSuballocationList::iterator rhs) const
8721 {
8722 return lhs->size < rhs->size;
8723 }
operatorVmaSuballocationItemSizeLess8724 bool operator()(
8725 const VmaSuballocationList::iterator lhs,
8726 VkDeviceSize rhsSize) const
8727 {
8728 return lhs->size < rhsSize;
8729 }
8730 };
8731
8732
8733 ////////////////////////////////////////////////////////////////////////////////
8734 // class VmaBlockMetadata
8735
VmaBlockMetadata(VmaAllocator hAllocator)8736 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8737 m_Size(0),
8738 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8739 {
8740 }
8741
8742 #if VMA_STATS_STRING_ENABLED
8743
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)8744 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8745 VkDeviceSize unusedBytes,
8746 size_t allocationCount,
8747 size_t unusedRangeCount) const
8748 {
8749 json.BeginObject();
8750
8751 json.WriteString("TotalBytes");
8752 json.WriteNumber(GetSize());
8753
8754 json.WriteString("UnusedBytes");
8755 json.WriteNumber(unusedBytes);
8756
8757 json.WriteString("Allocations");
8758 json.WriteNumber((uint64_t)allocationCount);
8759
8760 json.WriteString("UnusedRanges");
8761 json.WriteNumber((uint64_t)unusedRangeCount);
8762
8763 json.WriteString("Suballocations");
8764 json.BeginArray();
8765 }
8766
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)8767 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8768 VkDeviceSize offset,
8769 VmaAllocation hAllocation) const
8770 {
8771 json.BeginObject(true);
8772
8773 json.WriteString("Offset");
8774 json.WriteNumber(offset);
8775
8776 hAllocation->PrintParameters(json);
8777
8778 json.EndObject();
8779 }
8780
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)8781 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8782 VkDeviceSize offset,
8783 VkDeviceSize size) const
8784 {
8785 json.BeginObject(true);
8786
8787 json.WriteString("Offset");
8788 json.WriteNumber(offset);
8789
8790 json.WriteString("Type");
8791 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8792
8793 json.WriteString("Size");
8794 json.WriteNumber(size);
8795
8796 json.EndObject();
8797 }
8798
PrintDetailedMap_End(class VmaJsonWriter & json)8799 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8800 {
8801 json.EndArray();
8802 json.EndObject();
8803 }
8804
8805 #endif // #if VMA_STATS_STRING_ENABLED
8806
8807 ////////////////////////////////////////////////////////////////////////////////
8808 // class VmaBlockMetadata_Generic
8809
VmaBlockMetadata_Generic(VmaAllocator hAllocator)8810 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8811 VmaBlockMetadata(hAllocator),
8812 m_FreeCount(0),
8813 m_SumFreeSize(0),
8814 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8815 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8816 {
8817 }
8818
~VmaBlockMetadata_Generic()8819 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8820 {
8821 }
8822
Init(VkDeviceSize size)8823 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8824 {
8825 VmaBlockMetadata::Init(size);
8826
8827 m_FreeCount = 1;
8828 m_SumFreeSize = size;
8829
8830 VmaSuballocation suballoc = {};
8831 suballoc.offset = 0;
8832 suballoc.size = size;
8833 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8834 suballoc.hAllocation = VK_NULL_HANDLE;
8835
8836 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8837 m_Suballocations.push_back(suballoc);
8838 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8839 --suballocItem;
8840 m_FreeSuballocationsBySize.push_back(suballocItem);
8841 }
8842
Validate()8843 bool VmaBlockMetadata_Generic::Validate() const
8844 {
8845 VMA_VALIDATE(!m_Suballocations.empty());
8846
8847 // Expected offset of new suballocation as calculated from previous ones.
8848 VkDeviceSize calculatedOffset = 0;
8849 // Expected number of free suballocations as calculated from traversing their list.
8850 uint32_t calculatedFreeCount = 0;
8851 // Expected sum size of free suballocations as calculated from traversing their list.
8852 VkDeviceSize calculatedSumFreeSize = 0;
8853 // Expected number of free suballocations that should be registered in
8854 // m_FreeSuballocationsBySize calculated from traversing their list.
8855 size_t freeSuballocationsToRegister = 0;
8856 // True if previous visited suballocation was free.
8857 bool prevFree = false;
8858
8859 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8860 suballocItem != m_Suballocations.cend();
8861 ++suballocItem)
8862 {
8863 const VmaSuballocation& subAlloc = *suballocItem;
8864
8865 // Actual offset of this suballocation doesn't match expected one.
8866 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8867
8868 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8869 // Two adjacent free suballocations are invalid. They should be merged.
8870 VMA_VALIDATE(!prevFree || !currFree);
8871
8872 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8873
8874 if(currFree)
8875 {
8876 calculatedSumFreeSize += subAlloc.size;
8877 ++calculatedFreeCount;
8878 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8879 {
8880 ++freeSuballocationsToRegister;
8881 }
8882
8883 // Margin required between allocations - every free space must be at least that large.
8884 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8885 }
8886 else
8887 {
8888 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8889 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8890
8891 // Margin required between allocations - previous allocation must be free.
8892 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8893 }
8894
8895 calculatedOffset += subAlloc.size;
8896 prevFree = currFree;
8897 }
8898
8899 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
8900 // match expected one.
8901 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
8902
8903 VkDeviceSize lastSize = 0;
8904 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
8905 {
8906 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
8907
8908 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
8909 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8910 // They must be sorted by size ascending.
8911 VMA_VALIDATE(suballocItem->size >= lastSize);
8912
8913 lastSize = suballocItem->size;
8914 }
8915
8916 // Check if totals match calculacted values.
8917 VMA_VALIDATE(ValidateFreeSuballocationList());
8918 VMA_VALIDATE(calculatedOffset == GetSize());
8919 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
8920 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
8921
8922 return true;
8923 }
8924
GetUnusedRangeSizeMax()8925 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
8926 {
8927 if(!m_FreeSuballocationsBySize.empty())
8928 {
8929 return m_FreeSuballocationsBySize.back()->size;
8930 }
8931 else
8932 {
8933 return 0;
8934 }
8935 }
8936
IsEmpty()8937 bool VmaBlockMetadata_Generic::IsEmpty() const
8938 {
8939 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
8940 }
8941
CalcAllocationStatInfo(VmaStatInfo & outInfo)8942 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8943 {
8944 outInfo.blockCount = 1;
8945
8946 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8947 outInfo.allocationCount = rangeCount - m_FreeCount;
8948 outInfo.unusedRangeCount = m_FreeCount;
8949
8950 outInfo.unusedBytes = m_SumFreeSize;
8951 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
8952
8953 outInfo.allocationSizeMin = UINT64_MAX;
8954 outInfo.allocationSizeMax = 0;
8955 outInfo.unusedRangeSizeMin = UINT64_MAX;
8956 outInfo.unusedRangeSizeMax = 0;
8957
8958 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8959 suballocItem != m_Suballocations.cend();
8960 ++suballocItem)
8961 {
8962 const VmaSuballocation& suballoc = *suballocItem;
8963 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8964 {
8965 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8966 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
8967 }
8968 else
8969 {
8970 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
8971 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
8972 }
8973 }
8974 }
8975
AddPoolStats(VmaPoolStats & inoutStats)8976 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
8977 {
8978 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
8979
8980 inoutStats.size += GetSize();
8981 inoutStats.unusedSize += m_SumFreeSize;
8982 inoutStats.allocationCount += rangeCount - m_FreeCount;
8983 inoutStats.unusedRangeCount += m_FreeCount;
8984 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
8985 }
8986
8987 #if VMA_STATS_STRING_ENABLED
8988
PrintDetailedMap(class VmaJsonWriter & json)8989 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
8990 {
8991 PrintDetailedMap_Begin(json,
8992 m_SumFreeSize, // unusedBytes
8993 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
8994 m_FreeCount); // unusedRangeCount
8995
8996 size_t i = 0;
8997 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8998 suballocItem != m_Suballocations.cend();
8999 ++suballocItem, ++i)
9000 {
9001 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9002 {
9003 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9004 }
9005 else
9006 {
9007 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9008 }
9009 }
9010
9011 PrintDetailedMap_End(json);
9012 }
9013
9014 #endif // #if VMA_STATS_STRING_ENABLED
9015
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9016 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9017 uint32_t currentFrameIndex,
9018 uint32_t frameInUseCount,
9019 VkDeviceSize bufferImageGranularity,
9020 VkDeviceSize allocSize,
9021 VkDeviceSize allocAlignment,
9022 bool upperAddress,
9023 VmaSuballocationType allocType,
9024 bool canMakeOtherLost,
9025 uint32_t strategy,
9026 VmaAllocationRequest* pAllocationRequest)
9027 {
9028 VMA_ASSERT(allocSize > 0);
9029 VMA_ASSERT(!upperAddress);
9030 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9031 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9032 VMA_HEAVY_ASSERT(Validate());
9033
9034 pAllocationRequest->type = VmaAllocationRequestType::Normal;
9035
9036 // There is not enough total free space in this block to fullfill the request: Early return.
9037 if(canMakeOtherLost == false &&
9038 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9039 {
9040 return false;
9041 }
9042
9043 // New algorithm, efficiently searching freeSuballocationsBySize.
9044 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9045 if(freeSuballocCount > 0)
9046 {
9047 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9048 {
9049 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9050 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9051 m_FreeSuballocationsBySize.data(),
9052 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9053 allocSize + 2 * VMA_DEBUG_MARGIN,
9054 VmaSuballocationItemSizeLess());
9055 size_t index = it - m_FreeSuballocationsBySize.data();
9056 for(; index < freeSuballocCount; ++index)
9057 {
9058 if(CheckAllocation(
9059 currentFrameIndex,
9060 frameInUseCount,
9061 bufferImageGranularity,
9062 allocSize,
9063 allocAlignment,
9064 allocType,
9065 m_FreeSuballocationsBySize[index],
9066 false, // canMakeOtherLost
9067 &pAllocationRequest->offset,
9068 &pAllocationRequest->itemsToMakeLostCount,
9069 &pAllocationRequest->sumFreeSize,
9070 &pAllocationRequest->sumItemSize))
9071 {
9072 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9073 return true;
9074 }
9075 }
9076 }
9077 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9078 {
9079 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9080 it != m_Suballocations.end();
9081 ++it)
9082 {
9083 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9084 currentFrameIndex,
9085 frameInUseCount,
9086 bufferImageGranularity,
9087 allocSize,
9088 allocAlignment,
9089 allocType,
9090 it,
9091 false, // canMakeOtherLost
9092 &pAllocationRequest->offset,
9093 &pAllocationRequest->itemsToMakeLostCount,
9094 &pAllocationRequest->sumFreeSize,
9095 &pAllocationRequest->sumItemSize))
9096 {
9097 pAllocationRequest->item = it;
9098 return true;
9099 }
9100 }
9101 }
9102 else // WORST_FIT, FIRST_FIT
9103 {
9104 // Search staring from biggest suballocations.
9105 for(size_t index = freeSuballocCount; index--; )
9106 {
9107 if(CheckAllocation(
9108 currentFrameIndex,
9109 frameInUseCount,
9110 bufferImageGranularity,
9111 allocSize,
9112 allocAlignment,
9113 allocType,
9114 m_FreeSuballocationsBySize[index],
9115 false, // canMakeOtherLost
9116 &pAllocationRequest->offset,
9117 &pAllocationRequest->itemsToMakeLostCount,
9118 &pAllocationRequest->sumFreeSize,
9119 &pAllocationRequest->sumItemSize))
9120 {
9121 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9122 return true;
9123 }
9124 }
9125 }
9126 }
9127
9128 if(canMakeOtherLost)
9129 {
9130 // Brute-force algorithm. TODO: Come up with something better.
9131
9132 bool found = false;
9133 VmaAllocationRequest tmpAllocRequest = {};
9134 tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9135 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9136 suballocIt != m_Suballocations.end();
9137 ++suballocIt)
9138 {
9139 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9140 suballocIt->hAllocation->CanBecomeLost())
9141 {
9142 if(CheckAllocation(
9143 currentFrameIndex,
9144 frameInUseCount,
9145 bufferImageGranularity,
9146 allocSize,
9147 allocAlignment,
9148 allocType,
9149 suballocIt,
9150 canMakeOtherLost,
9151 &tmpAllocRequest.offset,
9152 &tmpAllocRequest.itemsToMakeLostCount,
9153 &tmpAllocRequest.sumFreeSize,
9154 &tmpAllocRequest.sumItemSize))
9155 {
9156 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9157 {
9158 *pAllocationRequest = tmpAllocRequest;
9159 pAllocationRequest->item = suballocIt;
9160 break;
9161 }
9162 if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9163 {
9164 *pAllocationRequest = tmpAllocRequest;
9165 pAllocationRequest->item = suballocIt;
9166 found = true;
9167 }
9168 }
9169 }
9170 }
9171
9172 return found;
9173 }
9174
9175 return false;
9176 }
9177
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)9178 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9179 uint32_t currentFrameIndex,
9180 uint32_t frameInUseCount,
9181 VmaAllocationRequest* pAllocationRequest)
9182 {
9183 VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9184
9185 while(pAllocationRequest->itemsToMakeLostCount > 0)
9186 {
9187 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9188 {
9189 ++pAllocationRequest->item;
9190 }
9191 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9192 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9193 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9194 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9195 {
9196 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9197 --pAllocationRequest->itemsToMakeLostCount;
9198 }
9199 else
9200 {
9201 return false;
9202 }
9203 }
9204
9205 VMA_HEAVY_ASSERT(Validate());
9206 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9207 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9208
9209 return true;
9210 }
9211
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)9212 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9213 {
9214 uint32_t lostAllocationCount = 0;
9215 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9216 it != m_Suballocations.end();
9217 ++it)
9218 {
9219 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9220 it->hAllocation->CanBecomeLost() &&
9221 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9222 {
9223 it = FreeSuballocation(it);
9224 ++lostAllocationCount;
9225 }
9226 }
9227 return lostAllocationCount;
9228 }
9229
CheckCorruption(const void * pBlockData)9230 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9231 {
9232 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9233 it != m_Suballocations.end();
9234 ++it)
9235 {
9236 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9237 {
9238 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9239 {
9240 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9241 return VK_ERROR_VALIDATION_FAILED_EXT;
9242 }
9243 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9244 {
9245 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9246 return VK_ERROR_VALIDATION_FAILED_EXT;
9247 }
9248 }
9249 }
9250
9251 return VK_SUCCESS;
9252 }
9253
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)9254 void VmaBlockMetadata_Generic::Alloc(
9255 const VmaAllocationRequest& request,
9256 VmaSuballocationType type,
9257 VkDeviceSize allocSize,
9258 VmaAllocation hAllocation)
9259 {
9260 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9261 VMA_ASSERT(request.item != m_Suballocations.end());
9262 VmaSuballocation& suballoc = *request.item;
9263 // Given suballocation is a free block.
9264 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9265 // Given offset is inside this suballocation.
9266 VMA_ASSERT(request.offset >= suballoc.offset);
9267 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9268 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9269 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9270
9271 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9272 // it to become used.
9273 UnregisterFreeSuballocation(request.item);
9274
9275 suballoc.offset = request.offset;
9276 suballoc.size = allocSize;
9277 suballoc.type = type;
9278 suballoc.hAllocation = hAllocation;
9279
9280 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9281 if(paddingEnd)
9282 {
9283 VmaSuballocation paddingSuballoc = {};
9284 paddingSuballoc.offset = request.offset + allocSize;
9285 paddingSuballoc.size = paddingEnd;
9286 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9287 VmaSuballocationList::iterator next = request.item;
9288 ++next;
9289 const VmaSuballocationList::iterator paddingEndItem =
9290 m_Suballocations.insert(next, paddingSuballoc);
9291 RegisterFreeSuballocation(paddingEndItem);
9292 }
9293
9294 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9295 if(paddingBegin)
9296 {
9297 VmaSuballocation paddingSuballoc = {};
9298 paddingSuballoc.offset = request.offset - paddingBegin;
9299 paddingSuballoc.size = paddingBegin;
9300 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9301 const VmaSuballocationList::iterator paddingBeginItem =
9302 m_Suballocations.insert(request.item, paddingSuballoc);
9303 RegisterFreeSuballocation(paddingBeginItem);
9304 }
9305
9306 // Update totals.
9307 m_FreeCount = m_FreeCount - 1;
9308 if(paddingBegin > 0)
9309 {
9310 ++m_FreeCount;
9311 }
9312 if(paddingEnd > 0)
9313 {
9314 ++m_FreeCount;
9315 }
9316 m_SumFreeSize -= allocSize;
9317 }
9318
Free(const VmaAllocation allocation)9319 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9320 {
9321 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9322 suballocItem != m_Suballocations.end();
9323 ++suballocItem)
9324 {
9325 VmaSuballocation& suballoc = *suballocItem;
9326 if(suballoc.hAllocation == allocation)
9327 {
9328 FreeSuballocation(suballocItem);
9329 VMA_HEAVY_ASSERT(Validate());
9330 return;
9331 }
9332 }
9333 VMA_ASSERT(0 && "Not found!");
9334 }
9335
FreeAtOffset(VkDeviceSize offset)9336 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9337 {
9338 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9339 suballocItem != m_Suballocations.end();
9340 ++suballocItem)
9341 {
9342 VmaSuballocation& suballoc = *suballocItem;
9343 if(suballoc.offset == offset)
9344 {
9345 FreeSuballocation(suballocItem);
9346 return;
9347 }
9348 }
9349 VMA_ASSERT(0 && "Not found!");
9350 }
9351
ValidateFreeSuballocationList()9352 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9353 {
9354 VkDeviceSize lastSize = 0;
9355 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9356 {
9357 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9358
9359 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9360 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9361 VMA_VALIDATE(it->size >= lastSize);
9362 lastSize = it->size;
9363 }
9364 return true;
9365 }
9366
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)9367 bool VmaBlockMetadata_Generic::CheckAllocation(
9368 uint32_t currentFrameIndex,
9369 uint32_t frameInUseCount,
9370 VkDeviceSize bufferImageGranularity,
9371 VkDeviceSize allocSize,
9372 VkDeviceSize allocAlignment,
9373 VmaSuballocationType allocType,
9374 VmaSuballocationList::const_iterator suballocItem,
9375 bool canMakeOtherLost,
9376 VkDeviceSize* pOffset,
9377 size_t* itemsToMakeLostCount,
9378 VkDeviceSize* pSumFreeSize,
9379 VkDeviceSize* pSumItemSize) const
9380 {
9381 VMA_ASSERT(allocSize > 0);
9382 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9383 VMA_ASSERT(suballocItem != m_Suballocations.cend());
9384 VMA_ASSERT(pOffset != VMA_NULL);
9385
9386 *itemsToMakeLostCount = 0;
9387 *pSumFreeSize = 0;
9388 *pSumItemSize = 0;
9389
9390 if(canMakeOtherLost)
9391 {
9392 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9393 {
9394 *pSumFreeSize = suballocItem->size;
9395 }
9396 else
9397 {
9398 if(suballocItem->hAllocation->CanBecomeLost() &&
9399 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9400 {
9401 ++*itemsToMakeLostCount;
9402 *pSumItemSize = suballocItem->size;
9403 }
9404 else
9405 {
9406 return false;
9407 }
9408 }
9409
9410 // Remaining size is too small for this request: Early return.
9411 if(GetSize() - suballocItem->offset < allocSize)
9412 {
9413 return false;
9414 }
9415
9416 // Start from offset equal to beginning of this suballocation.
9417 *pOffset = suballocItem->offset;
9418
9419 // Apply VMA_DEBUG_MARGIN at the beginning.
9420 if(VMA_DEBUG_MARGIN > 0)
9421 {
9422 *pOffset += VMA_DEBUG_MARGIN;
9423 }
9424
9425 // Apply alignment.
9426 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9427
9428 // Check previous suballocations for BufferImageGranularity conflicts.
9429 // Make bigger alignment if necessary.
9430 if(bufferImageGranularity > 1)
9431 {
9432 bool bufferImageGranularityConflict = false;
9433 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9434 while(prevSuballocItem != m_Suballocations.cbegin())
9435 {
9436 --prevSuballocItem;
9437 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9438 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9439 {
9440 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9441 {
9442 bufferImageGranularityConflict = true;
9443 break;
9444 }
9445 }
9446 else
9447 // Already on previous page.
9448 break;
9449 }
9450 if(bufferImageGranularityConflict)
9451 {
9452 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9453 }
9454 }
9455
9456 // Now that we have final *pOffset, check if we are past suballocItem.
9457 // If yes, return false - this function should be called for another suballocItem as starting point.
9458 if(*pOffset >= suballocItem->offset + suballocItem->size)
9459 {
9460 return false;
9461 }
9462
9463 // Calculate padding at the beginning based on current offset.
9464 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9465
9466 // Calculate required margin at the end.
9467 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9468
9469 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9470 // Another early return check.
9471 if(suballocItem->offset + totalSize > GetSize())
9472 {
9473 return false;
9474 }
9475
9476 // Advance lastSuballocItem until desired size is reached.
9477 // Update itemsToMakeLostCount.
9478 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9479 if(totalSize > suballocItem->size)
9480 {
9481 VkDeviceSize remainingSize = totalSize - suballocItem->size;
9482 while(remainingSize > 0)
9483 {
9484 ++lastSuballocItem;
9485 if(lastSuballocItem == m_Suballocations.cend())
9486 {
9487 return false;
9488 }
9489 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9490 {
9491 *pSumFreeSize += lastSuballocItem->size;
9492 }
9493 else
9494 {
9495 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9496 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9497 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9498 {
9499 ++*itemsToMakeLostCount;
9500 *pSumItemSize += lastSuballocItem->size;
9501 }
9502 else
9503 {
9504 return false;
9505 }
9506 }
9507 remainingSize = (lastSuballocItem->size < remainingSize) ?
9508 remainingSize - lastSuballocItem->size : 0;
9509 }
9510 }
9511
9512 // Check next suballocations for BufferImageGranularity conflicts.
9513 // If conflict exists, we must mark more allocations lost or fail.
9514 if(bufferImageGranularity > 1)
9515 {
9516 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9517 ++nextSuballocItem;
9518 while(nextSuballocItem != m_Suballocations.cend())
9519 {
9520 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9521 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9522 {
9523 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9524 {
9525 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9526 if(nextSuballoc.hAllocation->CanBecomeLost() &&
9527 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9528 {
9529 ++*itemsToMakeLostCount;
9530 }
9531 else
9532 {
9533 return false;
9534 }
9535 }
9536 }
9537 else
9538 {
9539 // Already on next page.
9540 break;
9541 }
9542 ++nextSuballocItem;
9543 }
9544 }
9545 }
9546 else
9547 {
9548 const VmaSuballocation& suballoc = *suballocItem;
9549 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9550
9551 *pSumFreeSize = suballoc.size;
9552
9553 // Size of this suballocation is too small for this request: Early return.
9554 if(suballoc.size < allocSize)
9555 {
9556 return false;
9557 }
9558
9559 // Start from offset equal to beginning of this suballocation.
9560 *pOffset = suballoc.offset;
9561
9562 // Apply VMA_DEBUG_MARGIN at the beginning.
9563 if(VMA_DEBUG_MARGIN > 0)
9564 {
9565 *pOffset += VMA_DEBUG_MARGIN;
9566 }
9567
9568 // Apply alignment.
9569 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9570
9571 // Check previous suballocations for BufferImageGranularity conflicts.
9572 // Make bigger alignment if necessary.
9573 if(bufferImageGranularity > 1)
9574 {
9575 bool bufferImageGranularityConflict = false;
9576 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9577 while(prevSuballocItem != m_Suballocations.cbegin())
9578 {
9579 --prevSuballocItem;
9580 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9581 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9582 {
9583 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9584 {
9585 bufferImageGranularityConflict = true;
9586 break;
9587 }
9588 }
9589 else
9590 // Already on previous page.
9591 break;
9592 }
9593 if(bufferImageGranularityConflict)
9594 {
9595 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9596 }
9597 }
9598
9599 // Calculate padding at the beginning based on current offset.
9600 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9601
9602 // Calculate required margin at the end.
9603 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9604
9605 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9606 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9607 {
9608 return false;
9609 }
9610
9611 // Check next suballocations for BufferImageGranularity conflicts.
9612 // If conflict exists, allocation cannot be made here.
9613 if(bufferImageGranularity > 1)
9614 {
9615 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9616 ++nextSuballocItem;
9617 while(nextSuballocItem != m_Suballocations.cend())
9618 {
9619 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9620 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9621 {
9622 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9623 {
9624 return false;
9625 }
9626 }
9627 else
9628 {
9629 // Already on next page.
9630 break;
9631 }
9632 ++nextSuballocItem;
9633 }
9634 }
9635 }
9636
9637 // All tests passed: Success. pOffset is already filled.
9638 return true;
9639 }
9640
MergeFreeWithNext(VmaSuballocationList::iterator item)9641 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9642 {
9643 VMA_ASSERT(item != m_Suballocations.end());
9644 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9645
9646 VmaSuballocationList::iterator nextItem = item;
9647 ++nextItem;
9648 VMA_ASSERT(nextItem != m_Suballocations.end());
9649 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9650
9651 item->size += nextItem->size;
9652 --m_FreeCount;
9653 m_Suballocations.erase(nextItem);
9654 }
9655
FreeSuballocation(VmaSuballocationList::iterator suballocItem)9656 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9657 {
9658 // Change this suballocation to be marked as free.
9659 VmaSuballocation& suballoc = *suballocItem;
9660 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9661 suballoc.hAllocation = VK_NULL_HANDLE;
9662
9663 // Update totals.
9664 ++m_FreeCount;
9665 m_SumFreeSize += suballoc.size;
9666
9667 // Merge with previous and/or next suballocation if it's also free.
9668 bool mergeWithNext = false;
9669 bool mergeWithPrev = false;
9670
9671 VmaSuballocationList::iterator nextItem = suballocItem;
9672 ++nextItem;
9673 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9674 {
9675 mergeWithNext = true;
9676 }
9677
9678 VmaSuballocationList::iterator prevItem = suballocItem;
9679 if(suballocItem != m_Suballocations.begin())
9680 {
9681 --prevItem;
9682 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9683 {
9684 mergeWithPrev = true;
9685 }
9686 }
9687
9688 if(mergeWithNext)
9689 {
9690 UnregisterFreeSuballocation(nextItem);
9691 MergeFreeWithNext(suballocItem);
9692 }
9693
9694 if(mergeWithPrev)
9695 {
9696 UnregisterFreeSuballocation(prevItem);
9697 MergeFreeWithNext(prevItem);
9698 RegisterFreeSuballocation(prevItem);
9699 return prevItem;
9700 }
9701 else
9702 {
9703 RegisterFreeSuballocation(suballocItem);
9704 return suballocItem;
9705 }
9706 }
9707
RegisterFreeSuballocation(VmaSuballocationList::iterator item)9708 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9709 {
9710 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9711 VMA_ASSERT(item->size > 0);
9712
9713 // You may want to enable this validation at the beginning or at the end of
9714 // this function, depending on what do you want to check.
9715 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9716
9717 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9718 {
9719 if(m_FreeSuballocationsBySize.empty())
9720 {
9721 m_FreeSuballocationsBySize.push_back(item);
9722 }
9723 else
9724 {
9725 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9726 }
9727 }
9728
9729 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9730 }
9731
9732
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)9733 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9734 {
9735 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9736 VMA_ASSERT(item->size > 0);
9737
9738 // You may want to enable this validation at the beginning or at the end of
9739 // this function, depending on what do you want to check.
9740 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9741
9742 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9743 {
9744 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9745 m_FreeSuballocationsBySize.data(),
9746 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9747 item,
9748 VmaSuballocationItemSizeLess());
9749 for(size_t index = it - m_FreeSuballocationsBySize.data();
9750 index < m_FreeSuballocationsBySize.size();
9751 ++index)
9752 {
9753 if(m_FreeSuballocationsBySize[index] == item)
9754 {
9755 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9756 return;
9757 }
9758 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9759 }
9760 VMA_ASSERT(0 && "Not found.");
9761 }
9762
9763 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9764 }
9765
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)9766 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9767 VkDeviceSize bufferImageGranularity,
9768 VmaSuballocationType& inOutPrevSuballocType) const
9769 {
9770 if(bufferImageGranularity == 1 || IsEmpty())
9771 {
9772 return false;
9773 }
9774
9775 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9776 bool typeConflictFound = false;
9777 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9778 it != m_Suballocations.cend();
9779 ++it)
9780 {
9781 const VmaSuballocationType suballocType = it->type;
9782 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9783 {
9784 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9785 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9786 {
9787 typeConflictFound = true;
9788 }
9789 inOutPrevSuballocType = suballocType;
9790 }
9791 }
9792
9793 return typeConflictFound || minAlignment >= bufferImageGranularity;
9794 }
9795
9796 ////////////////////////////////////////////////////////////////////////////////
9797 // class VmaBlockMetadata_Linear
9798
VmaBlockMetadata_Linear(VmaAllocator hAllocator)9799 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9800 VmaBlockMetadata(hAllocator),
9801 m_SumFreeSize(0),
9802 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9803 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9804 m_1stVectorIndex(0),
9805 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9806 m_1stNullItemsBeginCount(0),
9807 m_1stNullItemsMiddleCount(0),
9808 m_2ndNullItemsCount(0)
9809 {
9810 }
9811
~VmaBlockMetadata_Linear()9812 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9813 {
9814 }
9815
Init(VkDeviceSize size)9816 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9817 {
9818 VmaBlockMetadata::Init(size);
9819 m_SumFreeSize = size;
9820 }
9821
Validate()9822 bool VmaBlockMetadata_Linear::Validate() const
9823 {
9824 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9825 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9826
9827 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9828 VMA_VALIDATE(!suballocations1st.empty() ||
9829 suballocations2nd.empty() ||
9830 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9831
9832 if(!suballocations1st.empty())
9833 {
9834 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9835 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9836 // Null item at the end should be just pop_back().
9837 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9838 }
9839 if(!suballocations2nd.empty())
9840 {
9841 // Null item at the end should be just pop_back().
9842 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9843 }
9844
9845 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9846 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9847
9848 VkDeviceSize sumUsedSize = 0;
9849 const size_t suballoc1stCount = suballocations1st.size();
9850 VkDeviceSize offset = VMA_DEBUG_MARGIN;
9851
9852 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9853 {
9854 const size_t suballoc2ndCount = suballocations2nd.size();
9855 size_t nullItem2ndCount = 0;
9856 for(size_t i = 0; i < suballoc2ndCount; ++i)
9857 {
9858 const VmaSuballocation& suballoc = suballocations2nd[i];
9859 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9860
9861 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9862 VMA_VALIDATE(suballoc.offset >= offset);
9863
9864 if(!currFree)
9865 {
9866 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9867 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9868 sumUsedSize += suballoc.size;
9869 }
9870 else
9871 {
9872 ++nullItem2ndCount;
9873 }
9874
9875 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9876 }
9877
9878 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9879 }
9880
9881 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9882 {
9883 const VmaSuballocation& suballoc = suballocations1st[i];
9884 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9885 suballoc.hAllocation == VK_NULL_HANDLE);
9886 }
9887
9888 size_t nullItem1stCount = m_1stNullItemsBeginCount;
9889
9890 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9891 {
9892 const VmaSuballocation& suballoc = suballocations1st[i];
9893 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9894
9895 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9896 VMA_VALIDATE(suballoc.offset >= offset);
9897 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
9898
9899 if(!currFree)
9900 {
9901 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9902 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9903 sumUsedSize += suballoc.size;
9904 }
9905 else
9906 {
9907 ++nullItem1stCount;
9908 }
9909
9910 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9911 }
9912 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
9913
9914 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9915 {
9916 const size_t suballoc2ndCount = suballocations2nd.size();
9917 size_t nullItem2ndCount = 0;
9918 for(size_t i = suballoc2ndCount; i--; )
9919 {
9920 const VmaSuballocation& suballoc = suballocations2nd[i];
9921 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9922
9923 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9924 VMA_VALIDATE(suballoc.offset >= offset);
9925
9926 if(!currFree)
9927 {
9928 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9929 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9930 sumUsedSize += suballoc.size;
9931 }
9932 else
9933 {
9934 ++nullItem2ndCount;
9935 }
9936
9937 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9938 }
9939
9940 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9941 }
9942
9943 VMA_VALIDATE(offset <= GetSize());
9944 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
9945
9946 return true;
9947 }
9948
GetAllocationCount()9949 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
9950 {
9951 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
9952 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
9953 }
9954
GetUnusedRangeSizeMax()9955 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
9956 {
9957 const VkDeviceSize size = GetSize();
9958
9959 /*
9960 We don't consider gaps inside allocation vectors with freed allocations because
9961 they are not suitable for reuse in linear allocator. We consider only space that
9962 is available for new allocations.
9963 */
9964 if(IsEmpty())
9965 {
9966 return size;
9967 }
9968
9969 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9970
9971 switch(m_2ndVectorMode)
9972 {
9973 case SECOND_VECTOR_EMPTY:
9974 /*
9975 Available space is after end of 1st, as well as before beginning of 1st (which
9976 whould make it a ring buffer).
9977 */
9978 {
9979 const size_t suballocations1stCount = suballocations1st.size();
9980 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
9981 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
9982 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
9983 return VMA_MAX(
9984 firstSuballoc.offset,
9985 size - (lastSuballoc.offset + lastSuballoc.size));
9986 }
9987 break;
9988
9989 case SECOND_VECTOR_RING_BUFFER:
9990 /*
9991 Available space is only between end of 2nd and beginning of 1st.
9992 */
9993 {
9994 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9995 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
9996 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
9997 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
9998 }
9999 break;
10000
10001 case SECOND_VECTOR_DOUBLE_STACK:
10002 /*
10003 Available space is only between end of 1st and top of 2nd.
10004 */
10005 {
10006 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10007 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10008 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10009 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10010 }
10011 break;
10012
10013 default:
10014 VMA_ASSERT(0);
10015 return 0;
10016 }
10017 }
10018
CalcAllocationStatInfo(VmaStatInfo & outInfo)10019 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10020 {
10021 const VkDeviceSize size = GetSize();
10022 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10023 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10024 const size_t suballoc1stCount = suballocations1st.size();
10025 const size_t suballoc2ndCount = suballocations2nd.size();
10026
10027 outInfo.blockCount = 1;
10028 outInfo.allocationCount = (uint32_t)GetAllocationCount();
10029 outInfo.unusedRangeCount = 0;
10030 outInfo.usedBytes = 0;
10031 outInfo.allocationSizeMin = UINT64_MAX;
10032 outInfo.allocationSizeMax = 0;
10033 outInfo.unusedRangeSizeMin = UINT64_MAX;
10034 outInfo.unusedRangeSizeMax = 0;
10035
10036 VkDeviceSize lastOffset = 0;
10037
10038 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10039 {
10040 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10041 size_t nextAlloc2ndIndex = 0;
10042 while(lastOffset < freeSpace2ndTo1stEnd)
10043 {
10044 // Find next non-null allocation or move nextAllocIndex to the end.
10045 while(nextAlloc2ndIndex < suballoc2ndCount &&
10046 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10047 {
10048 ++nextAlloc2ndIndex;
10049 }
10050
10051 // Found non-null allocation.
10052 if(nextAlloc2ndIndex < suballoc2ndCount)
10053 {
10054 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10055
10056 // 1. Process free space before this allocation.
10057 if(lastOffset < suballoc.offset)
10058 {
10059 // There is free space from lastOffset to suballoc.offset.
10060 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10061 ++outInfo.unusedRangeCount;
10062 outInfo.unusedBytes += unusedRangeSize;
10063 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10064 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10065 }
10066
10067 // 2. Process this allocation.
10068 // There is allocation with suballoc.offset, suballoc.size.
10069 outInfo.usedBytes += suballoc.size;
10070 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10071 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10072
10073 // 3. Prepare for next iteration.
10074 lastOffset = suballoc.offset + suballoc.size;
10075 ++nextAlloc2ndIndex;
10076 }
10077 // We are at the end.
10078 else
10079 {
10080 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10081 if(lastOffset < freeSpace2ndTo1stEnd)
10082 {
10083 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10084 ++outInfo.unusedRangeCount;
10085 outInfo.unusedBytes += unusedRangeSize;
10086 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10087 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10088 }
10089
10090 // End of loop.
10091 lastOffset = freeSpace2ndTo1stEnd;
10092 }
10093 }
10094 }
10095
10096 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10097 const VkDeviceSize freeSpace1stTo2ndEnd =
10098 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10099 while(lastOffset < freeSpace1stTo2ndEnd)
10100 {
10101 // Find next non-null allocation or move nextAllocIndex to the end.
10102 while(nextAlloc1stIndex < suballoc1stCount &&
10103 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10104 {
10105 ++nextAlloc1stIndex;
10106 }
10107
10108 // Found non-null allocation.
10109 if(nextAlloc1stIndex < suballoc1stCount)
10110 {
10111 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10112
10113 // 1. Process free space before this allocation.
10114 if(lastOffset < suballoc.offset)
10115 {
10116 // There is free space from lastOffset to suballoc.offset.
10117 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10118 ++outInfo.unusedRangeCount;
10119 outInfo.unusedBytes += unusedRangeSize;
10120 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10121 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10122 }
10123
10124 // 2. Process this allocation.
10125 // There is allocation with suballoc.offset, suballoc.size.
10126 outInfo.usedBytes += suballoc.size;
10127 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10128 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10129
10130 // 3. Prepare for next iteration.
10131 lastOffset = suballoc.offset + suballoc.size;
10132 ++nextAlloc1stIndex;
10133 }
10134 // We are at the end.
10135 else
10136 {
10137 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10138 if(lastOffset < freeSpace1stTo2ndEnd)
10139 {
10140 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10141 ++outInfo.unusedRangeCount;
10142 outInfo.unusedBytes += unusedRangeSize;
10143 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10144 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10145 }
10146
10147 // End of loop.
10148 lastOffset = freeSpace1stTo2ndEnd;
10149 }
10150 }
10151
10152 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10153 {
10154 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10155 while(lastOffset < size)
10156 {
10157 // Find next non-null allocation or move nextAllocIndex to the end.
10158 while(nextAlloc2ndIndex != SIZE_MAX &&
10159 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10160 {
10161 --nextAlloc2ndIndex;
10162 }
10163
10164 // Found non-null allocation.
10165 if(nextAlloc2ndIndex != SIZE_MAX)
10166 {
10167 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10168
10169 // 1. Process free space before this allocation.
10170 if(lastOffset < suballoc.offset)
10171 {
10172 // There is free space from lastOffset to suballoc.offset.
10173 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10174 ++outInfo.unusedRangeCount;
10175 outInfo.unusedBytes += unusedRangeSize;
10176 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10177 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10178 }
10179
10180 // 2. Process this allocation.
10181 // There is allocation with suballoc.offset, suballoc.size.
10182 outInfo.usedBytes += suballoc.size;
10183 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10184 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10185
10186 // 3. Prepare for next iteration.
10187 lastOffset = suballoc.offset + suballoc.size;
10188 --nextAlloc2ndIndex;
10189 }
10190 // We are at the end.
10191 else
10192 {
10193 // There is free space from lastOffset to size.
10194 if(lastOffset < size)
10195 {
10196 const VkDeviceSize unusedRangeSize = size - lastOffset;
10197 ++outInfo.unusedRangeCount;
10198 outInfo.unusedBytes += unusedRangeSize;
10199 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10200 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10201 }
10202
10203 // End of loop.
10204 lastOffset = size;
10205 }
10206 }
10207 }
10208
10209 outInfo.unusedBytes = size - outInfo.usedBytes;
10210 }
10211
AddPoolStats(VmaPoolStats & inoutStats)10212 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10213 {
10214 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10215 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10216 const VkDeviceSize size = GetSize();
10217 const size_t suballoc1stCount = suballocations1st.size();
10218 const size_t suballoc2ndCount = suballocations2nd.size();
10219
10220 inoutStats.size += size;
10221
10222 VkDeviceSize lastOffset = 0;
10223
10224 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10225 {
10226 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10227 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10228 while(lastOffset < freeSpace2ndTo1stEnd)
10229 {
10230 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10231 while(nextAlloc2ndIndex < suballoc2ndCount &&
10232 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10233 {
10234 ++nextAlloc2ndIndex;
10235 }
10236
10237 // Found non-null allocation.
10238 if(nextAlloc2ndIndex < suballoc2ndCount)
10239 {
10240 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10241
10242 // 1. Process free space before this allocation.
10243 if(lastOffset < suballoc.offset)
10244 {
10245 // There is free space from lastOffset to suballoc.offset.
10246 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10247 inoutStats.unusedSize += unusedRangeSize;
10248 ++inoutStats.unusedRangeCount;
10249 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10250 }
10251
10252 // 2. Process this allocation.
10253 // There is allocation with suballoc.offset, suballoc.size.
10254 ++inoutStats.allocationCount;
10255
10256 // 3. Prepare for next iteration.
10257 lastOffset = suballoc.offset + suballoc.size;
10258 ++nextAlloc2ndIndex;
10259 }
10260 // We are at the end.
10261 else
10262 {
10263 if(lastOffset < freeSpace2ndTo1stEnd)
10264 {
10265 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10266 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10267 inoutStats.unusedSize += unusedRangeSize;
10268 ++inoutStats.unusedRangeCount;
10269 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10270 }
10271
10272 // End of loop.
10273 lastOffset = freeSpace2ndTo1stEnd;
10274 }
10275 }
10276 }
10277
10278 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10279 const VkDeviceSize freeSpace1stTo2ndEnd =
10280 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10281 while(lastOffset < freeSpace1stTo2ndEnd)
10282 {
10283 // Find next non-null allocation or move nextAllocIndex to the end.
10284 while(nextAlloc1stIndex < suballoc1stCount &&
10285 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10286 {
10287 ++nextAlloc1stIndex;
10288 }
10289
10290 // Found non-null allocation.
10291 if(nextAlloc1stIndex < suballoc1stCount)
10292 {
10293 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10294
10295 // 1. Process free space before this allocation.
10296 if(lastOffset < suballoc.offset)
10297 {
10298 // There is free space from lastOffset to suballoc.offset.
10299 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10300 inoutStats.unusedSize += unusedRangeSize;
10301 ++inoutStats.unusedRangeCount;
10302 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10303 }
10304
10305 // 2. Process this allocation.
10306 // There is allocation with suballoc.offset, suballoc.size.
10307 ++inoutStats.allocationCount;
10308
10309 // 3. Prepare for next iteration.
10310 lastOffset = suballoc.offset + suballoc.size;
10311 ++nextAlloc1stIndex;
10312 }
10313 // We are at the end.
10314 else
10315 {
10316 if(lastOffset < freeSpace1stTo2ndEnd)
10317 {
10318 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10319 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10320 inoutStats.unusedSize += unusedRangeSize;
10321 ++inoutStats.unusedRangeCount;
10322 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10323 }
10324
10325 // End of loop.
10326 lastOffset = freeSpace1stTo2ndEnd;
10327 }
10328 }
10329
10330 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10331 {
10332 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10333 while(lastOffset < size)
10334 {
10335 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10336 while(nextAlloc2ndIndex != SIZE_MAX &&
10337 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10338 {
10339 --nextAlloc2ndIndex;
10340 }
10341
10342 // Found non-null allocation.
10343 if(nextAlloc2ndIndex != SIZE_MAX)
10344 {
10345 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10346
10347 // 1. Process free space before this allocation.
10348 if(lastOffset < suballoc.offset)
10349 {
10350 // There is free space from lastOffset to suballoc.offset.
10351 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10352 inoutStats.unusedSize += unusedRangeSize;
10353 ++inoutStats.unusedRangeCount;
10354 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10355 }
10356
10357 // 2. Process this allocation.
10358 // There is allocation with suballoc.offset, suballoc.size.
10359 ++inoutStats.allocationCount;
10360
10361 // 3. Prepare for next iteration.
10362 lastOffset = suballoc.offset + suballoc.size;
10363 --nextAlloc2ndIndex;
10364 }
10365 // We are at the end.
10366 else
10367 {
10368 if(lastOffset < size)
10369 {
10370 // There is free space from lastOffset to size.
10371 const VkDeviceSize unusedRangeSize = size - lastOffset;
10372 inoutStats.unusedSize += unusedRangeSize;
10373 ++inoutStats.unusedRangeCount;
10374 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10375 }
10376
10377 // End of loop.
10378 lastOffset = size;
10379 }
10380 }
10381 }
10382 }
10383
10384 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10385 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10386 {
10387 const VkDeviceSize size = GetSize();
10388 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10389 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10390 const size_t suballoc1stCount = suballocations1st.size();
10391 const size_t suballoc2ndCount = suballocations2nd.size();
10392
10393 // FIRST PASS
10394
10395 size_t unusedRangeCount = 0;
10396 VkDeviceSize usedBytes = 0;
10397
10398 VkDeviceSize lastOffset = 0;
10399
10400 size_t alloc2ndCount = 0;
10401 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10402 {
10403 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10404 size_t nextAlloc2ndIndex = 0;
10405 while(lastOffset < freeSpace2ndTo1stEnd)
10406 {
10407 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10408 while(nextAlloc2ndIndex < suballoc2ndCount &&
10409 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10410 {
10411 ++nextAlloc2ndIndex;
10412 }
10413
10414 // Found non-null allocation.
10415 if(nextAlloc2ndIndex < suballoc2ndCount)
10416 {
10417 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10418
10419 // 1. Process free space before this allocation.
10420 if(lastOffset < suballoc.offset)
10421 {
10422 // There is free space from lastOffset to suballoc.offset.
10423 ++unusedRangeCount;
10424 }
10425
10426 // 2. Process this allocation.
10427 // There is allocation with suballoc.offset, suballoc.size.
10428 ++alloc2ndCount;
10429 usedBytes += suballoc.size;
10430
10431 // 3. Prepare for next iteration.
10432 lastOffset = suballoc.offset + suballoc.size;
10433 ++nextAlloc2ndIndex;
10434 }
10435 // We are at the end.
10436 else
10437 {
10438 if(lastOffset < freeSpace2ndTo1stEnd)
10439 {
10440 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10441 ++unusedRangeCount;
10442 }
10443
10444 // End of loop.
10445 lastOffset = freeSpace2ndTo1stEnd;
10446 }
10447 }
10448 }
10449
10450 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10451 size_t alloc1stCount = 0;
10452 const VkDeviceSize freeSpace1stTo2ndEnd =
10453 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10454 while(lastOffset < freeSpace1stTo2ndEnd)
10455 {
10456 // Find next non-null allocation or move nextAllocIndex to the end.
10457 while(nextAlloc1stIndex < suballoc1stCount &&
10458 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10459 {
10460 ++nextAlloc1stIndex;
10461 }
10462
10463 // Found non-null allocation.
10464 if(nextAlloc1stIndex < suballoc1stCount)
10465 {
10466 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10467
10468 // 1. Process free space before this allocation.
10469 if(lastOffset < suballoc.offset)
10470 {
10471 // There is free space from lastOffset to suballoc.offset.
10472 ++unusedRangeCount;
10473 }
10474
10475 // 2. Process this allocation.
10476 // There is allocation with suballoc.offset, suballoc.size.
10477 ++alloc1stCount;
10478 usedBytes += suballoc.size;
10479
10480 // 3. Prepare for next iteration.
10481 lastOffset = suballoc.offset + suballoc.size;
10482 ++nextAlloc1stIndex;
10483 }
10484 // We are at the end.
10485 else
10486 {
10487 if(lastOffset < size)
10488 {
10489 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10490 ++unusedRangeCount;
10491 }
10492
10493 // End of loop.
10494 lastOffset = freeSpace1stTo2ndEnd;
10495 }
10496 }
10497
10498 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10499 {
10500 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10501 while(lastOffset < size)
10502 {
10503 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10504 while(nextAlloc2ndIndex != SIZE_MAX &&
10505 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10506 {
10507 --nextAlloc2ndIndex;
10508 }
10509
10510 // Found non-null allocation.
10511 if(nextAlloc2ndIndex != SIZE_MAX)
10512 {
10513 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10514
10515 // 1. Process free space before this allocation.
10516 if(lastOffset < suballoc.offset)
10517 {
10518 // There is free space from lastOffset to suballoc.offset.
10519 ++unusedRangeCount;
10520 }
10521
10522 // 2. Process this allocation.
10523 // There is allocation with suballoc.offset, suballoc.size.
10524 ++alloc2ndCount;
10525 usedBytes += suballoc.size;
10526
10527 // 3. Prepare for next iteration.
10528 lastOffset = suballoc.offset + suballoc.size;
10529 --nextAlloc2ndIndex;
10530 }
10531 // We are at the end.
10532 else
10533 {
10534 if(lastOffset < size)
10535 {
10536 // There is free space from lastOffset to size.
10537 ++unusedRangeCount;
10538 }
10539
10540 // End of loop.
10541 lastOffset = size;
10542 }
10543 }
10544 }
10545
10546 const VkDeviceSize unusedBytes = size - usedBytes;
10547 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10548
10549 // SECOND PASS
10550 lastOffset = 0;
10551
10552 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10553 {
10554 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10555 size_t nextAlloc2ndIndex = 0;
10556 while(lastOffset < freeSpace2ndTo1stEnd)
10557 {
10558 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10559 while(nextAlloc2ndIndex < suballoc2ndCount &&
10560 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10561 {
10562 ++nextAlloc2ndIndex;
10563 }
10564
10565 // Found non-null allocation.
10566 if(nextAlloc2ndIndex < suballoc2ndCount)
10567 {
10568 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10569
10570 // 1. Process free space before this allocation.
10571 if(lastOffset < suballoc.offset)
10572 {
10573 // There is free space from lastOffset to suballoc.offset.
10574 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10575 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10576 }
10577
10578 // 2. Process this allocation.
10579 // There is allocation with suballoc.offset, suballoc.size.
10580 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10581
10582 // 3. Prepare for next iteration.
10583 lastOffset = suballoc.offset + suballoc.size;
10584 ++nextAlloc2ndIndex;
10585 }
10586 // We are at the end.
10587 else
10588 {
10589 if(lastOffset < freeSpace2ndTo1stEnd)
10590 {
10591 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10592 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10593 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10594 }
10595
10596 // End of loop.
10597 lastOffset = freeSpace2ndTo1stEnd;
10598 }
10599 }
10600 }
10601
10602 nextAlloc1stIndex = m_1stNullItemsBeginCount;
10603 while(lastOffset < freeSpace1stTo2ndEnd)
10604 {
10605 // Find next non-null allocation or move nextAllocIndex to the end.
10606 while(nextAlloc1stIndex < suballoc1stCount &&
10607 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10608 {
10609 ++nextAlloc1stIndex;
10610 }
10611
10612 // Found non-null allocation.
10613 if(nextAlloc1stIndex < suballoc1stCount)
10614 {
10615 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10616
10617 // 1. Process free space before this allocation.
10618 if(lastOffset < suballoc.offset)
10619 {
10620 // There is free space from lastOffset to suballoc.offset.
10621 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10622 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10623 }
10624
10625 // 2. Process this allocation.
10626 // There is allocation with suballoc.offset, suballoc.size.
10627 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10628
10629 // 3. Prepare for next iteration.
10630 lastOffset = suballoc.offset + suballoc.size;
10631 ++nextAlloc1stIndex;
10632 }
10633 // We are at the end.
10634 else
10635 {
10636 if(lastOffset < freeSpace1stTo2ndEnd)
10637 {
10638 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10639 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10640 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10641 }
10642
10643 // End of loop.
10644 lastOffset = freeSpace1stTo2ndEnd;
10645 }
10646 }
10647
10648 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10649 {
10650 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10651 while(lastOffset < size)
10652 {
10653 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10654 while(nextAlloc2ndIndex != SIZE_MAX &&
10655 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10656 {
10657 --nextAlloc2ndIndex;
10658 }
10659
10660 // Found non-null allocation.
10661 if(nextAlloc2ndIndex != SIZE_MAX)
10662 {
10663 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10664
10665 // 1. Process free space before this allocation.
10666 if(lastOffset < suballoc.offset)
10667 {
10668 // There is free space from lastOffset to suballoc.offset.
10669 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10670 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10671 }
10672
10673 // 2. Process this allocation.
10674 // There is allocation with suballoc.offset, suballoc.size.
10675 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10676
10677 // 3. Prepare for next iteration.
10678 lastOffset = suballoc.offset + suballoc.size;
10679 --nextAlloc2ndIndex;
10680 }
10681 // We are at the end.
10682 else
10683 {
10684 if(lastOffset < size)
10685 {
10686 // There is free space from lastOffset to size.
10687 const VkDeviceSize unusedRangeSize = size - lastOffset;
10688 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10689 }
10690
10691 // End of loop.
10692 lastOffset = size;
10693 }
10694 }
10695 }
10696
10697 PrintDetailedMap_End(json);
10698 }
10699 #endif // #if VMA_STATS_STRING_ENABLED
10700
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10701 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10702 uint32_t currentFrameIndex,
10703 uint32_t frameInUseCount,
10704 VkDeviceSize bufferImageGranularity,
10705 VkDeviceSize allocSize,
10706 VkDeviceSize allocAlignment,
10707 bool upperAddress,
10708 VmaSuballocationType allocType,
10709 bool canMakeOtherLost,
10710 uint32_t strategy,
10711 VmaAllocationRequest* pAllocationRequest)
10712 {
10713 VMA_ASSERT(allocSize > 0);
10714 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10715 VMA_ASSERT(pAllocationRequest != VMA_NULL);
10716 VMA_HEAVY_ASSERT(Validate());
10717 return upperAddress ?
10718 CreateAllocationRequest_UpperAddress(
10719 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10720 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10721 CreateAllocationRequest_LowerAddress(
10722 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10723 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10724 }
10725
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10726 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10727 uint32_t currentFrameIndex,
10728 uint32_t frameInUseCount,
10729 VkDeviceSize bufferImageGranularity,
10730 VkDeviceSize allocSize,
10731 VkDeviceSize allocAlignment,
10732 VmaSuballocationType allocType,
10733 bool canMakeOtherLost,
10734 uint32_t strategy,
10735 VmaAllocationRequest* pAllocationRequest)
10736 {
10737 const VkDeviceSize size = GetSize();
10738 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10739 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10740
10741 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10742 {
10743 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10744 return false;
10745 }
10746
10747 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10748 if(allocSize > size)
10749 {
10750 return false;
10751 }
10752 VkDeviceSize resultBaseOffset = size - allocSize;
10753 if(!suballocations2nd.empty())
10754 {
10755 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10756 resultBaseOffset = lastSuballoc.offset - allocSize;
10757 if(allocSize > lastSuballoc.offset)
10758 {
10759 return false;
10760 }
10761 }
10762
10763 // Start from offset equal to end of free space.
10764 VkDeviceSize resultOffset = resultBaseOffset;
10765
10766 // Apply VMA_DEBUG_MARGIN at the end.
10767 if(VMA_DEBUG_MARGIN > 0)
10768 {
10769 if(resultOffset < VMA_DEBUG_MARGIN)
10770 {
10771 return false;
10772 }
10773 resultOffset -= VMA_DEBUG_MARGIN;
10774 }
10775
10776 // Apply alignment.
10777 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10778
10779 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10780 // Make bigger alignment if necessary.
10781 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10782 {
10783 bool bufferImageGranularityConflict = false;
10784 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10785 {
10786 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10787 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10788 {
10789 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10790 {
10791 bufferImageGranularityConflict = true;
10792 break;
10793 }
10794 }
10795 else
10796 // Already on previous page.
10797 break;
10798 }
10799 if(bufferImageGranularityConflict)
10800 {
10801 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10802 }
10803 }
10804
10805 // There is enough free space.
10806 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10807 suballocations1st.back().offset + suballocations1st.back().size :
10808 0;
10809 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10810 {
10811 // Check previous suballocations for BufferImageGranularity conflicts.
10812 // If conflict exists, allocation cannot be made here.
10813 if(bufferImageGranularity > 1)
10814 {
10815 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10816 {
10817 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10818 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10819 {
10820 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10821 {
10822 return false;
10823 }
10824 }
10825 else
10826 {
10827 // Already on next page.
10828 break;
10829 }
10830 }
10831 }
10832
10833 // All tests passed: Success.
10834 pAllocationRequest->offset = resultOffset;
10835 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10836 pAllocationRequest->sumItemSize = 0;
10837 // pAllocationRequest->item unused.
10838 pAllocationRequest->itemsToMakeLostCount = 0;
10839 pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10840 return true;
10841 }
10842
10843 return false;
10844 }
10845
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10846 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10847 uint32_t currentFrameIndex,
10848 uint32_t frameInUseCount,
10849 VkDeviceSize bufferImageGranularity,
10850 VkDeviceSize allocSize,
10851 VkDeviceSize allocAlignment,
10852 VmaSuballocationType allocType,
10853 bool canMakeOtherLost,
10854 uint32_t strategy,
10855 VmaAllocationRequest* pAllocationRequest)
10856 {
10857 const VkDeviceSize size = GetSize();
10858 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10859 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10860
10861 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10862 {
10863 // Try to allocate at the end of 1st vector.
10864
10865 VkDeviceSize resultBaseOffset = 0;
10866 if(!suballocations1st.empty())
10867 {
10868 const VmaSuballocation& lastSuballoc = suballocations1st.back();
10869 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10870 }
10871
10872 // Start from offset equal to beginning of free space.
10873 VkDeviceSize resultOffset = resultBaseOffset;
10874
10875 // Apply VMA_DEBUG_MARGIN at the beginning.
10876 if(VMA_DEBUG_MARGIN > 0)
10877 {
10878 resultOffset += VMA_DEBUG_MARGIN;
10879 }
10880
10881 // Apply alignment.
10882 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10883
10884 // Check previous suballocations for BufferImageGranularity conflicts.
10885 // Make bigger alignment if necessary.
10886 if(bufferImageGranularity > 1 && !suballocations1st.empty())
10887 {
10888 bool bufferImageGranularityConflict = false;
10889 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10890 {
10891 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10892 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10893 {
10894 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10895 {
10896 bufferImageGranularityConflict = true;
10897 break;
10898 }
10899 }
10900 else
10901 // Already on previous page.
10902 break;
10903 }
10904 if(bufferImageGranularityConflict)
10905 {
10906 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10907 }
10908 }
10909
10910 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
10911 suballocations2nd.back().offset : size;
10912
10913 // There is enough free space at the end after alignment.
10914 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
10915 {
10916 // Check next suballocations for BufferImageGranularity conflicts.
10917 // If conflict exists, allocation cannot be made here.
10918 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10919 {
10920 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10921 {
10922 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10923 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10924 {
10925 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
10926 {
10927 return false;
10928 }
10929 }
10930 else
10931 {
10932 // Already on previous page.
10933 break;
10934 }
10935 }
10936 }
10937
10938 // All tests passed: Success.
10939 pAllocationRequest->offset = resultOffset;
10940 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
10941 pAllocationRequest->sumItemSize = 0;
10942 // pAllocationRequest->item, customData unused.
10943 pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
10944 pAllocationRequest->itemsToMakeLostCount = 0;
10945 return true;
10946 }
10947 }
10948
10949 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
10950 // beginning of 1st vector as the end of free space.
10951 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10952 {
10953 VMA_ASSERT(!suballocations1st.empty());
10954
10955 VkDeviceSize resultBaseOffset = 0;
10956 if(!suballocations2nd.empty())
10957 {
10958 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10959 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10960 }
10961
10962 // Start from offset equal to beginning of free space.
10963 VkDeviceSize resultOffset = resultBaseOffset;
10964
10965 // Apply VMA_DEBUG_MARGIN at the beginning.
10966 if(VMA_DEBUG_MARGIN > 0)
10967 {
10968 resultOffset += VMA_DEBUG_MARGIN;
10969 }
10970
10971 // Apply alignment.
10972 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10973
10974 // Check previous suballocations for BufferImageGranularity conflicts.
10975 // Make bigger alignment if necessary.
10976 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10977 {
10978 bool bufferImageGranularityConflict = false;
10979 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
10980 {
10981 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
10982 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10983 {
10984 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10985 {
10986 bufferImageGranularityConflict = true;
10987 break;
10988 }
10989 }
10990 else
10991 // Already on previous page.
10992 break;
10993 }
10994 if(bufferImageGranularityConflict)
10995 {
10996 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
10997 }
10998 }
10999
11000 pAllocationRequest->itemsToMakeLostCount = 0;
11001 pAllocationRequest->sumItemSize = 0;
11002 size_t index1st = m_1stNullItemsBeginCount;
11003
11004 if(canMakeOtherLost)
11005 {
11006 while(index1st < suballocations1st.size() &&
11007 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11008 {
11009 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11010 const VmaSuballocation& suballoc = suballocations1st[index1st];
11011 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11012 {
11013 // No problem.
11014 }
11015 else
11016 {
11017 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11018 if(suballoc.hAllocation->CanBecomeLost() &&
11019 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11020 {
11021 ++pAllocationRequest->itemsToMakeLostCount;
11022 pAllocationRequest->sumItemSize += suballoc.size;
11023 }
11024 else
11025 {
11026 return false;
11027 }
11028 }
11029 ++index1st;
11030 }
11031
11032 // Check next suballocations for BufferImageGranularity conflicts.
11033 // If conflict exists, we must mark more allocations lost or fail.
11034 if(bufferImageGranularity > 1)
11035 {
11036 while(index1st < suballocations1st.size())
11037 {
11038 const VmaSuballocation& suballoc = suballocations1st[index1st];
11039 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11040 {
11041 if(suballoc.hAllocation != VK_NULL_HANDLE)
11042 {
11043 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11044 if(suballoc.hAllocation->CanBecomeLost() &&
11045 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11046 {
11047 ++pAllocationRequest->itemsToMakeLostCount;
11048 pAllocationRequest->sumItemSize += suballoc.size;
11049 }
11050 else
11051 {
11052 return false;
11053 }
11054 }
11055 }
11056 else
11057 {
11058 // Already on next page.
11059 break;
11060 }
11061 ++index1st;
11062 }
11063 }
11064
11065 // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11066 if(index1st == suballocations1st.size() &&
11067 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11068 {
11069 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11070 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11071 }
11072 }
11073
11074 // There is enough free space at the end after alignment.
11075 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11076 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11077 {
11078 // Check next suballocations for BufferImageGranularity conflicts.
11079 // If conflict exists, allocation cannot be made here.
11080 if(bufferImageGranularity > 1)
11081 {
11082 for(size_t nextSuballocIndex = index1st;
11083 nextSuballocIndex < suballocations1st.size();
11084 nextSuballocIndex++)
11085 {
11086 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11087 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11088 {
11089 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11090 {
11091 return false;
11092 }
11093 }
11094 else
11095 {
11096 // Already on next page.
11097 break;
11098 }
11099 }
11100 }
11101
11102 // All tests passed: Success.
11103 pAllocationRequest->offset = resultOffset;
11104 pAllocationRequest->sumFreeSize =
11105 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11106 - resultBaseOffset
11107 - pAllocationRequest->sumItemSize;
11108 pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11109 // pAllocationRequest->item, customData unused.
11110 return true;
11111 }
11112 }
11113
11114 return false;
11115 }
11116
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11117 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11118 uint32_t currentFrameIndex,
11119 uint32_t frameInUseCount,
11120 VmaAllocationRequest* pAllocationRequest)
11121 {
11122 if(pAllocationRequest->itemsToMakeLostCount == 0)
11123 {
11124 return true;
11125 }
11126
11127 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11128
11129 // We always start from 1st.
11130 SuballocationVectorType* suballocations = &AccessSuballocations1st();
11131 size_t index = m_1stNullItemsBeginCount;
11132 size_t madeLostCount = 0;
11133 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11134 {
11135 if(index == suballocations->size())
11136 {
11137 index = 0;
11138 // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11139 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11140 {
11141 suballocations = &AccessSuballocations2nd();
11142 }
11143 // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11144 // suballocations continues pointing at AccessSuballocations1st().
11145 VMA_ASSERT(!suballocations->empty());
11146 }
11147 VmaSuballocation& suballoc = (*suballocations)[index];
11148 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11149 {
11150 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11151 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11152 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11153 {
11154 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11155 suballoc.hAllocation = VK_NULL_HANDLE;
11156 m_SumFreeSize += suballoc.size;
11157 if(suballocations == &AccessSuballocations1st())
11158 {
11159 ++m_1stNullItemsMiddleCount;
11160 }
11161 else
11162 {
11163 ++m_2ndNullItemsCount;
11164 }
11165 ++madeLostCount;
11166 }
11167 else
11168 {
11169 return false;
11170 }
11171 }
11172 ++index;
11173 }
11174
11175 CleanupAfterFree();
11176 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11177
11178 return true;
11179 }
11180
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11181 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11182 {
11183 uint32_t lostAllocationCount = 0;
11184
11185 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11186 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11187 {
11188 VmaSuballocation& suballoc = suballocations1st[i];
11189 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11190 suballoc.hAllocation->CanBecomeLost() &&
11191 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11192 {
11193 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11194 suballoc.hAllocation = VK_NULL_HANDLE;
11195 ++m_1stNullItemsMiddleCount;
11196 m_SumFreeSize += suballoc.size;
11197 ++lostAllocationCount;
11198 }
11199 }
11200
11201 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11202 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11203 {
11204 VmaSuballocation& suballoc = suballocations2nd[i];
11205 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11206 suballoc.hAllocation->CanBecomeLost() &&
11207 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11208 {
11209 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11210 suballoc.hAllocation = VK_NULL_HANDLE;
11211 ++m_2ndNullItemsCount;
11212 m_SumFreeSize += suballoc.size;
11213 ++lostAllocationCount;
11214 }
11215 }
11216
11217 if(lostAllocationCount)
11218 {
11219 CleanupAfterFree();
11220 }
11221
11222 return lostAllocationCount;
11223 }
11224
CheckCorruption(const void * pBlockData)11225 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11226 {
11227 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11228 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11229 {
11230 const VmaSuballocation& suballoc = suballocations1st[i];
11231 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11232 {
11233 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11234 {
11235 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11236 return VK_ERROR_VALIDATION_FAILED_EXT;
11237 }
11238 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11239 {
11240 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11241 return VK_ERROR_VALIDATION_FAILED_EXT;
11242 }
11243 }
11244 }
11245
11246 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11247 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11248 {
11249 const VmaSuballocation& suballoc = suballocations2nd[i];
11250 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11251 {
11252 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11253 {
11254 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11255 return VK_ERROR_VALIDATION_FAILED_EXT;
11256 }
11257 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11258 {
11259 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11260 return VK_ERROR_VALIDATION_FAILED_EXT;
11261 }
11262 }
11263 }
11264
11265 return VK_SUCCESS;
11266 }
11267
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11268 void VmaBlockMetadata_Linear::Alloc(
11269 const VmaAllocationRequest& request,
11270 VmaSuballocationType type,
11271 VkDeviceSize allocSize,
11272 VmaAllocation hAllocation)
11273 {
11274 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11275
11276 switch(request.type)
11277 {
11278 case VmaAllocationRequestType::UpperAddress:
11279 {
11280 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11281 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11282 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11283 suballocations2nd.push_back(newSuballoc);
11284 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11285 }
11286 break;
11287 case VmaAllocationRequestType::EndOf1st:
11288 {
11289 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11290
11291 VMA_ASSERT(suballocations1st.empty() ||
11292 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11293 // Check if it fits before the end of the block.
11294 VMA_ASSERT(request.offset + allocSize <= GetSize());
11295
11296 suballocations1st.push_back(newSuballoc);
11297 }
11298 break;
11299 case VmaAllocationRequestType::EndOf2nd:
11300 {
11301 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11302 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11303 VMA_ASSERT(!suballocations1st.empty() &&
11304 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11305 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11306
11307 switch(m_2ndVectorMode)
11308 {
11309 case SECOND_VECTOR_EMPTY:
11310 // First allocation from second part ring buffer.
11311 VMA_ASSERT(suballocations2nd.empty());
11312 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11313 break;
11314 case SECOND_VECTOR_RING_BUFFER:
11315 // 2-part ring buffer is already started.
11316 VMA_ASSERT(!suballocations2nd.empty());
11317 break;
11318 case SECOND_VECTOR_DOUBLE_STACK:
11319 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11320 break;
11321 default:
11322 VMA_ASSERT(0);
11323 }
11324
11325 suballocations2nd.push_back(newSuballoc);
11326 }
11327 break;
11328 default:
11329 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11330 }
11331
11332 m_SumFreeSize -= newSuballoc.size;
11333 }
11334
Free(const VmaAllocation allocation)11335 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11336 {
11337 FreeAtOffset(allocation->GetOffset());
11338 }
11339
FreeAtOffset(VkDeviceSize offset)11340 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11341 {
11342 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11343 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11344
11345 if(!suballocations1st.empty())
11346 {
11347 // First allocation: Mark it as next empty at the beginning.
11348 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11349 if(firstSuballoc.offset == offset)
11350 {
11351 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11352 firstSuballoc.hAllocation = VK_NULL_HANDLE;
11353 m_SumFreeSize += firstSuballoc.size;
11354 ++m_1stNullItemsBeginCount;
11355 CleanupAfterFree();
11356 return;
11357 }
11358 }
11359
11360 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11361 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11362 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11363 {
11364 VmaSuballocation& lastSuballoc = suballocations2nd.back();
11365 if(lastSuballoc.offset == offset)
11366 {
11367 m_SumFreeSize += lastSuballoc.size;
11368 suballocations2nd.pop_back();
11369 CleanupAfterFree();
11370 return;
11371 }
11372 }
11373 // Last allocation in 1st vector.
11374 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11375 {
11376 VmaSuballocation& lastSuballoc = suballocations1st.back();
11377 if(lastSuballoc.offset == offset)
11378 {
11379 m_SumFreeSize += lastSuballoc.size;
11380 suballocations1st.pop_back();
11381 CleanupAfterFree();
11382 return;
11383 }
11384 }
11385
11386 // Item from the middle of 1st vector.
11387 {
11388 VmaSuballocation refSuballoc;
11389 refSuballoc.offset = offset;
11390 // Rest of members stays uninitialized intentionally for better performance.
11391 SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11392 suballocations1st.begin() + m_1stNullItemsBeginCount,
11393 suballocations1st.end(),
11394 refSuballoc,
11395 VmaSuballocationOffsetLess());
11396 if(it != suballocations1st.end())
11397 {
11398 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11399 it->hAllocation = VK_NULL_HANDLE;
11400 ++m_1stNullItemsMiddleCount;
11401 m_SumFreeSize += it->size;
11402 CleanupAfterFree();
11403 return;
11404 }
11405 }
11406
11407 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11408 {
11409 // Item from the middle of 2nd vector.
11410 VmaSuballocation refSuballoc;
11411 refSuballoc.offset = offset;
11412 // Rest of members stays uninitialized intentionally for better performance.
11413 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11414 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11415 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11416 if(it != suballocations2nd.end())
11417 {
11418 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11419 it->hAllocation = VK_NULL_HANDLE;
11420 ++m_2ndNullItemsCount;
11421 m_SumFreeSize += it->size;
11422 CleanupAfterFree();
11423 return;
11424 }
11425 }
11426
11427 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11428 }
11429
ShouldCompact1st()11430 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11431 {
11432 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11433 const size_t suballocCount = AccessSuballocations1st().size();
11434 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11435 }
11436
CleanupAfterFree()11437 void VmaBlockMetadata_Linear::CleanupAfterFree()
11438 {
11439 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11440 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11441
11442 if(IsEmpty())
11443 {
11444 suballocations1st.clear();
11445 suballocations2nd.clear();
11446 m_1stNullItemsBeginCount = 0;
11447 m_1stNullItemsMiddleCount = 0;
11448 m_2ndNullItemsCount = 0;
11449 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11450 }
11451 else
11452 {
11453 const size_t suballoc1stCount = suballocations1st.size();
11454 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11455 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11456
11457 // Find more null items at the beginning of 1st vector.
11458 while(m_1stNullItemsBeginCount < suballoc1stCount &&
11459 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11460 {
11461 ++m_1stNullItemsBeginCount;
11462 --m_1stNullItemsMiddleCount;
11463 }
11464
11465 // Find more null items at the end of 1st vector.
11466 while(m_1stNullItemsMiddleCount > 0 &&
11467 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11468 {
11469 --m_1stNullItemsMiddleCount;
11470 suballocations1st.pop_back();
11471 }
11472
11473 // Find more null items at the end of 2nd vector.
11474 while(m_2ndNullItemsCount > 0 &&
11475 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11476 {
11477 --m_2ndNullItemsCount;
11478 suballocations2nd.pop_back();
11479 }
11480
11481 // Find more null items at the beginning of 2nd vector.
11482 while(m_2ndNullItemsCount > 0 &&
11483 suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11484 {
11485 --m_2ndNullItemsCount;
11486 VmaVectorRemove(suballocations2nd, 0);
11487 }
11488
11489 if(ShouldCompact1st())
11490 {
11491 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11492 size_t srcIndex = m_1stNullItemsBeginCount;
11493 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11494 {
11495 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11496 {
11497 ++srcIndex;
11498 }
11499 if(dstIndex != srcIndex)
11500 {
11501 suballocations1st[dstIndex] = suballocations1st[srcIndex];
11502 }
11503 ++srcIndex;
11504 }
11505 suballocations1st.resize(nonNullItemCount);
11506 m_1stNullItemsBeginCount = 0;
11507 m_1stNullItemsMiddleCount = 0;
11508 }
11509
11510 // 2nd vector became empty.
11511 if(suballocations2nd.empty())
11512 {
11513 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11514 }
11515
11516 // 1st vector became empty.
11517 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11518 {
11519 suballocations1st.clear();
11520 m_1stNullItemsBeginCount = 0;
11521
11522 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11523 {
11524 // Swap 1st with 2nd. Now 2nd is empty.
11525 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11526 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11527 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11528 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11529 {
11530 ++m_1stNullItemsBeginCount;
11531 --m_1stNullItemsMiddleCount;
11532 }
11533 m_2ndNullItemsCount = 0;
11534 m_1stVectorIndex ^= 1;
11535 }
11536 }
11537 }
11538
11539 VMA_HEAVY_ASSERT(Validate());
11540 }
11541
11542
11543 ////////////////////////////////////////////////////////////////////////////////
11544 // class VmaBlockMetadata_Buddy
11545
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)11546 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11547 VmaBlockMetadata(hAllocator),
11548 m_Root(VMA_NULL),
11549 m_AllocationCount(0),
11550 m_FreeCount(1),
11551 m_SumFreeSize(0)
11552 {
11553 memset(m_FreeList, 0, sizeof(m_FreeList));
11554 }
11555
~VmaBlockMetadata_Buddy()11556 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11557 {
11558 DeleteNode(m_Root);
11559 }
11560
Init(VkDeviceSize size)11561 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11562 {
11563 VmaBlockMetadata::Init(size);
11564
11565 m_UsableSize = VmaPrevPow2(size);
11566 m_SumFreeSize = m_UsableSize;
11567
11568 // Calculate m_LevelCount.
11569 m_LevelCount = 1;
11570 while(m_LevelCount < MAX_LEVELS &&
11571 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11572 {
11573 ++m_LevelCount;
11574 }
11575
11576 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11577 rootNode->offset = 0;
11578 rootNode->type = Node::TYPE_FREE;
11579 rootNode->parent = VMA_NULL;
11580 rootNode->buddy = VMA_NULL;
11581
11582 m_Root = rootNode;
11583 AddToFreeListFront(0, rootNode);
11584 }
11585
Validate()11586 bool VmaBlockMetadata_Buddy::Validate() const
11587 {
11588 // Validate tree.
11589 ValidationContext ctx;
11590 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11591 {
11592 VMA_VALIDATE(false && "ValidateNode failed.");
11593 }
11594 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11595 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11596
11597 // Validate free node lists.
11598 for(uint32_t level = 0; level < m_LevelCount; ++level)
11599 {
11600 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11601 m_FreeList[level].front->free.prev == VMA_NULL);
11602
11603 for(Node* node = m_FreeList[level].front;
11604 node != VMA_NULL;
11605 node = node->free.next)
11606 {
11607 VMA_VALIDATE(node->type == Node::TYPE_FREE);
11608
11609 if(node->free.next == VMA_NULL)
11610 {
11611 VMA_VALIDATE(m_FreeList[level].back == node);
11612 }
11613 else
11614 {
11615 VMA_VALIDATE(node->free.next->free.prev == node);
11616 }
11617 }
11618 }
11619
11620 // Validate that free lists ar higher levels are empty.
11621 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11622 {
11623 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11624 }
11625
11626 return true;
11627 }
11628
GetUnusedRangeSizeMax()11629 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11630 {
11631 for(uint32_t level = 0; level < m_LevelCount; ++level)
11632 {
11633 if(m_FreeList[level].front != VMA_NULL)
11634 {
11635 return LevelToNodeSize(level);
11636 }
11637 }
11638 return 0;
11639 }
11640
CalcAllocationStatInfo(VmaStatInfo & outInfo)11641 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11642 {
11643 const VkDeviceSize unusableSize = GetUnusableSize();
11644
11645 outInfo.blockCount = 1;
11646
11647 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11648 outInfo.usedBytes = outInfo.unusedBytes = 0;
11649
11650 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11651 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11652 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11653
11654 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11655
11656 if(unusableSize > 0)
11657 {
11658 ++outInfo.unusedRangeCount;
11659 outInfo.unusedBytes += unusableSize;
11660 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11661 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11662 }
11663 }
11664
AddPoolStats(VmaPoolStats & inoutStats)11665 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11666 {
11667 const VkDeviceSize unusableSize = GetUnusableSize();
11668
11669 inoutStats.size += GetSize();
11670 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11671 inoutStats.allocationCount += m_AllocationCount;
11672 inoutStats.unusedRangeCount += m_FreeCount;
11673 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11674
11675 if(unusableSize > 0)
11676 {
11677 ++inoutStats.unusedRangeCount;
11678 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11679 }
11680 }
11681
11682 #if VMA_STATS_STRING_ENABLED
11683
PrintDetailedMap(class VmaJsonWriter & json)11684 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11685 {
11686 // TODO optimize
11687 VmaStatInfo stat;
11688 CalcAllocationStatInfo(stat);
11689
11690 PrintDetailedMap_Begin(
11691 json,
11692 stat.unusedBytes,
11693 stat.allocationCount,
11694 stat.unusedRangeCount);
11695
11696 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11697
11698 const VkDeviceSize unusableSize = GetUnusableSize();
11699 if(unusableSize > 0)
11700 {
11701 PrintDetailedMap_UnusedRange(json,
11702 m_UsableSize, // offset
11703 unusableSize); // size
11704 }
11705
11706 PrintDetailedMap_End(json);
11707 }
11708
11709 #endif // #if VMA_STATS_STRING_ENABLED
11710
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)11711 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11712 uint32_t currentFrameIndex,
11713 uint32_t frameInUseCount,
11714 VkDeviceSize bufferImageGranularity,
11715 VkDeviceSize allocSize,
11716 VkDeviceSize allocAlignment,
11717 bool upperAddress,
11718 VmaSuballocationType allocType,
11719 bool canMakeOtherLost,
11720 uint32_t strategy,
11721 VmaAllocationRequest* pAllocationRequest)
11722 {
11723 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11724
11725 // Simple way to respect bufferImageGranularity. May be optimized some day.
11726 // Whenever it might be an OPTIMAL image...
11727 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11728 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11729 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11730 {
11731 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11732 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11733 }
11734
11735 if(allocSize > m_UsableSize)
11736 {
11737 return false;
11738 }
11739
11740 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11741 for(uint32_t level = targetLevel + 1; level--; )
11742 {
11743 for(Node* freeNode = m_FreeList[level].front;
11744 freeNode != VMA_NULL;
11745 freeNode = freeNode->free.next)
11746 {
11747 if(freeNode->offset % allocAlignment == 0)
11748 {
11749 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11750 pAllocationRequest->offset = freeNode->offset;
11751 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11752 pAllocationRequest->sumItemSize = 0;
11753 pAllocationRequest->itemsToMakeLostCount = 0;
11754 pAllocationRequest->customData = (void*)(uintptr_t)level;
11755 return true;
11756 }
11757 }
11758 }
11759
11760 return false;
11761 }
11762
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11763 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11764 uint32_t currentFrameIndex,
11765 uint32_t frameInUseCount,
11766 VmaAllocationRequest* pAllocationRequest)
11767 {
11768 /*
11769 Lost allocations are not supported in buddy allocator at the moment.
11770 Support might be added in the future.
11771 */
11772 return pAllocationRequest->itemsToMakeLostCount == 0;
11773 }
11774
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11775 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11776 {
11777 /*
11778 Lost allocations are not supported in buddy allocator at the moment.
11779 Support might be added in the future.
11780 */
11781 return 0;
11782 }
11783
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11784 void VmaBlockMetadata_Buddy::Alloc(
11785 const VmaAllocationRequest& request,
11786 VmaSuballocationType type,
11787 VkDeviceSize allocSize,
11788 VmaAllocation hAllocation)
11789 {
11790 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11791
11792 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11793 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11794
11795 Node* currNode = m_FreeList[currLevel].front;
11796 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11797 while(currNode->offset != request.offset)
11798 {
11799 currNode = currNode->free.next;
11800 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11801 }
11802
11803 // Go down, splitting free nodes.
11804 while(currLevel < targetLevel)
11805 {
11806 // currNode is already first free node at currLevel.
11807 // Remove it from list of free nodes at this currLevel.
11808 RemoveFromFreeList(currLevel, currNode);
11809
11810 const uint32_t childrenLevel = currLevel + 1;
11811
11812 // Create two free sub-nodes.
11813 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11814 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11815
11816 leftChild->offset = currNode->offset;
11817 leftChild->type = Node::TYPE_FREE;
11818 leftChild->parent = currNode;
11819 leftChild->buddy = rightChild;
11820
11821 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11822 rightChild->type = Node::TYPE_FREE;
11823 rightChild->parent = currNode;
11824 rightChild->buddy = leftChild;
11825
11826 // Convert current currNode to split type.
11827 currNode->type = Node::TYPE_SPLIT;
11828 currNode->split.leftChild = leftChild;
11829
11830 // Add child nodes to free list. Order is important!
11831 AddToFreeListFront(childrenLevel, rightChild);
11832 AddToFreeListFront(childrenLevel, leftChild);
11833
11834 ++m_FreeCount;
11835 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11836 ++currLevel;
11837 currNode = m_FreeList[currLevel].front;
11838
11839 /*
11840 We can be sure that currNode, as left child of node previously split,
11841 also fullfills the alignment requirement.
11842 */
11843 }
11844
11845 // Remove from free list.
11846 VMA_ASSERT(currLevel == targetLevel &&
11847 currNode != VMA_NULL &&
11848 currNode->type == Node::TYPE_FREE);
11849 RemoveFromFreeList(currLevel, currNode);
11850
11851 // Convert to allocation node.
11852 currNode->type = Node::TYPE_ALLOCATION;
11853 currNode->allocation.alloc = hAllocation;
11854
11855 ++m_AllocationCount;
11856 --m_FreeCount;
11857 m_SumFreeSize -= allocSize;
11858 }
11859
DeleteNode(Node * node)11860 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11861 {
11862 if(node->type == Node::TYPE_SPLIT)
11863 {
11864 DeleteNode(node->split.leftChild->buddy);
11865 DeleteNode(node->split.leftChild);
11866 }
11867
11868 vma_delete(GetAllocationCallbacks(), node);
11869 }
11870
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11871 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11872 {
11873 VMA_VALIDATE(level < m_LevelCount);
11874 VMA_VALIDATE(curr->parent == parent);
11875 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11876 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11877 switch(curr->type)
11878 {
11879 case Node::TYPE_FREE:
11880 // curr->free.prev, next are validated separately.
11881 ctx.calculatedSumFreeSize += levelNodeSize;
11882 ++ctx.calculatedFreeCount;
11883 break;
11884 case Node::TYPE_ALLOCATION:
11885 ++ctx.calculatedAllocationCount;
11886 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11887 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11888 break;
11889 case Node::TYPE_SPLIT:
11890 {
11891 const uint32_t childrenLevel = level + 1;
11892 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11893 const Node* const leftChild = curr->split.leftChild;
11894 VMA_VALIDATE(leftChild != VMA_NULL);
11895 VMA_VALIDATE(leftChild->offset == curr->offset);
11896 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11897 {
11898 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11899 }
11900 const Node* const rightChild = leftChild->buddy;
11901 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
11902 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
11903 {
11904 VMA_VALIDATE(false && "ValidateNode for right child failed.");
11905 }
11906 }
11907 break;
11908 default:
11909 return false;
11910 }
11911
11912 return true;
11913 }
11914
AllocSizeToLevel(VkDeviceSize allocSize)11915 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
11916 {
11917 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
11918 uint32_t level = 0;
11919 VkDeviceSize currLevelNodeSize = m_UsableSize;
11920 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
11921 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
11922 {
11923 ++level;
11924 currLevelNodeSize = nextLevelNodeSize;
11925 nextLevelNodeSize = currLevelNodeSize >> 1;
11926 }
11927 return level;
11928 }
11929
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)11930 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
11931 {
11932 // Find node and level.
11933 Node* node = m_Root;
11934 VkDeviceSize nodeOffset = 0;
11935 uint32_t level = 0;
11936 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
11937 while(node->type == Node::TYPE_SPLIT)
11938 {
11939 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
11940 if(offset < nodeOffset + nextLevelSize)
11941 {
11942 node = node->split.leftChild;
11943 }
11944 else
11945 {
11946 node = node->split.leftChild->buddy;
11947 nodeOffset += nextLevelSize;
11948 }
11949 ++level;
11950 levelNodeSize = nextLevelSize;
11951 }
11952
11953 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
11954 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
11955
11956 ++m_FreeCount;
11957 --m_AllocationCount;
11958 m_SumFreeSize += alloc->GetSize();
11959
11960 node->type = Node::TYPE_FREE;
11961
11962 // Join free nodes if possible.
11963 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
11964 {
11965 RemoveFromFreeList(level, node->buddy);
11966 Node* const parent = node->parent;
11967
11968 vma_delete(GetAllocationCallbacks(), node->buddy);
11969 vma_delete(GetAllocationCallbacks(), node);
11970 parent->type = Node::TYPE_FREE;
11971
11972 node = parent;
11973 --level;
11974 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
11975 --m_FreeCount;
11976 }
11977
11978 AddToFreeListFront(level, node);
11979 }
11980
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)11981 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
11982 {
11983 switch(node->type)
11984 {
11985 case Node::TYPE_FREE:
11986 ++outInfo.unusedRangeCount;
11987 outInfo.unusedBytes += levelNodeSize;
11988 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
11989 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
11990 break;
11991 case Node::TYPE_ALLOCATION:
11992 {
11993 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
11994 ++outInfo.allocationCount;
11995 outInfo.usedBytes += allocSize;
11996 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
11997 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
11998
11999 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12000 if(unusedRangeSize > 0)
12001 {
12002 ++outInfo.unusedRangeCount;
12003 outInfo.unusedBytes += unusedRangeSize;
12004 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12005 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12006 }
12007 }
12008 break;
12009 case Node::TYPE_SPLIT:
12010 {
12011 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12012 const Node* const leftChild = node->split.leftChild;
12013 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12014 const Node* const rightChild = leftChild->buddy;
12015 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12016 }
12017 break;
12018 default:
12019 VMA_ASSERT(0);
12020 }
12021 }
12022
AddToFreeListFront(uint32_t level,Node * node)12023 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12024 {
12025 VMA_ASSERT(node->type == Node::TYPE_FREE);
12026
12027 // List is empty.
12028 Node* const frontNode = m_FreeList[level].front;
12029 if(frontNode == VMA_NULL)
12030 {
12031 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12032 node->free.prev = node->free.next = VMA_NULL;
12033 m_FreeList[level].front = m_FreeList[level].back = node;
12034 }
12035 else
12036 {
12037 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12038 node->free.prev = VMA_NULL;
12039 node->free.next = frontNode;
12040 frontNode->free.prev = node;
12041 m_FreeList[level].front = node;
12042 }
12043 }
12044
RemoveFromFreeList(uint32_t level,Node * node)12045 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12046 {
12047 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12048
12049 // It is at the front.
12050 if(node->free.prev == VMA_NULL)
12051 {
12052 VMA_ASSERT(m_FreeList[level].front == node);
12053 m_FreeList[level].front = node->free.next;
12054 }
12055 else
12056 {
12057 Node* const prevFreeNode = node->free.prev;
12058 VMA_ASSERT(prevFreeNode->free.next == node);
12059 prevFreeNode->free.next = node->free.next;
12060 }
12061
12062 // It is at the back.
12063 if(node->free.next == VMA_NULL)
12064 {
12065 VMA_ASSERT(m_FreeList[level].back == node);
12066 m_FreeList[level].back = node->free.prev;
12067 }
12068 else
12069 {
12070 Node* const nextFreeNode = node->free.next;
12071 VMA_ASSERT(nextFreeNode->free.prev == node);
12072 nextFreeNode->free.prev = node->free.prev;
12073 }
12074 }
12075
12076 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)12077 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12078 {
12079 switch(node->type)
12080 {
12081 case Node::TYPE_FREE:
12082 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12083 break;
12084 case Node::TYPE_ALLOCATION:
12085 {
12086 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12087 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12088 if(allocSize < levelNodeSize)
12089 {
12090 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12091 }
12092 }
12093 break;
12094 case Node::TYPE_SPLIT:
12095 {
12096 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12097 const Node* const leftChild = node->split.leftChild;
12098 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12099 const Node* const rightChild = leftChild->buddy;
12100 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12101 }
12102 break;
12103 default:
12104 VMA_ASSERT(0);
12105 }
12106 }
12107 #endif // #if VMA_STATS_STRING_ENABLED
12108
12109
12110 ////////////////////////////////////////////////////////////////////////////////
12111 // class VmaDeviceMemoryBlock
12112
VmaDeviceMemoryBlock(VmaAllocator hAllocator)12113 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12114 m_pMetadata(VMA_NULL),
12115 m_MemoryTypeIndex(UINT32_MAX),
12116 m_Id(0),
12117 m_hMemory(VK_NULL_HANDLE),
12118 m_MapCount(0),
12119 m_pMappedData(VMA_NULL)
12120 {
12121 }
12122
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)12123 void VmaDeviceMemoryBlock::Init(
12124 VmaAllocator hAllocator,
12125 VmaPool hParentPool,
12126 uint32_t newMemoryTypeIndex,
12127 VkDeviceMemory newMemory,
12128 VkDeviceSize newSize,
12129 uint32_t id,
12130 uint32_t algorithm)
12131 {
12132 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12133
12134 m_hParentPool = hParentPool;
12135 m_MemoryTypeIndex = newMemoryTypeIndex;
12136 m_Id = id;
12137 m_hMemory = newMemory;
12138
12139 switch(algorithm)
12140 {
12141 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12142 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12143 break;
12144 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12145 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12146 break;
12147 default:
12148 VMA_ASSERT(0);
12149 // Fall-through.
12150 case 0:
12151 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12152 }
12153 m_pMetadata->Init(newSize);
12154 }
12155
Destroy(VmaAllocator allocator)12156 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12157 {
12158 // This is the most important assert in the entire library.
12159 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12160 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12161
12162 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12163 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12164 m_hMemory = VK_NULL_HANDLE;
12165
12166 vma_delete(allocator, m_pMetadata);
12167 m_pMetadata = VMA_NULL;
12168 }
12169
Validate()12170 bool VmaDeviceMemoryBlock::Validate() const
12171 {
12172 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12173 (m_pMetadata->GetSize() != 0));
12174
12175 return m_pMetadata->Validate();
12176 }
12177
CheckCorruption(VmaAllocator hAllocator)12178 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12179 {
12180 void* pData = nullptr;
12181 VkResult res = Map(hAllocator, 1, &pData);
12182 if(res != VK_SUCCESS)
12183 {
12184 return res;
12185 }
12186
12187 res = m_pMetadata->CheckCorruption(pData);
12188
12189 Unmap(hAllocator, 1);
12190
12191 return res;
12192 }
12193
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)12194 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12195 {
12196 if(count == 0)
12197 {
12198 return VK_SUCCESS;
12199 }
12200
12201 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12202 if(m_MapCount != 0)
12203 {
12204 m_MapCount += count;
12205 VMA_ASSERT(m_pMappedData != VMA_NULL);
12206 if(ppData != VMA_NULL)
12207 {
12208 *ppData = m_pMappedData;
12209 }
12210 return VK_SUCCESS;
12211 }
12212 else
12213 {
12214 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12215 hAllocator->m_hDevice,
12216 m_hMemory,
12217 0, // offset
12218 VK_WHOLE_SIZE,
12219 0, // flags
12220 &m_pMappedData);
12221 if(result == VK_SUCCESS)
12222 {
12223 if(ppData != VMA_NULL)
12224 {
12225 *ppData = m_pMappedData;
12226 }
12227 m_MapCount = count;
12228 }
12229 return result;
12230 }
12231 }
12232
Unmap(VmaAllocator hAllocator,uint32_t count)12233 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12234 {
12235 if(count == 0)
12236 {
12237 return;
12238 }
12239
12240 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12241 if(m_MapCount >= count)
12242 {
12243 m_MapCount -= count;
12244 if(m_MapCount == 0)
12245 {
12246 m_pMappedData = VMA_NULL;
12247 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12248 }
12249 }
12250 else
12251 {
12252 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12253 }
12254 }
12255
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12256 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12257 {
12258 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12259 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12260
12261 void* pData;
12262 VkResult res = Map(hAllocator, 1, &pData);
12263 if(res != VK_SUCCESS)
12264 {
12265 return res;
12266 }
12267
12268 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12269 VmaWriteMagicValue(pData, allocOffset + allocSize);
12270
12271 Unmap(hAllocator, 1);
12272
12273 return VK_SUCCESS;
12274 }
12275
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12276 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12277 {
12278 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12279 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12280
12281 void* pData;
12282 VkResult res = Map(hAllocator, 1, &pData);
12283 if(res != VK_SUCCESS)
12284 {
12285 return res;
12286 }
12287
12288 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12289 {
12290 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12291 }
12292 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12293 {
12294 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12295 }
12296
12297 Unmap(hAllocator, 1);
12298
12299 return VK_SUCCESS;
12300 }
12301
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)12302 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12303 const VmaAllocator hAllocator,
12304 const VmaAllocation hAllocation,
12305 VkDeviceSize allocationLocalOffset,
12306 VkBuffer hBuffer,
12307 const void* pNext)
12308 {
12309 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12310 hAllocation->GetBlock() == this);
12311 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12312 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12313 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12314 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12315 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12316 return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12317 }
12318
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)12319 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12320 const VmaAllocator hAllocator,
12321 const VmaAllocation hAllocation,
12322 VkDeviceSize allocationLocalOffset,
12323 VkImage hImage,
12324 const void* pNext)
12325 {
12326 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12327 hAllocation->GetBlock() == this);
12328 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12329 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12330 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12331 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12332 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12333 return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12334 }
12335
InitStatInfo(VmaStatInfo & outInfo)12336 static void InitStatInfo(VmaStatInfo& outInfo)
12337 {
12338 memset(&outInfo, 0, sizeof(outInfo));
12339 outInfo.allocationSizeMin = UINT64_MAX;
12340 outInfo.unusedRangeSizeMin = UINT64_MAX;
12341 }
12342
12343 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)12344 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12345 {
12346 inoutInfo.blockCount += srcInfo.blockCount;
12347 inoutInfo.allocationCount += srcInfo.allocationCount;
12348 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12349 inoutInfo.usedBytes += srcInfo.usedBytes;
12350 inoutInfo.unusedBytes += srcInfo.unusedBytes;
12351 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12352 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12353 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12354 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12355 }
12356
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)12357 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12358 {
12359 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12360 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12361 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12362 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12363 }
12364
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)12365 VmaPool_T::VmaPool_T(
12366 VmaAllocator hAllocator,
12367 const VmaPoolCreateInfo& createInfo,
12368 VkDeviceSize preferredBlockSize) :
12369 m_BlockVector(
12370 hAllocator,
12371 this, // hParentPool
12372 createInfo.memoryTypeIndex,
12373 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12374 createInfo.minBlockCount,
12375 createInfo.maxBlockCount,
12376 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12377 createInfo.frameInUseCount,
12378 createInfo.blockSize != 0, // explicitBlockSize
12379 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12380 m_Id(0),
12381 m_Name(VMA_NULL)
12382 {
12383 }
12384
~VmaPool_T()12385 VmaPool_T::~VmaPool_T()
12386 {
12387 }
12388
SetName(const char * pName)12389 void VmaPool_T::SetName(const char* pName)
12390 {
12391 const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12392 VmaFreeString(allocs, m_Name);
12393
12394 if(pName != VMA_NULL)
12395 {
12396 m_Name = VmaCreateStringCopy(allocs, pName);
12397 }
12398 else
12399 {
12400 m_Name = VMA_NULL;
12401 }
12402 }
12403
12404 #if VMA_STATS_STRING_ENABLED
12405
12406 #endif // #if VMA_STATS_STRING_ENABLED
12407
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool explicitBlockSize,uint32_t algorithm)12408 VmaBlockVector::VmaBlockVector(
12409 VmaAllocator hAllocator,
12410 VmaPool hParentPool,
12411 uint32_t memoryTypeIndex,
12412 VkDeviceSize preferredBlockSize,
12413 size_t minBlockCount,
12414 size_t maxBlockCount,
12415 VkDeviceSize bufferImageGranularity,
12416 uint32_t frameInUseCount,
12417 bool explicitBlockSize,
12418 uint32_t algorithm) :
12419 m_hAllocator(hAllocator),
12420 m_hParentPool(hParentPool),
12421 m_MemoryTypeIndex(memoryTypeIndex),
12422 m_PreferredBlockSize(preferredBlockSize),
12423 m_MinBlockCount(minBlockCount),
12424 m_MaxBlockCount(maxBlockCount),
12425 m_BufferImageGranularity(bufferImageGranularity),
12426 m_FrameInUseCount(frameInUseCount),
12427 m_ExplicitBlockSize(explicitBlockSize),
12428 m_Algorithm(algorithm),
12429 m_HasEmptyBlock(false),
12430 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12431 m_NextBlockId(0)
12432 {
12433 }
12434
~VmaBlockVector()12435 VmaBlockVector::~VmaBlockVector()
12436 {
12437 for(size_t i = m_Blocks.size(); i--; )
12438 {
12439 m_Blocks[i]->Destroy(m_hAllocator);
12440 vma_delete(m_hAllocator, m_Blocks[i]);
12441 }
12442 }
12443
CreateMinBlocks()12444 VkResult VmaBlockVector::CreateMinBlocks()
12445 {
12446 for(size_t i = 0; i < m_MinBlockCount; ++i)
12447 {
12448 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12449 if(res != VK_SUCCESS)
12450 {
12451 return res;
12452 }
12453 }
12454 return VK_SUCCESS;
12455 }
12456
GetPoolStats(VmaPoolStats * pStats)12457 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12458 {
12459 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12460
12461 const size_t blockCount = m_Blocks.size();
12462
12463 pStats->size = 0;
12464 pStats->unusedSize = 0;
12465 pStats->allocationCount = 0;
12466 pStats->unusedRangeCount = 0;
12467 pStats->unusedRangeSizeMax = 0;
12468 pStats->blockCount = blockCount;
12469
12470 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12471 {
12472 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12473 VMA_ASSERT(pBlock);
12474 VMA_HEAVY_ASSERT(pBlock->Validate());
12475 pBlock->m_pMetadata->AddPoolStats(*pStats);
12476 }
12477 }
12478
IsEmpty()12479 bool VmaBlockVector::IsEmpty()
12480 {
12481 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12482 return m_Blocks.empty();
12483 }
12484
IsCorruptionDetectionEnabled()12485 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12486 {
12487 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12488 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12489 (VMA_DEBUG_MARGIN > 0) &&
12490 (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12491 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12492 }
12493
12494 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12495
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12496 VkResult VmaBlockVector::Allocate(
12497 uint32_t currentFrameIndex,
12498 VkDeviceSize size,
12499 VkDeviceSize alignment,
12500 const VmaAllocationCreateInfo& createInfo,
12501 VmaSuballocationType suballocType,
12502 size_t allocationCount,
12503 VmaAllocation* pAllocations)
12504 {
12505 size_t allocIndex;
12506 VkResult res = VK_SUCCESS;
12507
12508 if(IsCorruptionDetectionEnabled())
12509 {
12510 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12511 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12512 }
12513
12514 {
12515 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12516 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12517 {
12518 res = AllocatePage(
12519 currentFrameIndex,
12520 size,
12521 alignment,
12522 createInfo,
12523 suballocType,
12524 pAllocations + allocIndex);
12525 if(res != VK_SUCCESS)
12526 {
12527 break;
12528 }
12529 }
12530 }
12531
12532 if(res != VK_SUCCESS)
12533 {
12534 // Free all already created allocations.
12535 while(allocIndex--)
12536 {
12537 Free(pAllocations[allocIndex]);
12538 }
12539 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12540 }
12541
12542 return res;
12543 }
12544
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12545 VkResult VmaBlockVector::AllocatePage(
12546 uint32_t currentFrameIndex,
12547 VkDeviceSize size,
12548 VkDeviceSize alignment,
12549 const VmaAllocationCreateInfo& createInfo,
12550 VmaSuballocationType suballocType,
12551 VmaAllocation* pAllocation)
12552 {
12553 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12554 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12555 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12556 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12557
12558 VkDeviceSize freeMemory;
12559 {
12560 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12561 VmaBudget heapBudget = {};
12562 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12563 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12564 }
12565
12566 const bool canFallbackToDedicated = !IsCustomPool();
12567 const bool canCreateNewBlock =
12568 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12569 (m_Blocks.size() < m_MaxBlockCount) &&
12570 (freeMemory >= size || !canFallbackToDedicated);
12571 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12572
12573 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12574 // Which in turn is available only when maxBlockCount = 1.
12575 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12576 {
12577 canMakeOtherLost = false;
12578 }
12579
12580 // Upper address can only be used with linear allocator and within single memory block.
12581 if(isUpperAddress &&
12582 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12583 {
12584 return VK_ERROR_FEATURE_NOT_PRESENT;
12585 }
12586
12587 // Validate strategy.
12588 switch(strategy)
12589 {
12590 case 0:
12591 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12592 break;
12593 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12594 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12595 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12596 break;
12597 default:
12598 return VK_ERROR_FEATURE_NOT_PRESENT;
12599 }
12600
12601 // Early reject: requested allocation size is larger that maximum block size for this block vector.
12602 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12603 {
12604 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12605 }
12606
12607 /*
12608 Under certain condition, this whole section can be skipped for optimization, so
12609 we move on directly to trying to allocate with canMakeOtherLost. That's the case
12610 e.g. for custom pools with linear algorithm.
12611 */
12612 if(!canMakeOtherLost || canCreateNewBlock)
12613 {
12614 // 1. Search existing allocations. Try to allocate without making other allocations lost.
12615 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12616 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12617
12618 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12619 {
12620 // Use only last block.
12621 if(!m_Blocks.empty())
12622 {
12623 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12624 VMA_ASSERT(pCurrBlock);
12625 VkResult res = AllocateFromBlock(
12626 pCurrBlock,
12627 currentFrameIndex,
12628 size,
12629 alignment,
12630 allocFlagsCopy,
12631 createInfo.pUserData,
12632 suballocType,
12633 strategy,
12634 pAllocation);
12635 if(res == VK_SUCCESS)
12636 {
12637 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12638 return VK_SUCCESS;
12639 }
12640 }
12641 }
12642 else
12643 {
12644 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12645 {
12646 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12647 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12648 {
12649 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12650 VMA_ASSERT(pCurrBlock);
12651 VkResult res = AllocateFromBlock(
12652 pCurrBlock,
12653 currentFrameIndex,
12654 size,
12655 alignment,
12656 allocFlagsCopy,
12657 createInfo.pUserData,
12658 suballocType,
12659 strategy,
12660 pAllocation);
12661 if(res == VK_SUCCESS)
12662 {
12663 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12664 return VK_SUCCESS;
12665 }
12666 }
12667 }
12668 else // WORST_FIT, FIRST_FIT
12669 {
12670 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12671 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12672 {
12673 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12674 VMA_ASSERT(pCurrBlock);
12675 VkResult res = AllocateFromBlock(
12676 pCurrBlock,
12677 currentFrameIndex,
12678 size,
12679 alignment,
12680 allocFlagsCopy,
12681 createInfo.pUserData,
12682 suballocType,
12683 strategy,
12684 pAllocation);
12685 if(res == VK_SUCCESS)
12686 {
12687 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12688 return VK_SUCCESS;
12689 }
12690 }
12691 }
12692 }
12693
12694 // 2. Try to create new block.
12695 if(canCreateNewBlock)
12696 {
12697 // Calculate optimal size for new block.
12698 VkDeviceSize newBlockSize = m_PreferredBlockSize;
12699 uint32_t newBlockSizeShift = 0;
12700 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12701
12702 if(!m_ExplicitBlockSize)
12703 {
12704 // Allocate 1/8, 1/4, 1/2 as first blocks.
12705 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12706 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12707 {
12708 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12709 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12710 {
12711 newBlockSize = smallerNewBlockSize;
12712 ++newBlockSizeShift;
12713 }
12714 else
12715 {
12716 break;
12717 }
12718 }
12719 }
12720
12721 size_t newBlockIndex = 0;
12722 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12723 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12724 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12725 if(!m_ExplicitBlockSize)
12726 {
12727 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12728 {
12729 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12730 if(smallerNewBlockSize >= size)
12731 {
12732 newBlockSize = smallerNewBlockSize;
12733 ++newBlockSizeShift;
12734 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12735 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12736 }
12737 else
12738 {
12739 break;
12740 }
12741 }
12742 }
12743
12744 if(res == VK_SUCCESS)
12745 {
12746 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12747 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12748
12749 res = AllocateFromBlock(
12750 pBlock,
12751 currentFrameIndex,
12752 size,
12753 alignment,
12754 allocFlagsCopy,
12755 createInfo.pUserData,
12756 suballocType,
12757 strategy,
12758 pAllocation);
12759 if(res == VK_SUCCESS)
12760 {
12761 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12762 return VK_SUCCESS;
12763 }
12764 else
12765 {
12766 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12767 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12768 }
12769 }
12770 }
12771 }
12772
12773 // 3. Try to allocate from existing blocks with making other allocations lost.
12774 if(canMakeOtherLost)
12775 {
12776 uint32_t tryIndex = 0;
12777 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12778 {
12779 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12780 VmaAllocationRequest bestRequest = {};
12781 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12782
12783 // 1. Search existing allocations.
12784 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12785 {
12786 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12787 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12788 {
12789 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12790 VMA_ASSERT(pCurrBlock);
12791 VmaAllocationRequest currRequest = {};
12792 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12793 currentFrameIndex,
12794 m_FrameInUseCount,
12795 m_BufferImageGranularity,
12796 size,
12797 alignment,
12798 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12799 suballocType,
12800 canMakeOtherLost,
12801 strategy,
12802 &currRequest))
12803 {
12804 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12805 if(pBestRequestBlock == VMA_NULL ||
12806 currRequestCost < bestRequestCost)
12807 {
12808 pBestRequestBlock = pCurrBlock;
12809 bestRequest = currRequest;
12810 bestRequestCost = currRequestCost;
12811
12812 if(bestRequestCost == 0)
12813 {
12814 break;
12815 }
12816 }
12817 }
12818 }
12819 }
12820 else // WORST_FIT, FIRST_FIT
12821 {
12822 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12823 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12824 {
12825 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12826 VMA_ASSERT(pCurrBlock);
12827 VmaAllocationRequest currRequest = {};
12828 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12829 currentFrameIndex,
12830 m_FrameInUseCount,
12831 m_BufferImageGranularity,
12832 size,
12833 alignment,
12834 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12835 suballocType,
12836 canMakeOtherLost,
12837 strategy,
12838 &currRequest))
12839 {
12840 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12841 if(pBestRequestBlock == VMA_NULL ||
12842 currRequestCost < bestRequestCost ||
12843 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
12844 {
12845 pBestRequestBlock = pCurrBlock;
12846 bestRequest = currRequest;
12847 bestRequestCost = currRequestCost;
12848
12849 if(bestRequestCost == 0 ||
12850 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
12851 {
12852 break;
12853 }
12854 }
12855 }
12856 }
12857 }
12858
12859 if(pBestRequestBlock != VMA_NULL)
12860 {
12861 if(mapped)
12862 {
12863 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
12864 if(res != VK_SUCCESS)
12865 {
12866 return res;
12867 }
12868 }
12869
12870 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
12871 currentFrameIndex,
12872 m_FrameInUseCount,
12873 &bestRequest))
12874 {
12875 // Allocate from this pBlock.
12876 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
12877 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
12878 UpdateHasEmptyBlock();
12879 (*pAllocation)->InitBlockAllocation(
12880 pBestRequestBlock,
12881 bestRequest.offset,
12882 alignment,
12883 size,
12884 m_MemoryTypeIndex,
12885 suballocType,
12886 mapped,
12887 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
12888 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
12889 VMA_DEBUG_LOG(" Returned from existing block");
12890 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
12891 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
12892 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12893 {
12894 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12895 }
12896 if(IsCorruptionDetectionEnabled())
12897 {
12898 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
12899 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12900 }
12901 return VK_SUCCESS;
12902 }
12903 // else: Some allocations must have been touched while we are here. Next try.
12904 }
12905 else
12906 {
12907 // Could not find place in any of the blocks - break outer loop.
12908 break;
12909 }
12910 }
12911 /* Maximum number of tries exceeded - a very unlike event when many other
12912 threads are simultaneously touching allocations making it impossible to make
12913 lost at the same time as we try to allocate. */
12914 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
12915 {
12916 return VK_ERROR_TOO_MANY_OBJECTS;
12917 }
12918 }
12919
12920 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12921 }
12922
Free(const VmaAllocation hAllocation)12923 void VmaBlockVector::Free(
12924 const VmaAllocation hAllocation)
12925 {
12926 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12927
12928 bool budgetExceeded = false;
12929 {
12930 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12931 VmaBudget heapBudget = {};
12932 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12933 budgetExceeded = heapBudget.usage >= heapBudget.budget;
12934 }
12935
12936 // Scope for lock.
12937 {
12938 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12939
12940 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12941
12942 if(IsCorruptionDetectionEnabled())
12943 {
12944 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12945 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12946 }
12947
12948 if(hAllocation->IsPersistentMap())
12949 {
12950 pBlock->Unmap(m_hAllocator, 1);
12951 }
12952
12953 pBlock->m_pMetadata->Free(hAllocation);
12954 VMA_HEAVY_ASSERT(pBlock->Validate());
12955
12956 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12957
12958 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12959 // pBlock became empty after this deallocation.
12960 if(pBlock->m_pMetadata->IsEmpty())
12961 {
12962 // Already has empty block. We don't want to have two, so delete this one.
12963 if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
12964 {
12965 pBlockToDelete = pBlock;
12966 Remove(pBlock);
12967 }
12968 // else: We now have an empty block - leave it.
12969 }
12970 // pBlock didn't become empty, but we have another empty block - find and free that one.
12971 // (This is optional, heuristics.)
12972 else if(m_HasEmptyBlock && canDeleteBlock)
12973 {
12974 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12975 if(pLastBlock->m_pMetadata->IsEmpty())
12976 {
12977 pBlockToDelete = pLastBlock;
12978 m_Blocks.pop_back();
12979 }
12980 }
12981
12982 UpdateHasEmptyBlock();
12983 IncrementallySortBlocks();
12984 }
12985
12986 // Destruction of a free block. Deferred until this point, outside of mutex
12987 // lock, for performance reason.
12988 if(pBlockToDelete != VMA_NULL)
12989 {
12990 VMA_DEBUG_LOG(" Deleted empty block");
12991 pBlockToDelete->Destroy(m_hAllocator);
12992 vma_delete(m_hAllocator, pBlockToDelete);
12993 }
12994 }
12995
CalcMaxBlockSize()12996 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12997 {
12998 VkDeviceSize result = 0;
12999 for(size_t i = m_Blocks.size(); i--; )
13000 {
13001 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13002 if(result >= m_PreferredBlockSize)
13003 {
13004 break;
13005 }
13006 }
13007 return result;
13008 }
13009
Remove(VmaDeviceMemoryBlock * pBlock)13010 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13011 {
13012 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13013 {
13014 if(m_Blocks[blockIndex] == pBlock)
13015 {
13016 VmaVectorRemove(m_Blocks, blockIndex);
13017 return;
13018 }
13019 }
13020 VMA_ASSERT(0);
13021 }
13022
IncrementallySortBlocks()13023 void VmaBlockVector::IncrementallySortBlocks()
13024 {
13025 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13026 {
13027 // Bubble sort only until first swap.
13028 for(size_t i = 1; i < m_Blocks.size(); ++i)
13029 {
13030 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13031 {
13032 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13033 return;
13034 }
13035 }
13036 }
13037 }
13038
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13039 VkResult VmaBlockVector::AllocateFromBlock(
13040 VmaDeviceMemoryBlock* pBlock,
13041 uint32_t currentFrameIndex,
13042 VkDeviceSize size,
13043 VkDeviceSize alignment,
13044 VmaAllocationCreateFlags allocFlags,
13045 void* pUserData,
13046 VmaSuballocationType suballocType,
13047 uint32_t strategy,
13048 VmaAllocation* pAllocation)
13049 {
13050 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13051 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13052 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13053 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13054
13055 VmaAllocationRequest currRequest = {};
13056 if(pBlock->m_pMetadata->CreateAllocationRequest(
13057 currentFrameIndex,
13058 m_FrameInUseCount,
13059 m_BufferImageGranularity,
13060 size,
13061 alignment,
13062 isUpperAddress,
13063 suballocType,
13064 false, // canMakeOtherLost
13065 strategy,
13066 &currRequest))
13067 {
13068 // Allocate from pCurrBlock.
13069 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13070
13071 if(mapped)
13072 {
13073 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13074 if(res != VK_SUCCESS)
13075 {
13076 return res;
13077 }
13078 }
13079
13080 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13081 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13082 UpdateHasEmptyBlock();
13083 (*pAllocation)->InitBlockAllocation(
13084 pBlock,
13085 currRequest.offset,
13086 alignment,
13087 size,
13088 m_MemoryTypeIndex,
13089 suballocType,
13090 mapped,
13091 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
13092 VMA_HEAVY_ASSERT(pBlock->Validate());
13093 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13094 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13095 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13096 {
13097 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13098 }
13099 if(IsCorruptionDetectionEnabled())
13100 {
13101 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13102 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13103 }
13104 return VK_SUCCESS;
13105 }
13106 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13107 }
13108
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13109 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13110 {
13111 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13112 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13113 allocInfo.allocationSize = blockSize;
13114
13115 #if VMA_BUFFER_DEVICE_ADDRESS
13116 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13117 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13118 if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13119 {
13120 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13121 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13122 }
13123 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13124
13125 VkDeviceMemory mem = VK_NULL_HANDLE;
13126 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13127 if(res < 0)
13128 {
13129 return res;
13130 }
13131
13132 // New VkDeviceMemory successfully created.
13133
13134 // Create new Allocation for it.
13135 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13136 pBlock->Init(
13137 m_hAllocator,
13138 m_hParentPool,
13139 m_MemoryTypeIndex,
13140 mem,
13141 allocInfo.allocationSize,
13142 m_NextBlockId++,
13143 m_Algorithm);
13144
13145 m_Blocks.push_back(pBlock);
13146 if(pNewBlockIndex != VMA_NULL)
13147 {
13148 *pNewBlockIndex = m_Blocks.size() - 1;
13149 }
13150
13151 return VK_SUCCESS;
13152 }
13153
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13154 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13155 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13156 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13157 {
13158 const size_t blockCount = m_Blocks.size();
13159 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13160
13161 enum BLOCK_FLAG
13162 {
13163 BLOCK_FLAG_USED = 0x00000001,
13164 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13165 };
13166
13167 struct BlockInfo
13168 {
13169 uint32_t flags;
13170 void* pMappedData;
13171 };
13172 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13173 blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13174 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13175
13176 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13177 const size_t moveCount = moves.size();
13178 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13179 {
13180 const VmaDefragmentationMove& move = moves[moveIndex];
13181 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13182 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13183 }
13184
13185 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13186
13187 // Go over all blocks. Get mapped pointer or map if necessary.
13188 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13189 {
13190 BlockInfo& currBlockInfo = blockInfo[blockIndex];
13191 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13192 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13193 {
13194 currBlockInfo.pMappedData = pBlock->GetMappedData();
13195 // It is not originally mapped - map it.
13196 if(currBlockInfo.pMappedData == VMA_NULL)
13197 {
13198 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13199 if(pDefragCtx->res == VK_SUCCESS)
13200 {
13201 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13202 }
13203 }
13204 }
13205 }
13206
13207 // Go over all moves. Do actual data transfer.
13208 if(pDefragCtx->res == VK_SUCCESS)
13209 {
13210 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13211 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13212
13213 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13214 {
13215 const VmaDefragmentationMove& move = moves[moveIndex];
13216
13217 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13218 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13219
13220 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13221
13222 // Invalidate source.
13223 if(isNonCoherent)
13224 {
13225 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13226 memRange.memory = pSrcBlock->GetDeviceMemory();
13227 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13228 memRange.size = VMA_MIN(
13229 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13230 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13231 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13232 }
13233
13234 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13235 memmove(
13236 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13237 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13238 static_cast<size_t>(move.size));
13239
13240 if(IsCorruptionDetectionEnabled())
13241 {
13242 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13243 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13244 }
13245
13246 // Flush destination.
13247 if(isNonCoherent)
13248 {
13249 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13250 memRange.memory = pDstBlock->GetDeviceMemory();
13251 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13252 memRange.size = VMA_MIN(
13253 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13254 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13255 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13256 }
13257 }
13258 }
13259
13260 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13261 // Regardless of pCtx->res == VK_SUCCESS.
13262 for(size_t blockIndex = blockCount; blockIndex--; )
13263 {
13264 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13265 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13266 {
13267 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13268 pBlock->Unmap(m_hAllocator, 1);
13269 }
13270 }
13271 }
13272
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13273 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13274 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13275 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13276 VkCommandBuffer commandBuffer)
13277 {
13278 const size_t blockCount = m_Blocks.size();
13279
13280 pDefragCtx->blockContexts.resize(blockCount);
13281 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13282
13283 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13284 const size_t moveCount = moves.size();
13285 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13286 {
13287 const VmaDefragmentationMove& move = moves[moveIndex];
13288
13289 //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13290 {
13291 // Old school move still require us to map the whole block
13292 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13293 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13294 }
13295 }
13296
13297 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13298
13299 // Go over all blocks. Create and bind buffer for whole block if necessary.
13300 {
13301 VkBufferCreateInfo bufCreateInfo;
13302 VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13303
13304 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13305 {
13306 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13307 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13308 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13309 {
13310 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13311 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13312 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13313 if(pDefragCtx->res == VK_SUCCESS)
13314 {
13315 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13316 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13317 }
13318 }
13319 }
13320 }
13321
13322 // Go over all moves. Post data transfer commands to command buffer.
13323 if(pDefragCtx->res == VK_SUCCESS)
13324 {
13325 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13326 {
13327 const VmaDefragmentationMove& move = moves[moveIndex];
13328
13329 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13330 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13331
13332 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13333
13334 VkBufferCopy region = {
13335 move.srcOffset,
13336 move.dstOffset,
13337 move.size };
13338 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13339 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion);
13340 }
13341 }
13342
13343 // Save buffers to defrag context for later destruction.
13344 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13345 {
13346 pDefragCtx->res = VK_NOT_READY;
13347 }
13348 }
13349
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13350 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13351 {
13352 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13353 {
13354 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13355 if(pBlock->m_pMetadata->IsEmpty())
13356 {
13357 if(m_Blocks.size() > m_MinBlockCount)
13358 {
13359 if(pDefragmentationStats != VMA_NULL)
13360 {
13361 ++pDefragmentationStats->deviceMemoryBlocksFreed;
13362 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13363 }
13364
13365 VmaVectorRemove(m_Blocks, blockIndex);
13366 pBlock->Destroy(m_hAllocator);
13367 vma_delete(m_hAllocator, pBlock);
13368 }
13369 else
13370 {
13371 break;
13372 }
13373 }
13374 }
13375 UpdateHasEmptyBlock();
13376 }
13377
UpdateHasEmptyBlock()13378 void VmaBlockVector::UpdateHasEmptyBlock()
13379 {
13380 m_HasEmptyBlock = false;
13381 for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13382 {
13383 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13384 if(pBlock->m_pMetadata->IsEmpty())
13385 {
13386 m_HasEmptyBlock = true;
13387 break;
13388 }
13389 }
13390 }
13391
13392 #if VMA_STATS_STRING_ENABLED
13393
PrintDetailedMap(class VmaJsonWriter & json)13394 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13395 {
13396 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13397
13398 json.BeginObject();
13399
13400 if(IsCustomPool())
13401 {
13402 const char* poolName = m_hParentPool->GetName();
13403 if(poolName != VMA_NULL && poolName[0] != '\0')
13404 {
13405 json.WriteString("Name");
13406 json.WriteString(poolName);
13407 }
13408
13409 json.WriteString("MemoryTypeIndex");
13410 json.WriteNumber(m_MemoryTypeIndex);
13411
13412 json.WriteString("BlockSize");
13413 json.WriteNumber(m_PreferredBlockSize);
13414
13415 json.WriteString("BlockCount");
13416 json.BeginObject(true);
13417 if(m_MinBlockCount > 0)
13418 {
13419 json.WriteString("Min");
13420 json.WriteNumber((uint64_t)m_MinBlockCount);
13421 }
13422 if(m_MaxBlockCount < SIZE_MAX)
13423 {
13424 json.WriteString("Max");
13425 json.WriteNumber((uint64_t)m_MaxBlockCount);
13426 }
13427 json.WriteString("Cur");
13428 json.WriteNumber((uint64_t)m_Blocks.size());
13429 json.EndObject();
13430
13431 if(m_FrameInUseCount > 0)
13432 {
13433 json.WriteString("FrameInUseCount");
13434 json.WriteNumber(m_FrameInUseCount);
13435 }
13436
13437 if(m_Algorithm != 0)
13438 {
13439 json.WriteString("Algorithm");
13440 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13441 }
13442 }
13443 else
13444 {
13445 json.WriteString("PreferredBlockSize");
13446 json.WriteNumber(m_PreferredBlockSize);
13447 }
13448
13449 json.WriteString("Blocks");
13450 json.BeginObject();
13451 for(size_t i = 0; i < m_Blocks.size(); ++i)
13452 {
13453 json.BeginString();
13454 json.ContinueString(m_Blocks[i]->GetId());
13455 json.EndString();
13456
13457 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13458 }
13459 json.EndObject();
13460
13461 json.EndObject();
13462 }
13463
13464 #endif // #if VMA_STATS_STRING_ENABLED
13465
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13466 void VmaBlockVector::Defragment(
13467 class VmaBlockVectorDefragmentationContext* pCtx,
13468 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13469 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13470 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13471 VkCommandBuffer commandBuffer)
13472 {
13473 pCtx->res = VK_SUCCESS;
13474
13475 const VkMemoryPropertyFlags memPropFlags =
13476 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13477 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13478
13479 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13480 isHostVisible;
13481 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13482 !IsCorruptionDetectionEnabled() &&
13483 ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13484
13485 // There are options to defragment this memory type.
13486 if(canDefragmentOnCpu || canDefragmentOnGpu)
13487 {
13488 bool defragmentOnGpu;
13489 // There is only one option to defragment this memory type.
13490 if(canDefragmentOnGpu != canDefragmentOnCpu)
13491 {
13492 defragmentOnGpu = canDefragmentOnGpu;
13493 }
13494 // Both options are available: Heuristics to choose the best one.
13495 else
13496 {
13497 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13498 m_hAllocator->IsIntegratedGpu();
13499 }
13500
13501 bool overlappingMoveSupported = !defragmentOnGpu;
13502
13503 if(m_hAllocator->m_UseMutex)
13504 {
13505 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13506 {
13507 if(!m_Mutex.TryLockWrite())
13508 {
13509 pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13510 return;
13511 }
13512 }
13513 else
13514 {
13515 m_Mutex.LockWrite();
13516 pCtx->mutexLocked = true;
13517 }
13518 }
13519
13520 pCtx->Begin(overlappingMoveSupported, flags);
13521
13522 // Defragment.
13523
13524 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13525 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13526 pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13527
13528 // Accumulate statistics.
13529 if(pStats != VMA_NULL)
13530 {
13531 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13532 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13533 pStats->bytesMoved += bytesMoved;
13534 pStats->allocationsMoved += allocationsMoved;
13535 VMA_ASSERT(bytesMoved <= maxBytesToMove);
13536 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13537 if(defragmentOnGpu)
13538 {
13539 maxGpuBytesToMove -= bytesMoved;
13540 maxGpuAllocationsToMove -= allocationsMoved;
13541 }
13542 else
13543 {
13544 maxCpuBytesToMove -= bytesMoved;
13545 maxCpuAllocationsToMove -= allocationsMoved;
13546 }
13547 }
13548
13549 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13550 {
13551 if(m_hAllocator->m_UseMutex)
13552 m_Mutex.UnlockWrite();
13553
13554 if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13555 pCtx->res = VK_NOT_READY;
13556
13557 return;
13558 }
13559
13560 if(pCtx->res >= VK_SUCCESS)
13561 {
13562 if(defragmentOnGpu)
13563 {
13564 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13565 }
13566 else
13567 {
13568 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13569 }
13570 }
13571 }
13572 }
13573
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13574 void VmaBlockVector::DefragmentationEnd(
13575 class VmaBlockVectorDefragmentationContext* pCtx,
13576 uint32_t flags,
13577 VmaDefragmentationStats* pStats)
13578 {
13579 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13580 {
13581 VMA_ASSERT(pCtx->mutexLocked == false);
13582
13583 // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13584 // lock protecting us. Since we mutate state here, we have to take the lock out now
13585 m_Mutex.LockWrite();
13586 pCtx->mutexLocked = true;
13587 }
13588
13589 // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13590 if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13591 {
13592 // Destroy buffers.
13593 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13594 {
13595 VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13596 if(blockCtx.hBuffer)
13597 {
13598 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13599 }
13600 }
13601
13602 if(pCtx->res >= VK_SUCCESS)
13603 {
13604 FreeEmptyBlocks(pStats);
13605 }
13606 }
13607
13608 if(pCtx->mutexLocked)
13609 {
13610 VMA_ASSERT(m_hAllocator->m_UseMutex);
13611 m_Mutex.UnlockWrite();
13612 }
13613 }
13614
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13615 uint32_t VmaBlockVector::ProcessDefragmentations(
13616 class VmaBlockVectorDefragmentationContext *pCtx,
13617 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13618 {
13619 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13620
13621 const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13622
13623 for(uint32_t i = 0; i < moveCount; ++ i)
13624 {
13625 VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13626
13627 pMove->allocation = move.hAllocation;
13628 pMove->memory = move.pDstBlock->GetDeviceMemory();
13629 pMove->offset = move.dstOffset;
13630
13631 ++ pMove;
13632 }
13633
13634 pCtx->defragmentationMovesProcessed += moveCount;
13635
13636 return moveCount;
13637 }
13638
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13639 void VmaBlockVector::CommitDefragmentations(
13640 class VmaBlockVectorDefragmentationContext *pCtx,
13641 VmaDefragmentationStats* pStats)
13642 {
13643 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13644
13645 for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13646 {
13647 const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13648
13649 move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13650 move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13651 }
13652
13653 pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13654 FreeEmptyBlocks(pStats);
13655 }
13656
CalcAllocationCount()13657 size_t VmaBlockVector::CalcAllocationCount() const
13658 {
13659 size_t result = 0;
13660 for(size_t i = 0; i < m_Blocks.size(); ++i)
13661 {
13662 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13663 }
13664 return result;
13665 }
13666
IsBufferImageGranularityConflictPossible()13667 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13668 {
13669 if(m_BufferImageGranularity == 1)
13670 {
13671 return false;
13672 }
13673 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13674 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13675 {
13676 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13677 VMA_ASSERT(m_Algorithm == 0);
13678 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13679 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13680 {
13681 return true;
13682 }
13683 }
13684 return false;
13685 }
13686
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13687 void VmaBlockVector::MakePoolAllocationsLost(
13688 uint32_t currentFrameIndex,
13689 size_t* pLostAllocationCount)
13690 {
13691 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13692 size_t lostAllocationCount = 0;
13693 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13694 {
13695 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13696 VMA_ASSERT(pBlock);
13697 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13698 }
13699 if(pLostAllocationCount != VMA_NULL)
13700 {
13701 *pLostAllocationCount = lostAllocationCount;
13702 }
13703 }
13704
CheckCorruption()13705 VkResult VmaBlockVector::CheckCorruption()
13706 {
13707 if(!IsCorruptionDetectionEnabled())
13708 {
13709 return VK_ERROR_FEATURE_NOT_PRESENT;
13710 }
13711
13712 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13713 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13714 {
13715 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13716 VMA_ASSERT(pBlock);
13717 VkResult res = pBlock->CheckCorruption(m_hAllocator);
13718 if(res != VK_SUCCESS)
13719 {
13720 return res;
13721 }
13722 }
13723 return VK_SUCCESS;
13724 }
13725
AddStats(VmaStats * pStats)13726 void VmaBlockVector::AddStats(VmaStats* pStats)
13727 {
13728 const uint32_t memTypeIndex = m_MemoryTypeIndex;
13729 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13730
13731 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13732
13733 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13734 {
13735 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13736 VMA_ASSERT(pBlock);
13737 VMA_HEAVY_ASSERT(pBlock->Validate());
13738 VmaStatInfo allocationStatInfo;
13739 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13740 VmaAddStatInfo(pStats->total, allocationStatInfo);
13741 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13742 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13743 }
13744 }
13745
13746 ////////////////////////////////////////////////////////////////////////////////
13747 // VmaDefragmentationAlgorithm_Generic members definition
13748
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13749 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13750 VmaAllocator hAllocator,
13751 VmaBlockVector* pBlockVector,
13752 uint32_t currentFrameIndex,
13753 bool overlappingMoveSupported) :
13754 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13755 m_AllocationCount(0),
13756 m_AllAllocations(false),
13757 m_BytesMoved(0),
13758 m_AllocationsMoved(0),
13759 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13760 {
13761 // Create block info for each block.
13762 const size_t blockCount = m_pBlockVector->m_Blocks.size();
13763 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13764 {
13765 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13766 pBlockInfo->m_OriginalBlockIndex = blockIndex;
13767 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13768 m_Blocks.push_back(pBlockInfo);
13769 }
13770
13771 // Sort them by m_pBlock pointer value.
13772 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13773 }
13774
~VmaDefragmentationAlgorithm_Generic()13775 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13776 {
13777 for(size_t i = m_Blocks.size(); i--; )
13778 {
13779 vma_delete(m_hAllocator, m_Blocks[i]);
13780 }
13781 }
13782
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13783 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13784 {
13785 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13786 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13787 {
13788 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13789 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13790 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13791 {
13792 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13793 (*it)->m_Allocations.push_back(allocInfo);
13794 }
13795 else
13796 {
13797 VMA_ASSERT(0);
13798 }
13799
13800 ++m_AllocationCount;
13801 }
13802 }
13803
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)13804 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
13805 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13806 VkDeviceSize maxBytesToMove,
13807 uint32_t maxAllocationsToMove,
13808 bool freeOldAllocations)
13809 {
13810 if(m_Blocks.empty())
13811 {
13812 return VK_SUCCESS;
13813 }
13814
13815 // This is a choice based on research.
13816 // Option 1:
13817 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
13818 // Option 2:
13819 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
13820 // Option 3:
13821 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
13822
13823 size_t srcBlockMinIndex = 0;
13824 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
13825 /*
13826 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
13827 {
13828 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
13829 if(blocksWithNonMovableCount > 0)
13830 {
13831 srcBlockMinIndex = blocksWithNonMovableCount - 1;
13832 }
13833 }
13834 */
13835
13836 size_t srcBlockIndex = m_Blocks.size() - 1;
13837 size_t srcAllocIndex = SIZE_MAX;
13838 for(;;)
13839 {
13840 // 1. Find next allocation to move.
13841 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
13842 // 1.2. Then start from last to first m_Allocations.
13843 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
13844 {
13845 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
13846 {
13847 // Finished: no more allocations to process.
13848 if(srcBlockIndex == srcBlockMinIndex)
13849 {
13850 return VK_SUCCESS;
13851 }
13852 else
13853 {
13854 --srcBlockIndex;
13855 srcAllocIndex = SIZE_MAX;
13856 }
13857 }
13858 else
13859 {
13860 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
13861 }
13862 }
13863
13864 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
13865 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
13866
13867 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
13868 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
13869 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
13870 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
13871
13872 // 2. Try to find new place for this allocation in preceding or current block.
13873 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
13874 {
13875 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
13876 VmaAllocationRequest dstAllocRequest;
13877 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
13878 m_CurrentFrameIndex,
13879 m_pBlockVector->GetFrameInUseCount(),
13880 m_pBlockVector->GetBufferImageGranularity(),
13881 size,
13882 alignment,
13883 false, // upperAddress
13884 suballocType,
13885 false, // canMakeOtherLost
13886 strategy,
13887 &dstAllocRequest) &&
13888 MoveMakesSense(
13889 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
13890 {
13891 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
13892
13893 // Reached limit on number of allocations or bytes to move.
13894 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
13895 (m_BytesMoved + size > maxBytesToMove))
13896 {
13897 return VK_SUCCESS;
13898 }
13899
13900 VmaDefragmentationMove move = {};
13901 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
13902 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
13903 move.srcOffset = srcOffset;
13904 move.dstOffset = dstAllocRequest.offset;
13905 move.size = size;
13906 move.hAllocation = allocInfo.m_hAllocation;
13907 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
13908 move.pDstBlock = pDstBlockInfo->m_pBlock;
13909
13910 moves.push_back(move);
13911
13912 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
13913 dstAllocRequest,
13914 suballocType,
13915 size,
13916 allocInfo.m_hAllocation);
13917
13918 if(freeOldAllocations)
13919 {
13920 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
13921 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
13922 }
13923
13924 if(allocInfo.m_pChanged != VMA_NULL)
13925 {
13926 *allocInfo.m_pChanged = VK_TRUE;
13927 }
13928
13929 ++m_AllocationsMoved;
13930 m_BytesMoved += size;
13931
13932 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
13933
13934 break;
13935 }
13936 }
13937
13938 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
13939
13940 if(srcAllocIndex > 0)
13941 {
13942 --srcAllocIndex;
13943 }
13944 else
13945 {
13946 if(srcBlockIndex > 0)
13947 {
13948 --srcBlockIndex;
13949 srcAllocIndex = SIZE_MAX;
13950 }
13951 else
13952 {
13953 return VK_SUCCESS;
13954 }
13955 }
13956 }
13957 }
13958
CalcBlocksWithNonMovableCount()13959 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
13960 {
13961 size_t result = 0;
13962 for(size_t i = 0; i < m_Blocks.size(); ++i)
13963 {
13964 if(m_Blocks[i]->m_HasNonMovableAllocations)
13965 {
13966 ++result;
13967 }
13968 }
13969 return result;
13970 }
13971
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)13972 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
13973 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13974 VkDeviceSize maxBytesToMove,
13975 uint32_t maxAllocationsToMove,
13976 VmaDefragmentationFlags flags)
13977 {
13978 if(!m_AllAllocations && m_AllocationCount == 0)
13979 {
13980 return VK_SUCCESS;
13981 }
13982
13983 const size_t blockCount = m_Blocks.size();
13984 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13985 {
13986 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
13987
13988 if(m_AllAllocations)
13989 {
13990 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
13991 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
13992 it != pMetadata->m_Suballocations.end();
13993 ++it)
13994 {
13995 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
13996 {
13997 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
13998 pBlockInfo->m_Allocations.push_back(allocInfo);
13999 }
14000 }
14001 }
14002
14003 pBlockInfo->CalcHasNonMovableAllocations();
14004
14005 // This is a choice based on research.
14006 // Option 1:
14007 pBlockInfo->SortAllocationsByOffsetDescending();
14008 // Option 2:
14009 //pBlockInfo->SortAllocationsBySizeDescending();
14010 }
14011
14012 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14013 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14014
14015 // This is a choice based on research.
14016 const uint32_t roundCount = 2;
14017
14018 // Execute defragmentation rounds (the main part).
14019 VkResult result = VK_SUCCESS;
14020 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14021 {
14022 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14023 }
14024
14025 return result;
14026 }
14027
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14028 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14029 size_t dstBlockIndex, VkDeviceSize dstOffset,
14030 size_t srcBlockIndex, VkDeviceSize srcOffset)
14031 {
14032 if(dstBlockIndex < srcBlockIndex)
14033 {
14034 return true;
14035 }
14036 if(dstBlockIndex > srcBlockIndex)
14037 {
14038 return false;
14039 }
14040 if(dstOffset < srcOffset)
14041 {
14042 return true;
14043 }
14044 return false;
14045 }
14046
14047 ////////////////////////////////////////////////////////////////////////////////
14048 // VmaDefragmentationAlgorithm_Fast
14049
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14050 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14051 VmaAllocator hAllocator,
14052 VmaBlockVector* pBlockVector,
14053 uint32_t currentFrameIndex,
14054 bool overlappingMoveSupported) :
14055 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14056 m_OverlappingMoveSupported(overlappingMoveSupported),
14057 m_AllocationCount(0),
14058 m_AllAllocations(false),
14059 m_BytesMoved(0),
14060 m_AllocationsMoved(0),
14061 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14062 {
14063 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14064
14065 }
14066
~VmaDefragmentationAlgorithm_Fast()14067 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14068 {
14069 }
14070
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14071 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14072 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14073 VkDeviceSize maxBytesToMove,
14074 uint32_t maxAllocationsToMove,
14075 VmaDefragmentationFlags flags)
14076 {
14077 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14078
14079 const size_t blockCount = m_pBlockVector->GetBlockCount();
14080 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14081 {
14082 return VK_SUCCESS;
14083 }
14084
14085 PreprocessMetadata();
14086
14087 // Sort blocks in order from most destination.
14088
14089 m_BlockInfos.resize(blockCount);
14090 for(size_t i = 0; i < blockCount; ++i)
14091 {
14092 m_BlockInfos[i].origBlockIndex = i;
14093 }
14094
14095 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14096 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14097 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14098 });
14099
14100 // THE MAIN ALGORITHM
14101
14102 FreeSpaceDatabase freeSpaceDb;
14103
14104 size_t dstBlockInfoIndex = 0;
14105 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14106 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14107 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14108 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14109 VkDeviceSize dstOffset = 0;
14110
14111 bool end = false;
14112 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14113 {
14114 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14115 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14116 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14117 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14118 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14119 {
14120 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14121 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14122 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14123 if(m_AllocationsMoved == maxAllocationsToMove ||
14124 m_BytesMoved + srcAllocSize > maxBytesToMove)
14125 {
14126 end = true;
14127 break;
14128 }
14129 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14130
14131 VmaDefragmentationMove move = {};
14132 // Try to place it in one of free spaces from the database.
14133 size_t freeSpaceInfoIndex;
14134 VkDeviceSize dstAllocOffset;
14135 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14136 freeSpaceInfoIndex, dstAllocOffset))
14137 {
14138 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14139 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14140 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14141
14142 // Same block
14143 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14144 {
14145 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14146
14147 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14148
14149 VmaSuballocation suballoc = *srcSuballocIt;
14150 suballoc.offset = dstAllocOffset;
14151 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14152 m_BytesMoved += srcAllocSize;
14153 ++m_AllocationsMoved;
14154
14155 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14156 ++nextSuballocIt;
14157 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14158 srcSuballocIt = nextSuballocIt;
14159
14160 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14161
14162 move.srcBlockIndex = srcOrigBlockIndex;
14163 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14164 move.srcOffset = srcAllocOffset;
14165 move.dstOffset = dstAllocOffset;
14166 move.size = srcAllocSize;
14167
14168 moves.push_back(move);
14169 }
14170 // Different block
14171 else
14172 {
14173 // MOVE OPTION 2: Move the allocation to a different block.
14174
14175 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14176
14177 VmaSuballocation suballoc = *srcSuballocIt;
14178 suballoc.offset = dstAllocOffset;
14179 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14180 m_BytesMoved += srcAllocSize;
14181 ++m_AllocationsMoved;
14182
14183 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14184 ++nextSuballocIt;
14185 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14186 srcSuballocIt = nextSuballocIt;
14187
14188 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14189
14190 move.srcBlockIndex = srcOrigBlockIndex;
14191 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14192 move.srcOffset = srcAllocOffset;
14193 move.dstOffset = dstAllocOffset;
14194 move.size = srcAllocSize;
14195
14196 moves.push_back(move);
14197 }
14198 }
14199 else
14200 {
14201 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14202
14203 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14204 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14205 dstAllocOffset + srcAllocSize > dstBlockSize)
14206 {
14207 // But before that, register remaining free space at the end of dst block.
14208 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14209
14210 ++dstBlockInfoIndex;
14211 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14212 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14213 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14214 dstBlockSize = pDstMetadata->GetSize();
14215 dstOffset = 0;
14216 dstAllocOffset = 0;
14217 }
14218
14219 // Same block
14220 if(dstBlockInfoIndex == srcBlockInfoIndex)
14221 {
14222 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14223
14224 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14225
14226 bool skipOver = overlap;
14227 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14228 {
14229 // If destination and source place overlap, skip if it would move it
14230 // by only < 1/64 of its size.
14231 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14232 }
14233
14234 if(skipOver)
14235 {
14236 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14237
14238 dstOffset = srcAllocOffset + srcAllocSize;
14239 ++srcSuballocIt;
14240 }
14241 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14242 else
14243 {
14244 srcSuballocIt->offset = dstAllocOffset;
14245 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14246 dstOffset = dstAllocOffset + srcAllocSize;
14247 m_BytesMoved += srcAllocSize;
14248 ++m_AllocationsMoved;
14249 ++srcSuballocIt;
14250
14251 move.srcBlockIndex = srcOrigBlockIndex;
14252 move.dstBlockIndex = dstOrigBlockIndex;
14253 move.srcOffset = srcAllocOffset;
14254 move.dstOffset = dstAllocOffset;
14255 move.size = srcAllocSize;
14256
14257 moves.push_back(move);
14258 }
14259 }
14260 // Different block
14261 else
14262 {
14263 // MOVE OPTION 2: Move the allocation to a different block.
14264
14265 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14266 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14267
14268 VmaSuballocation suballoc = *srcSuballocIt;
14269 suballoc.offset = dstAllocOffset;
14270 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14271 dstOffset = dstAllocOffset + srcAllocSize;
14272 m_BytesMoved += srcAllocSize;
14273 ++m_AllocationsMoved;
14274
14275 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14276 ++nextSuballocIt;
14277 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14278 srcSuballocIt = nextSuballocIt;
14279
14280 pDstMetadata->m_Suballocations.push_back(suballoc);
14281
14282 move.srcBlockIndex = srcOrigBlockIndex;
14283 move.dstBlockIndex = dstOrigBlockIndex;
14284 move.srcOffset = srcAllocOffset;
14285 move.dstOffset = dstAllocOffset;
14286 move.size = srcAllocSize;
14287
14288 moves.push_back(move);
14289 }
14290 }
14291 }
14292 }
14293
14294 m_BlockInfos.clear();
14295
14296 PostprocessMetadata();
14297
14298 return VK_SUCCESS;
14299 }
14300
PreprocessMetadata()14301 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14302 {
14303 const size_t blockCount = m_pBlockVector->GetBlockCount();
14304 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14305 {
14306 VmaBlockMetadata_Generic* const pMetadata =
14307 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14308 pMetadata->m_FreeCount = 0;
14309 pMetadata->m_SumFreeSize = pMetadata->GetSize();
14310 pMetadata->m_FreeSuballocationsBySize.clear();
14311 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14312 it != pMetadata->m_Suballocations.end(); )
14313 {
14314 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14315 {
14316 VmaSuballocationList::iterator nextIt = it;
14317 ++nextIt;
14318 pMetadata->m_Suballocations.erase(it);
14319 it = nextIt;
14320 }
14321 else
14322 {
14323 ++it;
14324 }
14325 }
14326 }
14327 }
14328
PostprocessMetadata()14329 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14330 {
14331 const size_t blockCount = m_pBlockVector->GetBlockCount();
14332 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14333 {
14334 VmaBlockMetadata_Generic* const pMetadata =
14335 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14336 const VkDeviceSize blockSize = pMetadata->GetSize();
14337
14338 // No allocations in this block - entire area is free.
14339 if(pMetadata->m_Suballocations.empty())
14340 {
14341 pMetadata->m_FreeCount = 1;
14342 //pMetadata->m_SumFreeSize is already set to blockSize.
14343 VmaSuballocation suballoc = {
14344 0, // offset
14345 blockSize, // size
14346 VMA_NULL, // hAllocation
14347 VMA_SUBALLOCATION_TYPE_FREE };
14348 pMetadata->m_Suballocations.push_back(suballoc);
14349 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14350 }
14351 // There are some allocations in this block.
14352 else
14353 {
14354 VkDeviceSize offset = 0;
14355 VmaSuballocationList::iterator it;
14356 for(it = pMetadata->m_Suballocations.begin();
14357 it != pMetadata->m_Suballocations.end();
14358 ++it)
14359 {
14360 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14361 VMA_ASSERT(it->offset >= offset);
14362
14363 // Need to insert preceding free space.
14364 if(it->offset > offset)
14365 {
14366 ++pMetadata->m_FreeCount;
14367 const VkDeviceSize freeSize = it->offset - offset;
14368 VmaSuballocation suballoc = {
14369 offset, // offset
14370 freeSize, // size
14371 VMA_NULL, // hAllocation
14372 VMA_SUBALLOCATION_TYPE_FREE };
14373 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14374 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14375 {
14376 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14377 }
14378 }
14379
14380 pMetadata->m_SumFreeSize -= it->size;
14381 offset = it->offset + it->size;
14382 }
14383
14384 // Need to insert trailing free space.
14385 if(offset < blockSize)
14386 {
14387 ++pMetadata->m_FreeCount;
14388 const VkDeviceSize freeSize = blockSize - offset;
14389 VmaSuballocation suballoc = {
14390 offset, // offset
14391 freeSize, // size
14392 VMA_NULL, // hAllocation
14393 VMA_SUBALLOCATION_TYPE_FREE };
14394 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14395 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14396 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14397 {
14398 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14399 }
14400 }
14401
14402 VMA_SORT(
14403 pMetadata->m_FreeSuballocationsBySize.begin(),
14404 pMetadata->m_FreeSuballocationsBySize.end(),
14405 VmaSuballocationItemSizeLess());
14406 }
14407
14408 VMA_HEAVY_ASSERT(pMetadata->Validate());
14409 }
14410 }
14411
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14412 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14413 {
14414 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14415 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14416 while(it != pMetadata->m_Suballocations.end())
14417 {
14418 if(it->offset < suballoc.offset)
14419 {
14420 ++it;
14421 }
14422 }
14423 pMetadata->m_Suballocations.insert(it, suballoc);
14424 }
14425
14426 ////////////////////////////////////////////////////////////////////////////////
14427 // VmaBlockVectorDefragmentationContext
14428
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14429 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14430 VmaAllocator hAllocator,
14431 VmaPool hCustomPool,
14432 VmaBlockVector* pBlockVector,
14433 uint32_t currFrameIndex) :
14434 res(VK_SUCCESS),
14435 mutexLocked(false),
14436 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14437 defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14438 defragmentationMovesProcessed(0),
14439 defragmentationMovesCommitted(0),
14440 hasDefragmentationPlan(0),
14441 m_hAllocator(hAllocator),
14442 m_hCustomPool(hCustomPool),
14443 m_pBlockVector(pBlockVector),
14444 m_CurrFrameIndex(currFrameIndex),
14445 m_pAlgorithm(VMA_NULL),
14446 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14447 m_AllAllocations(false)
14448 {
14449 }
14450
~VmaBlockVectorDefragmentationContext()14451 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14452 {
14453 vma_delete(m_hAllocator, m_pAlgorithm);
14454 }
14455
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14456 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14457 {
14458 AllocInfo info = { hAlloc, pChanged };
14459 m_Allocations.push_back(info);
14460 }
14461
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14462 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14463 {
14464 const bool allAllocations = m_AllAllocations ||
14465 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14466
14467 /********************************
14468 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14469 ********************************/
14470
14471 /*
14472 Fast algorithm is supported only when certain criteria are met:
14473 - VMA_DEBUG_MARGIN is 0.
14474 - All allocations in this block vector are moveable.
14475 - There is no possibility of image/buffer granularity conflict.
14476 - The defragmentation is not incremental
14477 */
14478 if(VMA_DEBUG_MARGIN == 0 &&
14479 allAllocations &&
14480 !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14481 !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14482 {
14483 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14484 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14485 }
14486 else
14487 {
14488 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14489 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14490 }
14491
14492 if(allAllocations)
14493 {
14494 m_pAlgorithm->AddAll();
14495 }
14496 else
14497 {
14498 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14499 {
14500 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14501 }
14502 }
14503 }
14504
14505 ////////////////////////////////////////////////////////////////////////////////
14506 // VmaDefragmentationContext
14507
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14508 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14509 VmaAllocator hAllocator,
14510 uint32_t currFrameIndex,
14511 uint32_t flags,
14512 VmaDefragmentationStats* pStats) :
14513 m_hAllocator(hAllocator),
14514 m_CurrFrameIndex(currFrameIndex),
14515 m_Flags(flags),
14516 m_pStats(pStats),
14517 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14518 {
14519 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14520 }
14521
~VmaDefragmentationContext_T()14522 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14523 {
14524 for(size_t i = m_CustomPoolContexts.size(); i--; )
14525 {
14526 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14527 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14528 vma_delete(m_hAllocator, pBlockVectorCtx);
14529 }
14530 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14531 {
14532 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14533 if(pBlockVectorCtx)
14534 {
14535 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14536 vma_delete(m_hAllocator, pBlockVectorCtx);
14537 }
14538 }
14539 }
14540
AddPools(uint32_t poolCount,const VmaPool * pPools)14541 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14542 {
14543 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14544 {
14545 VmaPool pool = pPools[poolIndex];
14546 VMA_ASSERT(pool);
14547 // Pools with algorithm other than default are not defragmented.
14548 if(pool->m_BlockVector.GetAlgorithm() == 0)
14549 {
14550 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14551
14552 for(size_t i = m_CustomPoolContexts.size(); i--; )
14553 {
14554 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14555 {
14556 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14557 break;
14558 }
14559 }
14560
14561 if(!pBlockVectorDefragCtx)
14562 {
14563 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14564 m_hAllocator,
14565 pool,
14566 &pool->m_BlockVector,
14567 m_CurrFrameIndex);
14568 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14569 }
14570
14571 pBlockVectorDefragCtx->AddAll();
14572 }
14573 }
14574 }
14575
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14576 void VmaDefragmentationContext_T::AddAllocations(
14577 uint32_t allocationCount,
14578 const VmaAllocation* pAllocations,
14579 VkBool32* pAllocationsChanged)
14580 {
14581 // Dispatch pAllocations among defragmentators. Create them when necessary.
14582 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14583 {
14584 const VmaAllocation hAlloc = pAllocations[allocIndex];
14585 VMA_ASSERT(hAlloc);
14586 // DedicatedAlloc cannot be defragmented.
14587 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14588 // Lost allocation cannot be defragmented.
14589 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14590 {
14591 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14592
14593 const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14594 // This allocation belongs to custom pool.
14595 if(hAllocPool != VK_NULL_HANDLE)
14596 {
14597 // Pools with algorithm other than default are not defragmented.
14598 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14599 {
14600 for(size_t i = m_CustomPoolContexts.size(); i--; )
14601 {
14602 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14603 {
14604 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14605 break;
14606 }
14607 }
14608 if(!pBlockVectorDefragCtx)
14609 {
14610 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14611 m_hAllocator,
14612 hAllocPool,
14613 &hAllocPool->m_BlockVector,
14614 m_CurrFrameIndex);
14615 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14616 }
14617 }
14618 }
14619 // This allocation belongs to default pool.
14620 else
14621 {
14622 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14623 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14624 if(!pBlockVectorDefragCtx)
14625 {
14626 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14627 m_hAllocator,
14628 VMA_NULL, // hCustomPool
14629 m_hAllocator->m_pBlockVectors[memTypeIndex],
14630 m_CurrFrameIndex);
14631 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14632 }
14633 }
14634
14635 if(pBlockVectorDefragCtx)
14636 {
14637 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14638 &pAllocationsChanged[allocIndex] : VMA_NULL;
14639 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14640 }
14641 }
14642 }
14643 }
14644
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14645 VkResult VmaDefragmentationContext_T::Defragment(
14646 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14647 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14648 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14649 {
14650 if(pStats)
14651 {
14652 memset(pStats, 0, sizeof(VmaDefragmentationStats));
14653 }
14654
14655 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14656 {
14657 // For incremental defragmetnations, we just earmark how much we can move
14658 // The real meat is in the defragmentation steps
14659 m_MaxCpuBytesToMove = maxCpuBytesToMove;
14660 m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14661
14662 m_MaxGpuBytesToMove = maxGpuBytesToMove;
14663 m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14664
14665 if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14666 m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14667 return VK_SUCCESS;
14668
14669 return VK_NOT_READY;
14670 }
14671
14672 if(commandBuffer == VK_NULL_HANDLE)
14673 {
14674 maxGpuBytesToMove = 0;
14675 maxGpuAllocationsToMove = 0;
14676 }
14677
14678 VkResult res = VK_SUCCESS;
14679
14680 // Process default pools.
14681 for(uint32_t memTypeIndex = 0;
14682 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14683 ++memTypeIndex)
14684 {
14685 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14686 if(pBlockVectorCtx)
14687 {
14688 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14689 pBlockVectorCtx->GetBlockVector()->Defragment(
14690 pBlockVectorCtx,
14691 pStats, flags,
14692 maxCpuBytesToMove, maxCpuAllocationsToMove,
14693 maxGpuBytesToMove, maxGpuAllocationsToMove,
14694 commandBuffer);
14695 if(pBlockVectorCtx->res != VK_SUCCESS)
14696 {
14697 res = pBlockVectorCtx->res;
14698 }
14699 }
14700 }
14701
14702 // Process custom pools.
14703 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14704 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14705 ++customCtxIndex)
14706 {
14707 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14708 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14709 pBlockVectorCtx->GetBlockVector()->Defragment(
14710 pBlockVectorCtx,
14711 pStats, flags,
14712 maxCpuBytesToMove, maxCpuAllocationsToMove,
14713 maxGpuBytesToMove, maxGpuAllocationsToMove,
14714 commandBuffer);
14715 if(pBlockVectorCtx->res != VK_SUCCESS)
14716 {
14717 res = pBlockVectorCtx->res;
14718 }
14719 }
14720
14721 return res;
14722 }
14723
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14724 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14725 {
14726 VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14727 uint32_t movesLeft = pInfo->moveCount;
14728
14729 // Process default pools.
14730 for(uint32_t memTypeIndex = 0;
14731 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14732 ++memTypeIndex)
14733 {
14734 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14735 if(pBlockVectorCtx)
14736 {
14737 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14738
14739 if(!pBlockVectorCtx->hasDefragmentationPlan)
14740 {
14741 pBlockVectorCtx->GetBlockVector()->Defragment(
14742 pBlockVectorCtx,
14743 m_pStats, m_Flags,
14744 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14745 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14746 VK_NULL_HANDLE);
14747
14748 if(pBlockVectorCtx->res < VK_SUCCESS)
14749 continue;
14750
14751 pBlockVectorCtx->hasDefragmentationPlan = true;
14752 }
14753
14754 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14755 pBlockVectorCtx,
14756 pCurrentMove, movesLeft);
14757
14758 movesLeft -= processed;
14759 pCurrentMove += processed;
14760 }
14761 }
14762
14763 // Process custom pools.
14764 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14765 customCtxIndex < customCtxCount;
14766 ++customCtxIndex)
14767 {
14768 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14769 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14770
14771 if(!pBlockVectorCtx->hasDefragmentationPlan)
14772 {
14773 pBlockVectorCtx->GetBlockVector()->Defragment(
14774 pBlockVectorCtx,
14775 m_pStats, m_Flags,
14776 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14777 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14778 VK_NULL_HANDLE);
14779
14780 if(pBlockVectorCtx->res < VK_SUCCESS)
14781 continue;
14782
14783 pBlockVectorCtx->hasDefragmentationPlan = true;
14784 }
14785
14786 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14787 pBlockVectorCtx,
14788 pCurrentMove, movesLeft);
14789
14790 movesLeft -= processed;
14791 pCurrentMove += processed;
14792 }
14793
14794 pInfo->moveCount = pInfo->moveCount - movesLeft;
14795
14796 return VK_SUCCESS;
14797 }
DefragmentPassEnd()14798 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
14799 {
14800 VkResult res = VK_SUCCESS;
14801
14802 // Process default pools.
14803 for(uint32_t memTypeIndex = 0;
14804 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14805 ++memTypeIndex)
14806 {
14807 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14808 if(pBlockVectorCtx)
14809 {
14810 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14811
14812 if(!pBlockVectorCtx->hasDefragmentationPlan)
14813 {
14814 res = VK_NOT_READY;
14815 continue;
14816 }
14817
14818 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14819 pBlockVectorCtx, m_pStats);
14820
14821 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14822 res = VK_NOT_READY;
14823 }
14824 }
14825
14826 // Process custom pools.
14827 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14828 customCtxIndex < customCtxCount;
14829 ++customCtxIndex)
14830 {
14831 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14832 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14833
14834 if(!pBlockVectorCtx->hasDefragmentationPlan)
14835 {
14836 res = VK_NOT_READY;
14837 continue;
14838 }
14839
14840 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
14841 pBlockVectorCtx, m_pStats);
14842
14843 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
14844 res = VK_NOT_READY;
14845 }
14846
14847 return res;
14848 }
14849
14850 ////////////////////////////////////////////////////////////////////////////////
14851 // VmaRecorder
14852
14853 #if VMA_RECORDING_ENABLED
14854
VmaRecorder()14855 VmaRecorder::VmaRecorder() :
14856 m_UseMutex(true),
14857 m_Flags(0),
14858 m_File(VMA_NULL),
14859 m_RecordingStartTime(std::chrono::high_resolution_clock::now())
14860 {
14861 }
14862
Init(const VmaRecordSettings & settings,bool useMutex)14863 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
14864 {
14865 m_UseMutex = useMutex;
14866 m_Flags = settings.flags;
14867
14868 #if defined(_WIN32)
14869 // Open file for writing.
14870 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
14871
14872 if(err != 0)
14873 {
14874 return VK_ERROR_INITIALIZATION_FAILED;
14875 }
14876 #else
14877 // Open file for writing.
14878 m_File = fopen(settings.pFilePath, "wb");
14879
14880 if(m_File == 0)
14881 {
14882 return VK_ERROR_INITIALIZATION_FAILED;
14883 }
14884 #endif
14885
14886 // Write header.
14887 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
14888 fprintf(m_File, "%s\n", "1,8");
14889
14890 return VK_SUCCESS;
14891 }
14892
~VmaRecorder()14893 VmaRecorder::~VmaRecorder()
14894 {
14895 if(m_File != VMA_NULL)
14896 {
14897 fclose(m_File);
14898 }
14899 }
14900
RecordCreateAllocator(uint32_t frameIndex)14901 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
14902 {
14903 CallParams callParams;
14904 GetBasicParams(callParams);
14905
14906 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14907 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
14908 Flush();
14909 }
14910
RecordDestroyAllocator(uint32_t frameIndex)14911 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
14912 {
14913 CallParams callParams;
14914 GetBasicParams(callParams);
14915
14916 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14917 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
14918 Flush();
14919 }
14920
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)14921 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
14922 {
14923 CallParams callParams;
14924 GetBasicParams(callParams);
14925
14926 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14927 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
14928 createInfo.memoryTypeIndex,
14929 createInfo.flags,
14930 createInfo.blockSize,
14931 (uint64_t)createInfo.minBlockCount,
14932 (uint64_t)createInfo.maxBlockCount,
14933 createInfo.frameInUseCount,
14934 pool);
14935 Flush();
14936 }
14937
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)14938 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
14939 {
14940 CallParams callParams;
14941 GetBasicParams(callParams);
14942
14943 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14944 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
14945 pool);
14946 Flush();
14947 }
14948
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)14949 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
14950 const VkMemoryRequirements& vkMemReq,
14951 const VmaAllocationCreateInfo& createInfo,
14952 VmaAllocation allocation)
14953 {
14954 CallParams callParams;
14955 GetBasicParams(callParams);
14956
14957 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14958 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14959 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
14960 vkMemReq.size,
14961 vkMemReq.alignment,
14962 vkMemReq.memoryTypeBits,
14963 createInfo.flags,
14964 createInfo.usage,
14965 createInfo.requiredFlags,
14966 createInfo.preferredFlags,
14967 createInfo.memoryTypeBits,
14968 createInfo.pool,
14969 allocation,
14970 userDataStr.GetString());
14971 Flush();
14972 }
14973
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)14974 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
14975 const VkMemoryRequirements& vkMemReq,
14976 const VmaAllocationCreateInfo& createInfo,
14977 uint64_t allocationCount,
14978 const VmaAllocation* pAllocations)
14979 {
14980 CallParams callParams;
14981 GetBasicParams(callParams);
14982
14983 VmaMutexLock lock(m_FileMutex, m_UseMutex);
14984 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
14985 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
14986 vkMemReq.size,
14987 vkMemReq.alignment,
14988 vkMemReq.memoryTypeBits,
14989 createInfo.flags,
14990 createInfo.usage,
14991 createInfo.requiredFlags,
14992 createInfo.preferredFlags,
14993 createInfo.memoryTypeBits,
14994 createInfo.pool);
14995 PrintPointerList(allocationCount, pAllocations);
14996 fprintf(m_File, ",%s\n", userDataStr.GetString());
14997 Flush();
14998 }
14999
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15000 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15001 const VkMemoryRequirements& vkMemReq,
15002 bool requiresDedicatedAllocation,
15003 bool prefersDedicatedAllocation,
15004 const VmaAllocationCreateInfo& createInfo,
15005 VmaAllocation allocation)
15006 {
15007 CallParams callParams;
15008 GetBasicParams(callParams);
15009
15010 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15011 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15012 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,
15013 vkMemReq.size,
15014 vkMemReq.alignment,
15015 vkMemReq.memoryTypeBits,
15016 requiresDedicatedAllocation ? 1 : 0,
15017 prefersDedicatedAllocation ? 1 : 0,
15018 createInfo.flags,
15019 createInfo.usage,
15020 createInfo.requiredFlags,
15021 createInfo.preferredFlags,
15022 createInfo.memoryTypeBits,
15023 createInfo.pool,
15024 allocation,
15025 userDataStr.GetString());
15026 Flush();
15027 }
15028
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15029 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15030 const VkMemoryRequirements& vkMemReq,
15031 bool requiresDedicatedAllocation,
15032 bool prefersDedicatedAllocation,
15033 const VmaAllocationCreateInfo& createInfo,
15034 VmaAllocation allocation)
15035 {
15036 CallParams callParams;
15037 GetBasicParams(callParams);
15038
15039 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15040 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15041 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,
15042 vkMemReq.size,
15043 vkMemReq.alignment,
15044 vkMemReq.memoryTypeBits,
15045 requiresDedicatedAllocation ? 1 : 0,
15046 prefersDedicatedAllocation ? 1 : 0,
15047 createInfo.flags,
15048 createInfo.usage,
15049 createInfo.requiredFlags,
15050 createInfo.preferredFlags,
15051 createInfo.memoryTypeBits,
15052 createInfo.pool,
15053 allocation,
15054 userDataStr.GetString());
15055 Flush();
15056 }
15057
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15058 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15059 VmaAllocation allocation)
15060 {
15061 CallParams callParams;
15062 GetBasicParams(callParams);
15063
15064 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15065 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15066 allocation);
15067 Flush();
15068 }
15069
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15070 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15071 uint64_t allocationCount,
15072 const VmaAllocation* pAllocations)
15073 {
15074 CallParams callParams;
15075 GetBasicParams(callParams);
15076
15077 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15078 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15079 PrintPointerList(allocationCount, pAllocations);
15080 fprintf(m_File, "\n");
15081 Flush();
15082 }
15083
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15084 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15085 VmaAllocation allocation,
15086 const void* pUserData)
15087 {
15088 CallParams callParams;
15089 GetBasicParams(callParams);
15090
15091 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15092 UserDataString userDataStr(
15093 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15094 pUserData);
15095 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15096 allocation,
15097 userDataStr.GetString());
15098 Flush();
15099 }
15100
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15101 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15102 VmaAllocation allocation)
15103 {
15104 CallParams callParams;
15105 GetBasicParams(callParams);
15106
15107 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15108 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15109 allocation);
15110 Flush();
15111 }
15112
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15113 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15114 VmaAllocation allocation)
15115 {
15116 CallParams callParams;
15117 GetBasicParams(callParams);
15118
15119 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15120 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15121 allocation);
15122 Flush();
15123 }
15124
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15125 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15126 VmaAllocation allocation)
15127 {
15128 CallParams callParams;
15129 GetBasicParams(callParams);
15130
15131 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15132 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15133 allocation);
15134 Flush();
15135 }
15136
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15137 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15138 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15139 {
15140 CallParams callParams;
15141 GetBasicParams(callParams);
15142
15143 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15144 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15145 allocation,
15146 offset,
15147 size);
15148 Flush();
15149 }
15150
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15151 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15152 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15153 {
15154 CallParams callParams;
15155 GetBasicParams(callParams);
15156
15157 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15158 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15159 allocation,
15160 offset,
15161 size);
15162 Flush();
15163 }
15164
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15165 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15166 const VkBufferCreateInfo& bufCreateInfo,
15167 const VmaAllocationCreateInfo& allocCreateInfo,
15168 VmaAllocation allocation)
15169 {
15170 CallParams callParams;
15171 GetBasicParams(callParams);
15172
15173 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15174 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15175 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,
15176 bufCreateInfo.flags,
15177 bufCreateInfo.size,
15178 bufCreateInfo.usage,
15179 bufCreateInfo.sharingMode,
15180 allocCreateInfo.flags,
15181 allocCreateInfo.usage,
15182 allocCreateInfo.requiredFlags,
15183 allocCreateInfo.preferredFlags,
15184 allocCreateInfo.memoryTypeBits,
15185 allocCreateInfo.pool,
15186 allocation,
15187 userDataStr.GetString());
15188 Flush();
15189 }
15190
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15191 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15192 const VkImageCreateInfo& imageCreateInfo,
15193 const VmaAllocationCreateInfo& allocCreateInfo,
15194 VmaAllocation allocation)
15195 {
15196 CallParams callParams;
15197 GetBasicParams(callParams);
15198
15199 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15200 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15201 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,
15202 imageCreateInfo.flags,
15203 imageCreateInfo.imageType,
15204 imageCreateInfo.format,
15205 imageCreateInfo.extent.width,
15206 imageCreateInfo.extent.height,
15207 imageCreateInfo.extent.depth,
15208 imageCreateInfo.mipLevels,
15209 imageCreateInfo.arrayLayers,
15210 imageCreateInfo.samples,
15211 imageCreateInfo.tiling,
15212 imageCreateInfo.usage,
15213 imageCreateInfo.sharingMode,
15214 imageCreateInfo.initialLayout,
15215 allocCreateInfo.flags,
15216 allocCreateInfo.usage,
15217 allocCreateInfo.requiredFlags,
15218 allocCreateInfo.preferredFlags,
15219 allocCreateInfo.memoryTypeBits,
15220 allocCreateInfo.pool,
15221 allocation,
15222 userDataStr.GetString());
15223 Flush();
15224 }
15225
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15226 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15227 VmaAllocation allocation)
15228 {
15229 CallParams callParams;
15230 GetBasicParams(callParams);
15231
15232 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15233 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15234 allocation);
15235 Flush();
15236 }
15237
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15238 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15239 VmaAllocation allocation)
15240 {
15241 CallParams callParams;
15242 GetBasicParams(callParams);
15243
15244 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15245 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15246 allocation);
15247 Flush();
15248 }
15249
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15250 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15251 VmaAllocation allocation)
15252 {
15253 CallParams callParams;
15254 GetBasicParams(callParams);
15255
15256 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15257 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15258 allocation);
15259 Flush();
15260 }
15261
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15262 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15263 VmaAllocation allocation)
15264 {
15265 CallParams callParams;
15266 GetBasicParams(callParams);
15267
15268 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15269 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15270 allocation);
15271 Flush();
15272 }
15273
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15274 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15275 VmaPool pool)
15276 {
15277 CallParams callParams;
15278 GetBasicParams(callParams);
15279
15280 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15281 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15282 pool);
15283 Flush();
15284 }
15285
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15286 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15287 const VmaDefragmentationInfo2& info,
15288 VmaDefragmentationContext ctx)
15289 {
15290 CallParams callParams;
15291 GetBasicParams(callParams);
15292
15293 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15294 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15295 info.flags);
15296 PrintPointerList(info.allocationCount, info.pAllocations);
15297 fprintf(m_File, ",");
15298 PrintPointerList(info.poolCount, info.pPools);
15299 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15300 info.maxCpuBytesToMove,
15301 info.maxCpuAllocationsToMove,
15302 info.maxGpuBytesToMove,
15303 info.maxGpuAllocationsToMove,
15304 info.commandBuffer,
15305 ctx);
15306 Flush();
15307 }
15308
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15309 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15310 VmaDefragmentationContext ctx)
15311 {
15312 CallParams callParams;
15313 GetBasicParams(callParams);
15314
15315 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15316 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15317 ctx);
15318 Flush();
15319 }
15320
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15321 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15322 VmaPool pool,
15323 const char* name)
15324 {
15325 CallParams callParams;
15326 GetBasicParams(callParams);
15327
15328 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15329 fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15330 pool, name != VMA_NULL ? name : "");
15331 Flush();
15332 }
15333
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15334 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15335 {
15336 if(pUserData != VMA_NULL)
15337 {
15338 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15339 {
15340 m_Str = (const char*)pUserData;
15341 }
15342 else
15343 {
15344 // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15345 snprintf(m_PtrStr, 17, "%p", pUserData);
15346 m_Str = m_PtrStr;
15347 }
15348 }
15349 else
15350 {
15351 m_Str = "";
15352 }
15353 }
15354
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15355 void VmaRecorder::WriteConfiguration(
15356 const VkPhysicalDeviceProperties& devProps,
15357 const VkPhysicalDeviceMemoryProperties& memProps,
15358 uint32_t vulkanApiVersion,
15359 bool dedicatedAllocationExtensionEnabled,
15360 bool bindMemory2ExtensionEnabled,
15361 bool memoryBudgetExtensionEnabled,
15362 bool deviceCoherentMemoryExtensionEnabled)
15363 {
15364 fprintf(m_File, "Config,Begin\n");
15365
15366 fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15367
15368 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15369 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15370 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15371 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15372 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15373 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15374
15375 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15376 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15377 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15378
15379 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15380 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15381 {
15382 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15383 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15384 }
15385 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15386 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15387 {
15388 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15389 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15390 }
15391
15392 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15393 fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15394 fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15395 fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15396
15397 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15398 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15399 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15400 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15401 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15402 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15403 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15404 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15405 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15406
15407 fprintf(m_File, "Config,End\n");
15408 }
15409
GetBasicParams(CallParams & outParams)15410 void VmaRecorder::GetBasicParams(CallParams& outParams)
15411 {
15412 #if defined(_WIN32)
15413 outParams.threadId = GetCurrentThreadId();
15414 #else
15415 // Use C++11 features to get thread id and convert it to uint32_t.
15416 // There is room for optimization since sstream is quite slow.
15417 // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15418 std::thread::id thread_id = std::this_thread::get_id();
15419 stringstream thread_id_to_string_converter;
15420 thread_id_to_string_converter << thread_id;
15421 string thread_id_as_string = thread_id_to_string_converter.str();
15422 outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15423 #endif
15424
15425 auto current_time = std::chrono::high_resolution_clock::now();
15426
15427 outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15428 }
15429
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15430 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15431 {
15432 if(count)
15433 {
15434 fprintf(m_File, "%p", pItems[0]);
15435 for(uint64_t i = 1; i < count; ++i)
15436 {
15437 fprintf(m_File, " %p", pItems[i]);
15438 }
15439 }
15440 }
15441
Flush()15442 void VmaRecorder::Flush()
15443 {
15444 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15445 {
15446 fflush(m_File);
15447 }
15448 }
15449
15450 #endif // #if VMA_RECORDING_ENABLED
15451
15452 ////////////////////////////////////////////////////////////////////////////////
15453 // VmaAllocationObjectAllocator
15454
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15455 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15456 m_Allocator(pAllocationCallbacks, 1024)
15457 {
15458 }
15459
Allocate(Types...args)15460 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15461 {
15462 VmaMutexLock mutexLock(m_Mutex);
15463 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15464 }
15465
Free(VmaAllocation hAlloc)15466 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15467 {
15468 VmaMutexLock mutexLock(m_Mutex);
15469 m_Allocator.Free(hAlloc);
15470 }
15471
15472 ////////////////////////////////////////////////////////////////////////////////
15473 // VmaAllocator_T
15474
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15475 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15476 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15477 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15478 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15479 m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15480 m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15481 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15482 m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15483 m_hDevice(pCreateInfo->device),
15484 m_hInstance(pCreateInfo->instance),
15485 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15486 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15487 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15488 m_AllocationObjectAllocator(&m_AllocationCallbacks),
15489 m_HeapSizeLimitMask(0),
15490 m_PreferredLargeHeapBlockSize(0),
15491 m_PhysicalDevice(pCreateInfo->physicalDevice),
15492 m_CurrentFrameIndex(0),
15493 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15494 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15495 m_NextPoolId(0),
15496 m_GlobalMemoryTypeBits(UINT32_MAX)
15497 #if VMA_RECORDING_ENABLED
15498 ,m_pRecorder(VMA_NULL)
15499 #endif
15500 {
15501 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15502 {
15503 m_UseKhrDedicatedAllocation = false;
15504 m_UseKhrBindMemory2 = false;
15505 }
15506
15507 if(VMA_DEBUG_DETECT_CORRUPTION)
15508 {
15509 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15510 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15511 }
15512
15513 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15514
15515 if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15516 {
15517 #if !(VMA_DEDICATED_ALLOCATION)
15518 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15519 {
15520 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15521 }
15522 #endif
15523 #if !(VMA_BIND_MEMORY2)
15524 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15525 {
15526 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15527 }
15528 #endif
15529 }
15530 #if !(VMA_MEMORY_BUDGET)
15531 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15532 {
15533 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15534 }
15535 #endif
15536 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15537 if(m_UseKhrBufferDeviceAddress)
15538 {
15539 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
15540 }
15541 #endif
15542 #if VMA_VULKAN_VERSION < 1002000
15543 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15544 {
15545 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15546 }
15547 #endif
15548 #if VMA_VULKAN_VERSION < 1001000
15549 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15550 {
15551 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15552 }
15553 #endif
15554
15555 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15556 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15557 memset(&m_MemProps, 0, sizeof(m_MemProps));
15558
15559 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15560 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15561 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15562
15563 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15564 {
15565 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15566 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15567 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15568 }
15569
15570 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15571
15572 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15573 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15574
15575 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15576 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15577 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15578 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15579
15580 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15581 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15582
15583 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15584
15585 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15586 {
15587 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15588 {
15589 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15590 if(limit != VK_WHOLE_SIZE)
15591 {
15592 m_HeapSizeLimitMask |= 1u << heapIndex;
15593 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15594 {
15595 m_MemProps.memoryHeaps[heapIndex].size = limit;
15596 }
15597 }
15598 }
15599 }
15600
15601 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15602 {
15603 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15604
15605 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15606 this,
15607 VK_NULL_HANDLE, // hParentPool
15608 memTypeIndex,
15609 preferredBlockSize,
15610 0,
15611 SIZE_MAX,
15612 GetBufferImageGranularity(),
15613 pCreateInfo->frameInUseCount,
15614 false, // explicitBlockSize
15615 false); // linearAlgorithm
15616 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15617 // becase minBlockCount is 0.
15618 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15619
15620 }
15621 }
15622
Init(const VmaAllocatorCreateInfo * pCreateInfo)15623 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15624 {
15625 VkResult res = VK_SUCCESS;
15626
15627 if(pCreateInfo->pRecordSettings != VMA_NULL &&
15628 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15629 {
15630 #if VMA_RECORDING_ENABLED
15631 m_pRecorder = vma_new(this, VmaRecorder)();
15632 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15633 if(res != VK_SUCCESS)
15634 {
15635 return res;
15636 }
15637 m_pRecorder->WriteConfiguration(
15638 m_PhysicalDeviceProperties,
15639 m_MemProps,
15640 m_VulkanApiVersion,
15641 m_UseKhrDedicatedAllocation,
15642 m_UseKhrBindMemory2,
15643 m_UseExtMemoryBudget,
15644 m_UseAmdDeviceCoherentMemory);
15645 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15646 #else
15647 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15648 return VK_ERROR_FEATURE_NOT_PRESENT;
15649 #endif
15650 }
15651
15652 #if VMA_MEMORY_BUDGET
15653 if(m_UseExtMemoryBudget)
15654 {
15655 UpdateVulkanBudget();
15656 }
15657 #endif // #if VMA_MEMORY_BUDGET
15658
15659 return res;
15660 }
15661
~VmaAllocator_T()15662 VmaAllocator_T::~VmaAllocator_T()
15663 {
15664 #if VMA_RECORDING_ENABLED
15665 if(m_pRecorder != VMA_NULL)
15666 {
15667 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15668 vma_delete(this, m_pRecorder);
15669 }
15670 #endif
15671
15672 VMA_ASSERT(m_Pools.empty());
15673
15674 for(size_t i = GetMemoryTypeCount(); i--; )
15675 {
15676 if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15677 {
15678 VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15679 }
15680
15681 vma_delete(this, m_pDedicatedAllocations[i]);
15682 vma_delete(this, m_pBlockVectors[i]);
15683 }
15684 }
15685
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15686 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15687 {
15688 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15689 ImportVulkanFunctions_Static();
15690 #endif
15691
15692 if(pVulkanFunctions != VMA_NULL)
15693 {
15694 ImportVulkanFunctions_Custom(pVulkanFunctions);
15695 }
15696
15697 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15698 ImportVulkanFunctions_Dynamic();
15699 #endif
15700
15701 ValidateVulkanFunctions();
15702 }
15703
15704 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15705
ImportVulkanFunctions_Static()15706 void VmaAllocator_T::ImportVulkanFunctions_Static()
15707 {
15708 // Vulkan 1.0
15709 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15710 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15711 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15712 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15713 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15714 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15715 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15716 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15717 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15718 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15719 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15720 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15721 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15722 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15723 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15724 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15725 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15726
15727 // Vulkan 1.1
15728 #if VMA_VULKAN_VERSION >= 1001000
15729 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15730 {
15731 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15732 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15733 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15734 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15735 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15736 }
15737 #endif
15738 }
15739
15740 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15741
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15742 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15743 {
15744 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15745
15746 #define VMA_COPY_IF_NOT_NULL(funcName) \
15747 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15748
15749 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15750 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15751 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15752 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15753 VMA_COPY_IF_NOT_NULL(vkMapMemory);
15754 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15755 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15756 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15757 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15758 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15759 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15760 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15761 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15762 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15763 VMA_COPY_IF_NOT_NULL(vkCreateImage);
15764 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15765 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15766
15767 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15768 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15769 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15770 #endif
15771
15772 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15773 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15774 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15775 #endif
15776
15777 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15778 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15779 #endif
15780
15781 #undef VMA_COPY_IF_NOT_NULL
15782 }
15783
15784 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15785
ImportVulkanFunctions_Dynamic()15786 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
15787 {
15788 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
15789 if(m_VulkanFunctions.memberName == VMA_NULL) \
15790 m_VulkanFunctions.memberName = \
15791 (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
15792 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
15793 if(m_VulkanFunctions.memberName == VMA_NULL) \
15794 m_VulkanFunctions.memberName = \
15795 (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
15796
15797 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
15798 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
15799 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
15800 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
15801 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
15802 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
15803 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
15804 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
15805 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
15806 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
15807 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
15808 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
15809 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
15810 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
15811 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
15812 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
15813 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
15814
15815 #if VMA_DEDICATED_ALLOCATION
15816 if(m_UseKhrDedicatedAllocation)
15817 {
15818 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
15819 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
15820 }
15821 #endif
15822
15823 #if VMA_BIND_MEMORY2
15824 if(m_UseKhrBindMemory2)
15825 {
15826 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
15827 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
15828 }
15829 #endif // #if VMA_BIND_MEMORY2
15830
15831 #if VMA_MEMORY_BUDGET
15832 if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15833 {
15834 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
15835 }
15836 #endif // #if VMA_MEMORY_BUDGET
15837
15838 #undef VMA_FETCH_DEVICE_FUNC
15839 #undef VMA_FETCH_INSTANCE_FUNC
15840 }
15841
15842 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15843
ValidateVulkanFunctions()15844 void VmaAllocator_T::ValidateVulkanFunctions()
15845 {
15846 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
15847 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
15848 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
15849 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
15850 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
15851 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
15852 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
15853 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
15854 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
15855 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
15856 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
15857 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
15858 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
15859 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
15860 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
15861 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
15862 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
15863
15864 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15865 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
15866 {
15867 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
15868 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
15869 }
15870 #endif
15871
15872 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15873 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
15874 {
15875 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
15876 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
15877 }
15878 #endif
15879
15880 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15881 if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15882 {
15883 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
15884 }
15885 #endif
15886 }
15887
CalcPreferredBlockSize(uint32_t memTypeIndex)15888 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
15889 {
15890 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
15891 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15892 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
15893 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
15894 }
15895
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15896 VkResult VmaAllocator_T::AllocateMemoryOfType(
15897 VkDeviceSize size,
15898 VkDeviceSize alignment,
15899 bool dedicatedAllocation,
15900 VkBuffer dedicatedBuffer,
15901 VkBufferUsageFlags dedicatedBufferUsage,
15902 VkImage dedicatedImage,
15903 const VmaAllocationCreateInfo& createInfo,
15904 uint32_t memTypeIndex,
15905 VmaSuballocationType suballocType,
15906 size_t allocationCount,
15907 VmaAllocation* pAllocations)
15908 {
15909 VMA_ASSERT(pAllocations != VMA_NULL);
15910 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
15911
15912 VmaAllocationCreateInfo finalCreateInfo = createInfo;
15913
15914 // If memory type is not HOST_VISIBLE, disable MAPPED.
15915 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
15916 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15917 {
15918 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
15919 }
15920 // If memory is lazily allocated, it should be always dedicated.
15921 if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
15922 {
15923 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15924 }
15925
15926 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
15927 VMA_ASSERT(blockVector);
15928
15929 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
15930 bool preferDedicatedMemory =
15931 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
15932 dedicatedAllocation ||
15933 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
15934 size > preferredBlockSize / 2;
15935
15936 if(preferDedicatedMemory &&
15937 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
15938 finalCreateInfo.pool == VK_NULL_HANDLE)
15939 {
15940 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
15941 }
15942
15943 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
15944 {
15945 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15946 {
15947 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15948 }
15949 else
15950 {
15951 return AllocateDedicatedMemory(
15952 size,
15953 suballocType,
15954 memTypeIndex,
15955 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15956 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15957 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15958 finalCreateInfo.pUserData,
15959 dedicatedBuffer,
15960 dedicatedBufferUsage,
15961 dedicatedImage,
15962 allocationCount,
15963 pAllocations);
15964 }
15965 }
15966 else
15967 {
15968 VkResult res = blockVector->Allocate(
15969 m_CurrentFrameIndex.load(),
15970 size,
15971 alignment,
15972 finalCreateInfo,
15973 suballocType,
15974 allocationCount,
15975 pAllocations);
15976 if(res == VK_SUCCESS)
15977 {
15978 return res;
15979 }
15980
15981 // 5. Try dedicated memory.
15982 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
15983 {
15984 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15985 }
15986 else
15987 {
15988 res = AllocateDedicatedMemory(
15989 size,
15990 suballocType,
15991 memTypeIndex,
15992 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
15993 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
15994 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
15995 finalCreateInfo.pUserData,
15996 dedicatedBuffer,
15997 dedicatedBufferUsage,
15998 dedicatedImage,
15999 allocationCount,
16000 pAllocations);
16001 if(res == VK_SUCCESS)
16002 {
16003 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16004 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16005 return VK_SUCCESS;
16006 }
16007 else
16008 {
16009 // Everything failed: Return error code.
16010 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16011 return res;
16012 }
16013 }
16014 }
16015 }
16016
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool withinBudget,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)16017 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16018 VkDeviceSize size,
16019 VmaSuballocationType suballocType,
16020 uint32_t memTypeIndex,
16021 bool withinBudget,
16022 bool map,
16023 bool isUserDataString,
16024 void* pUserData,
16025 VkBuffer dedicatedBuffer,
16026 VkBufferUsageFlags dedicatedBufferUsage,
16027 VkImage dedicatedImage,
16028 size_t allocationCount,
16029 VmaAllocation* pAllocations)
16030 {
16031 VMA_ASSERT(allocationCount > 0 && pAllocations);
16032
16033 if(withinBudget)
16034 {
16035 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16036 VmaBudget heapBudget = {};
16037 GetBudget(&heapBudget, heapIndex, 1);
16038 if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16039 {
16040 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16041 }
16042 }
16043
16044 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16045 allocInfo.memoryTypeIndex = memTypeIndex;
16046 allocInfo.allocationSize = size;
16047
16048 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16049 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16050 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16051 {
16052 if(dedicatedBuffer != VK_NULL_HANDLE)
16053 {
16054 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16055 dedicatedAllocInfo.buffer = dedicatedBuffer;
16056 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16057 }
16058 else if(dedicatedImage != VK_NULL_HANDLE)
16059 {
16060 dedicatedAllocInfo.image = dedicatedImage;
16061 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16062 }
16063 }
16064 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16065
16066 #if VMA_BUFFER_DEVICE_ADDRESS
16067 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16068 if(m_UseKhrBufferDeviceAddress)
16069 {
16070 bool canContainBufferWithDeviceAddress = true;
16071 if(dedicatedBuffer != VK_NULL_HANDLE)
16072 {
16073 canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16074 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16075 }
16076 else if(dedicatedImage != VK_NULL_HANDLE)
16077 {
16078 canContainBufferWithDeviceAddress = false;
16079 }
16080 if(canContainBufferWithDeviceAddress)
16081 {
16082 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16083 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16084 }
16085 }
16086 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16087
16088 size_t allocIndex;
16089 VkResult res = VK_SUCCESS;
16090 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16091 {
16092 res = AllocateDedicatedMemoryPage(
16093 size,
16094 suballocType,
16095 memTypeIndex,
16096 allocInfo,
16097 map,
16098 isUserDataString,
16099 pUserData,
16100 pAllocations + allocIndex);
16101 if(res != VK_SUCCESS)
16102 {
16103 break;
16104 }
16105 }
16106
16107 if(res == VK_SUCCESS)
16108 {
16109 // Register them in m_pDedicatedAllocations.
16110 {
16111 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16112 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16113 VMA_ASSERT(pDedicatedAllocations);
16114 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16115 {
16116 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16117 }
16118 }
16119
16120 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16121 }
16122 else
16123 {
16124 // Free all already created allocations.
16125 while(allocIndex--)
16126 {
16127 VmaAllocation currAlloc = pAllocations[allocIndex];
16128 VkDeviceMemory hMemory = currAlloc->GetMemory();
16129
16130 /*
16131 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16132 before vkFreeMemory.
16133
16134 if(currAlloc->GetMappedData() != VMA_NULL)
16135 {
16136 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16137 }
16138 */
16139
16140 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16141 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16142 currAlloc->SetUserData(this, VMA_NULL);
16143 m_AllocationObjectAllocator.Free(currAlloc);
16144 }
16145
16146 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16147 }
16148
16149 return res;
16150 }
16151
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16152 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16153 VkDeviceSize size,
16154 VmaSuballocationType suballocType,
16155 uint32_t memTypeIndex,
16156 const VkMemoryAllocateInfo& allocInfo,
16157 bool map,
16158 bool isUserDataString,
16159 void* pUserData,
16160 VmaAllocation* pAllocation)
16161 {
16162 VkDeviceMemory hMemory = VK_NULL_HANDLE;
16163 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16164 if(res < 0)
16165 {
16166 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16167 return res;
16168 }
16169
16170 void* pMappedData = VMA_NULL;
16171 if(map)
16172 {
16173 res = (*m_VulkanFunctions.vkMapMemory)(
16174 m_hDevice,
16175 hMemory,
16176 0,
16177 VK_WHOLE_SIZE,
16178 0,
16179 &pMappedData);
16180 if(res < 0)
16181 {
16182 VMA_DEBUG_LOG(" vkMapMemory FAILED");
16183 FreeVulkanMemory(memTypeIndex, size, hMemory);
16184 return res;
16185 }
16186 }
16187
16188 *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16189 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16190 (*pAllocation)->SetUserData(this, pUserData);
16191 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16192 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16193 {
16194 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16195 }
16196
16197 return VK_SUCCESS;
16198 }
16199
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16200 void VmaAllocator_T::GetBufferMemoryRequirements(
16201 VkBuffer hBuffer,
16202 VkMemoryRequirements& memReq,
16203 bool& requiresDedicatedAllocation,
16204 bool& prefersDedicatedAllocation) const
16205 {
16206 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16207 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16208 {
16209 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16210 memReqInfo.buffer = hBuffer;
16211
16212 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16213
16214 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16215 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16216
16217 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16218
16219 memReq = memReq2.memoryRequirements;
16220 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16221 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16222 }
16223 else
16224 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16225 {
16226 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16227 requiresDedicatedAllocation = false;
16228 prefersDedicatedAllocation = false;
16229 }
16230 }
16231
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16232 void VmaAllocator_T::GetImageMemoryRequirements(
16233 VkImage hImage,
16234 VkMemoryRequirements& memReq,
16235 bool& requiresDedicatedAllocation,
16236 bool& prefersDedicatedAllocation) const
16237 {
16238 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16239 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16240 {
16241 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16242 memReqInfo.image = hImage;
16243
16244 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16245
16246 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16247 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16248
16249 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16250
16251 memReq = memReq2.memoryRequirements;
16252 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16253 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16254 }
16255 else
16256 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16257 {
16258 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16259 requiresDedicatedAllocation = false;
16260 prefersDedicatedAllocation = false;
16261 }
16262 }
16263
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16264 VkResult VmaAllocator_T::AllocateMemory(
16265 const VkMemoryRequirements& vkMemReq,
16266 bool requiresDedicatedAllocation,
16267 bool prefersDedicatedAllocation,
16268 VkBuffer dedicatedBuffer,
16269 VkBufferUsageFlags dedicatedBufferUsage,
16270 VkImage dedicatedImage,
16271 const VmaAllocationCreateInfo& createInfo,
16272 VmaSuballocationType suballocType,
16273 size_t allocationCount,
16274 VmaAllocation* pAllocations)
16275 {
16276 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16277
16278 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16279
16280 if(vkMemReq.size == 0)
16281 {
16282 return VK_ERROR_VALIDATION_FAILED_EXT;
16283 }
16284 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16285 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16286 {
16287 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16288 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16289 }
16290 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16291 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16292 {
16293 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16294 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16295 }
16296 if(requiresDedicatedAllocation)
16297 {
16298 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16299 {
16300 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16301 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16302 }
16303 if(createInfo.pool != VK_NULL_HANDLE)
16304 {
16305 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16306 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16307 }
16308 }
16309 if((createInfo.pool != VK_NULL_HANDLE) &&
16310 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16311 {
16312 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16313 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16314 }
16315
16316 if(createInfo.pool != VK_NULL_HANDLE)
16317 {
16318 const VkDeviceSize alignmentForPool = VMA_MAX(
16319 vkMemReq.alignment,
16320 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16321
16322 VmaAllocationCreateInfo createInfoForPool = createInfo;
16323 // If memory type is not HOST_VISIBLE, disable MAPPED.
16324 if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16325 (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16326 {
16327 createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16328 }
16329
16330 return createInfo.pool->m_BlockVector.Allocate(
16331 m_CurrentFrameIndex.load(),
16332 vkMemReq.size,
16333 alignmentForPool,
16334 createInfoForPool,
16335 suballocType,
16336 allocationCount,
16337 pAllocations);
16338 }
16339 else
16340 {
16341 // Bit mask of memory Vulkan types acceptable for this allocation.
16342 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16343 uint32_t memTypeIndex = UINT32_MAX;
16344 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16345 if(res == VK_SUCCESS)
16346 {
16347 VkDeviceSize alignmentForMemType = VMA_MAX(
16348 vkMemReq.alignment,
16349 GetMemoryTypeMinAlignment(memTypeIndex));
16350
16351 res = AllocateMemoryOfType(
16352 vkMemReq.size,
16353 alignmentForMemType,
16354 requiresDedicatedAllocation || prefersDedicatedAllocation,
16355 dedicatedBuffer,
16356 dedicatedBufferUsage,
16357 dedicatedImage,
16358 createInfo,
16359 memTypeIndex,
16360 suballocType,
16361 allocationCount,
16362 pAllocations);
16363 // Succeeded on first try.
16364 if(res == VK_SUCCESS)
16365 {
16366 return res;
16367 }
16368 // Allocation from this memory type failed. Try other compatible memory types.
16369 else
16370 {
16371 for(;;)
16372 {
16373 // Remove old memTypeIndex from list of possibilities.
16374 memoryTypeBits &= ~(1u << memTypeIndex);
16375 // Find alternative memTypeIndex.
16376 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16377 if(res == VK_SUCCESS)
16378 {
16379 alignmentForMemType = VMA_MAX(
16380 vkMemReq.alignment,
16381 GetMemoryTypeMinAlignment(memTypeIndex));
16382
16383 res = AllocateMemoryOfType(
16384 vkMemReq.size,
16385 alignmentForMemType,
16386 requiresDedicatedAllocation || prefersDedicatedAllocation,
16387 dedicatedBuffer,
16388 dedicatedBufferUsage,
16389 dedicatedImage,
16390 createInfo,
16391 memTypeIndex,
16392 suballocType,
16393 allocationCount,
16394 pAllocations);
16395 // Allocation from this alternative memory type succeeded.
16396 if(res == VK_SUCCESS)
16397 {
16398 return res;
16399 }
16400 // else: Allocation from this memory type failed. Try next one - next loop iteration.
16401 }
16402 // No other matching memory type index could be found.
16403 else
16404 {
16405 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16406 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16407 }
16408 }
16409 }
16410 }
16411 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16412 else
16413 return res;
16414 }
16415 }
16416
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16417 void VmaAllocator_T::FreeMemory(
16418 size_t allocationCount,
16419 const VmaAllocation* pAllocations)
16420 {
16421 VMA_ASSERT(pAllocations);
16422
16423 for(size_t allocIndex = allocationCount; allocIndex--; )
16424 {
16425 VmaAllocation allocation = pAllocations[allocIndex];
16426
16427 if(allocation != VK_NULL_HANDLE)
16428 {
16429 if(TouchAllocation(allocation))
16430 {
16431 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16432 {
16433 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16434 }
16435
16436 switch(allocation->GetType())
16437 {
16438 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16439 {
16440 VmaBlockVector* pBlockVector = VMA_NULL;
16441 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16442 if(hPool != VK_NULL_HANDLE)
16443 {
16444 pBlockVector = &hPool->m_BlockVector;
16445 }
16446 else
16447 {
16448 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16449 pBlockVector = m_pBlockVectors[memTypeIndex];
16450 }
16451 pBlockVector->Free(allocation);
16452 }
16453 break;
16454 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16455 FreeDedicatedMemory(allocation);
16456 break;
16457 default:
16458 VMA_ASSERT(0);
16459 }
16460 }
16461
16462 // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16463 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16464 allocation->SetUserData(this, VMA_NULL);
16465 m_AllocationObjectAllocator.Free(allocation);
16466 }
16467 }
16468 }
16469
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16470 VkResult VmaAllocator_T::ResizeAllocation(
16471 const VmaAllocation alloc,
16472 VkDeviceSize newSize)
16473 {
16474 // This function is deprecated and so it does nothing. It's left for backward compatibility.
16475 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16476 {
16477 return VK_ERROR_VALIDATION_FAILED_EXT;
16478 }
16479 if(newSize == alloc->GetSize())
16480 {
16481 return VK_SUCCESS;
16482 }
16483 return VK_ERROR_OUT_OF_POOL_MEMORY;
16484 }
16485
CalculateStats(VmaStats * pStats)16486 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16487 {
16488 // Initialize.
16489 InitStatInfo(pStats->total);
16490 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16491 InitStatInfo(pStats->memoryType[i]);
16492 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16493 InitStatInfo(pStats->memoryHeap[i]);
16494
16495 // Process default pools.
16496 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16497 {
16498 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16499 VMA_ASSERT(pBlockVector);
16500 pBlockVector->AddStats(pStats);
16501 }
16502
16503 // Process custom pools.
16504 {
16505 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16506 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16507 {
16508 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16509 }
16510 }
16511
16512 // Process dedicated allocations.
16513 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16514 {
16515 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16516 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16517 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16518 VMA_ASSERT(pDedicatedAllocVector);
16519 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16520 {
16521 VmaStatInfo allocationStatInfo;
16522 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16523 VmaAddStatInfo(pStats->total, allocationStatInfo);
16524 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16525 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16526 }
16527 }
16528
16529 // Postprocess.
16530 VmaPostprocessCalcStatInfo(pStats->total);
16531 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16532 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16533 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16534 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16535 }
16536
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16537 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16538 {
16539 #if VMA_MEMORY_BUDGET
16540 if(m_UseExtMemoryBudget)
16541 {
16542 if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16543 {
16544 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16545 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16546 {
16547 const uint32_t heapIndex = firstHeap + i;
16548
16549 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16550 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16551
16552 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16553 {
16554 outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16555 outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16556 }
16557 else
16558 {
16559 outBudget->usage = 0;
16560 }
16561
16562 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16563 outBudget->budget = VMA_MIN(
16564 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16565 }
16566 }
16567 else
16568 {
16569 UpdateVulkanBudget(); // Outside of mutex lock
16570 GetBudget(outBudget, firstHeap, heapCount); // Recursion
16571 }
16572 }
16573 else
16574 #endif
16575 {
16576 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16577 {
16578 const uint32_t heapIndex = firstHeap + i;
16579
16580 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16581 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16582
16583 outBudget->usage = outBudget->blockBytes;
16584 outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16585 }
16586 }
16587 }
16588
16589 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16590
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16591 VkResult VmaAllocator_T::DefragmentationBegin(
16592 const VmaDefragmentationInfo2& info,
16593 VmaDefragmentationStats* pStats,
16594 VmaDefragmentationContext* pContext)
16595 {
16596 if(info.pAllocationsChanged != VMA_NULL)
16597 {
16598 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16599 }
16600
16601 *pContext = vma_new(this, VmaDefragmentationContext_T)(
16602 this, m_CurrentFrameIndex.load(), info.flags, pStats);
16603
16604 (*pContext)->AddPools(info.poolCount, info.pPools);
16605 (*pContext)->AddAllocations(
16606 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16607
16608 VkResult res = (*pContext)->Defragment(
16609 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16610 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16611 info.commandBuffer, pStats, info.flags);
16612
16613 if(res != VK_NOT_READY)
16614 {
16615 vma_delete(this, *pContext);
16616 *pContext = VMA_NULL;
16617 }
16618
16619 return res;
16620 }
16621
DefragmentationEnd(VmaDefragmentationContext context)16622 VkResult VmaAllocator_T::DefragmentationEnd(
16623 VmaDefragmentationContext context)
16624 {
16625 vma_delete(this, context);
16626 return VK_SUCCESS;
16627 }
16628
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)16629 VkResult VmaAllocator_T::DefragmentationPassBegin(
16630 VmaDefragmentationPassInfo* pInfo,
16631 VmaDefragmentationContext context)
16632 {
16633 return context->DefragmentPassBegin(pInfo);
16634 }
DefragmentationPassEnd(VmaDefragmentationContext context)16635 VkResult VmaAllocator_T::DefragmentationPassEnd(
16636 VmaDefragmentationContext context)
16637 {
16638 return context->DefragmentPassEnd();
16639
16640 }
16641
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)16642 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16643 {
16644 if(hAllocation->CanBecomeLost())
16645 {
16646 /*
16647 Warning: This is a carefully designed algorithm.
16648 Do not modify unless you really know what you're doing :)
16649 */
16650 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16651 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16652 for(;;)
16653 {
16654 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16655 {
16656 pAllocationInfo->memoryType = UINT32_MAX;
16657 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
16658 pAllocationInfo->offset = 0;
16659 pAllocationInfo->size = hAllocation->GetSize();
16660 pAllocationInfo->pMappedData = VMA_NULL;
16661 pAllocationInfo->pUserData = hAllocation->GetUserData();
16662 return;
16663 }
16664 else if(localLastUseFrameIndex == localCurrFrameIndex)
16665 {
16666 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16667 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16668 pAllocationInfo->offset = hAllocation->GetOffset();
16669 pAllocationInfo->size = hAllocation->GetSize();
16670 pAllocationInfo->pMappedData = VMA_NULL;
16671 pAllocationInfo->pUserData = hAllocation->GetUserData();
16672 return;
16673 }
16674 else // Last use time earlier than current time.
16675 {
16676 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16677 {
16678 localLastUseFrameIndex = localCurrFrameIndex;
16679 }
16680 }
16681 }
16682 }
16683 else
16684 {
16685 #if VMA_STATS_STRING_ENABLED
16686 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16687 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16688 for(;;)
16689 {
16690 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16691 if(localLastUseFrameIndex == localCurrFrameIndex)
16692 {
16693 break;
16694 }
16695 else // Last use time earlier than current time.
16696 {
16697 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16698 {
16699 localLastUseFrameIndex = localCurrFrameIndex;
16700 }
16701 }
16702 }
16703 #endif
16704
16705 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
16706 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
16707 pAllocationInfo->offset = hAllocation->GetOffset();
16708 pAllocationInfo->size = hAllocation->GetSize();
16709 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
16710 pAllocationInfo->pUserData = hAllocation->GetUserData();
16711 }
16712 }
16713
TouchAllocation(VmaAllocation hAllocation)16714 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
16715 {
16716 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
16717 if(hAllocation->CanBecomeLost())
16718 {
16719 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16720 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16721 for(;;)
16722 {
16723 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
16724 {
16725 return false;
16726 }
16727 else if(localLastUseFrameIndex == localCurrFrameIndex)
16728 {
16729 return true;
16730 }
16731 else // Last use time earlier than current time.
16732 {
16733 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16734 {
16735 localLastUseFrameIndex = localCurrFrameIndex;
16736 }
16737 }
16738 }
16739 }
16740 else
16741 {
16742 #if VMA_STATS_STRING_ENABLED
16743 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
16744 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
16745 for(;;)
16746 {
16747 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
16748 if(localLastUseFrameIndex == localCurrFrameIndex)
16749 {
16750 break;
16751 }
16752 else // Last use time earlier than current time.
16753 {
16754 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
16755 {
16756 localLastUseFrameIndex = localCurrFrameIndex;
16757 }
16758 }
16759 }
16760 #endif
16761
16762 return true;
16763 }
16764 }
16765
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)16766 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
16767 {
16768 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
16769
16770 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
16771
16772 if(newCreateInfo.maxBlockCount == 0)
16773 {
16774 newCreateInfo.maxBlockCount = SIZE_MAX;
16775 }
16776 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
16777 {
16778 return VK_ERROR_INITIALIZATION_FAILED;
16779 }
16780 // Memory type index out of range or forbidden.
16781 if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
16782 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
16783 {
16784 return VK_ERROR_FEATURE_NOT_PRESENT;
16785 }
16786
16787 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
16788
16789 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
16790
16791 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
16792 if(res != VK_SUCCESS)
16793 {
16794 vma_delete(this, *pPool);
16795 *pPool = VMA_NULL;
16796 return res;
16797 }
16798
16799 // Add to m_Pools.
16800 {
16801 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16802 (*pPool)->SetId(m_NextPoolId++);
16803 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
16804 }
16805
16806 return VK_SUCCESS;
16807 }
16808
DestroyPool(VmaPool pool)16809 void VmaAllocator_T::DestroyPool(VmaPool pool)
16810 {
16811 // Remove from m_Pools.
16812 {
16813 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
16814 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
16815 VMA_ASSERT(success && "Pool not found in Allocator.");
16816 }
16817
16818 vma_delete(this, pool);
16819 }
16820
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)16821 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
16822 {
16823 pool->m_BlockVector.GetPoolStats(pPoolStats);
16824 }
16825
SetCurrentFrameIndex(uint32_t frameIndex)16826 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
16827 {
16828 m_CurrentFrameIndex.store(frameIndex);
16829
16830 #if VMA_MEMORY_BUDGET
16831 if(m_UseExtMemoryBudget)
16832 {
16833 UpdateVulkanBudget();
16834 }
16835 #endif // #if VMA_MEMORY_BUDGET
16836 }
16837
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)16838 void VmaAllocator_T::MakePoolAllocationsLost(
16839 VmaPool hPool,
16840 size_t* pLostAllocationCount)
16841 {
16842 hPool->m_BlockVector.MakePoolAllocationsLost(
16843 m_CurrentFrameIndex.load(),
16844 pLostAllocationCount);
16845 }
16846
CheckPoolCorruption(VmaPool hPool)16847 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
16848 {
16849 return hPool->m_BlockVector.CheckCorruption();
16850 }
16851
CheckCorruption(uint32_t memoryTypeBits)16852 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
16853 {
16854 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
16855
16856 // Process default pools.
16857 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16858 {
16859 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
16860 {
16861 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16862 VMA_ASSERT(pBlockVector);
16863 VkResult localRes = pBlockVector->CheckCorruption();
16864 switch(localRes)
16865 {
16866 case VK_ERROR_FEATURE_NOT_PRESENT:
16867 break;
16868 case VK_SUCCESS:
16869 finalRes = VK_SUCCESS;
16870 break;
16871 default:
16872 return localRes;
16873 }
16874 }
16875 }
16876
16877 // Process custom pools.
16878 {
16879 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16880 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16881 {
16882 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
16883 {
16884 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
16885 switch(localRes)
16886 {
16887 case VK_ERROR_FEATURE_NOT_PRESENT:
16888 break;
16889 case VK_SUCCESS:
16890 finalRes = VK_SUCCESS;
16891 break;
16892 default:
16893 return localRes;
16894 }
16895 }
16896 }
16897 }
16898
16899 return finalRes;
16900 }
16901
CreateLostAllocation(VmaAllocation * pAllocation)16902 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
16903 {
16904 *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
16905 (*pAllocation)->InitLost();
16906 }
16907
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)16908 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
16909 {
16910 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
16911
16912 // HeapSizeLimit is in effect for this heap.
16913 if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
16914 {
16915 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16916 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
16917 for(;;)
16918 {
16919 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
16920 if(blockBytesAfterAllocation > heapSize)
16921 {
16922 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16923 }
16924 if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
16925 {
16926 break;
16927 }
16928 }
16929 }
16930 else
16931 {
16932 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
16933 }
16934
16935 // VULKAN CALL vkAllocateMemory.
16936 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
16937
16938 if(res == VK_SUCCESS)
16939 {
16940 #if VMA_MEMORY_BUDGET
16941 ++m_Budget.m_OperationsSinceBudgetFetch;
16942 #endif
16943
16944 // Informative callback.
16945 if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
16946 {
16947 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
16948 }
16949 }
16950 else
16951 {
16952 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
16953 }
16954
16955 return res;
16956 }
16957
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)16958 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
16959 {
16960 // Informative callback.
16961 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
16962 {
16963 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
16964 }
16965
16966 // VULKAN CALL vkFreeMemory.
16967 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
16968
16969 m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
16970 }
16971
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)16972 VkResult VmaAllocator_T::BindVulkanBuffer(
16973 VkDeviceMemory memory,
16974 VkDeviceSize memoryOffset,
16975 VkBuffer buffer,
16976 const void* pNext)
16977 {
16978 if(pNext != VMA_NULL)
16979 {
16980 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16981 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
16982 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
16983 {
16984 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
16985 bindBufferMemoryInfo.pNext = pNext;
16986 bindBufferMemoryInfo.buffer = buffer;
16987 bindBufferMemoryInfo.memory = memory;
16988 bindBufferMemoryInfo.memoryOffset = memoryOffset;
16989 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
16990 }
16991 else
16992 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
16993 {
16994 return VK_ERROR_EXTENSION_NOT_PRESENT;
16995 }
16996 }
16997 else
16998 {
16999 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17000 }
17001 }
17002
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17003 VkResult VmaAllocator_T::BindVulkanImage(
17004 VkDeviceMemory memory,
17005 VkDeviceSize memoryOffset,
17006 VkImage image,
17007 const void* pNext)
17008 {
17009 if(pNext != VMA_NULL)
17010 {
17011 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17012 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17013 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17014 {
17015 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17016 bindBufferMemoryInfo.pNext = pNext;
17017 bindBufferMemoryInfo.image = image;
17018 bindBufferMemoryInfo.memory = memory;
17019 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17020 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17021 }
17022 else
17023 #endif // #if VMA_BIND_MEMORY2
17024 {
17025 return VK_ERROR_EXTENSION_NOT_PRESENT;
17026 }
17027 }
17028 else
17029 {
17030 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17031 }
17032 }
17033
Map(VmaAllocation hAllocation,void ** ppData)17034 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17035 {
17036 if(hAllocation->CanBecomeLost())
17037 {
17038 return VK_ERROR_MEMORY_MAP_FAILED;
17039 }
17040
17041 switch(hAllocation->GetType())
17042 {
17043 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17044 {
17045 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17046 char *pBytes = VMA_NULL;
17047 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17048 if(res == VK_SUCCESS)
17049 {
17050 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17051 hAllocation->BlockAllocMap();
17052 }
17053 return res;
17054 }
17055 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17056 return hAllocation->DedicatedAllocMap(this, ppData);
17057 default:
17058 VMA_ASSERT(0);
17059 return VK_ERROR_MEMORY_MAP_FAILED;
17060 }
17061 }
17062
Unmap(VmaAllocation hAllocation)17063 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17064 {
17065 switch(hAllocation->GetType())
17066 {
17067 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17068 {
17069 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17070 hAllocation->BlockAllocUnmap();
17071 pBlock->Unmap(this, 1);
17072 }
17073 break;
17074 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17075 hAllocation->DedicatedAllocUnmap(this);
17076 break;
17077 default:
17078 VMA_ASSERT(0);
17079 }
17080 }
17081
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17082 VkResult VmaAllocator_T::BindBufferMemory(
17083 VmaAllocation hAllocation,
17084 VkDeviceSize allocationLocalOffset,
17085 VkBuffer hBuffer,
17086 const void* pNext)
17087 {
17088 VkResult res = VK_SUCCESS;
17089 switch(hAllocation->GetType())
17090 {
17091 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17092 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17093 break;
17094 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17095 {
17096 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17097 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17098 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17099 break;
17100 }
17101 default:
17102 VMA_ASSERT(0);
17103 }
17104 return res;
17105 }
17106
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17107 VkResult VmaAllocator_T::BindImageMemory(
17108 VmaAllocation hAllocation,
17109 VkDeviceSize allocationLocalOffset,
17110 VkImage hImage,
17111 const void* pNext)
17112 {
17113 VkResult res = VK_SUCCESS;
17114 switch(hAllocation->GetType())
17115 {
17116 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17117 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17118 break;
17119 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17120 {
17121 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17122 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17123 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17124 break;
17125 }
17126 default:
17127 VMA_ASSERT(0);
17128 }
17129 return res;
17130 }
17131
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17132 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17133 VmaAllocation hAllocation,
17134 VkDeviceSize offset, VkDeviceSize size,
17135 VMA_CACHE_OPERATION op)
17136 {
17137 VkResult res = VK_SUCCESS;
17138
17139 VkMappedMemoryRange memRange = {};
17140 if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17141 {
17142 switch(op)
17143 {
17144 case VMA_CACHE_FLUSH:
17145 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17146 break;
17147 case VMA_CACHE_INVALIDATE:
17148 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17149 break;
17150 default:
17151 VMA_ASSERT(0);
17152 }
17153 }
17154 // else: Just ignore this call.
17155 return res;
17156 }
17157
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17158 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17159 uint32_t allocationCount,
17160 const VmaAllocation* allocations,
17161 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17162 VMA_CACHE_OPERATION op)
17163 {
17164 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17165 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17166 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17167
17168 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17169 {
17170 const VmaAllocation alloc = allocations[allocIndex];
17171 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17172 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17173 VkMappedMemoryRange newRange;
17174 if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17175 {
17176 ranges.push_back(newRange);
17177 }
17178 }
17179
17180 VkResult res = VK_SUCCESS;
17181 if(!ranges.empty())
17182 {
17183 switch(op)
17184 {
17185 case VMA_CACHE_FLUSH:
17186 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17187 break;
17188 case VMA_CACHE_INVALIDATE:
17189 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17190 break;
17191 default:
17192 VMA_ASSERT(0);
17193 }
17194 }
17195 // else: Just ignore this call.
17196 return res;
17197 }
17198
FreeDedicatedMemory(const VmaAllocation allocation)17199 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17200 {
17201 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17202
17203 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17204 {
17205 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17206 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17207 VMA_ASSERT(pDedicatedAllocations);
17208 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17209 VMA_ASSERT(success);
17210 }
17211
17212 VkDeviceMemory hMemory = allocation->GetMemory();
17213
17214 /*
17215 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17216 before vkFreeMemory.
17217
17218 if(allocation->GetMappedData() != VMA_NULL)
17219 {
17220 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17221 }
17222 */
17223
17224 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17225
17226 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17227 }
17228
CalculateGpuDefragmentationMemoryTypeBits()17229 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17230 {
17231 VkBufferCreateInfo dummyBufCreateInfo;
17232 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17233
17234 uint32_t memoryTypeBits = 0;
17235
17236 // Create buffer.
17237 VkBuffer buf = VK_NULL_HANDLE;
17238 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17239 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17240 if(res == VK_SUCCESS)
17241 {
17242 // Query for supported memory types.
17243 VkMemoryRequirements memReq;
17244 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17245 memoryTypeBits = memReq.memoryTypeBits;
17246
17247 // Destroy buffer.
17248 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17249 }
17250
17251 return memoryTypeBits;
17252 }
17253
CalculateGlobalMemoryTypeBits()17254 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17255 {
17256 // Make sure memory information is already fetched.
17257 VMA_ASSERT(GetMemoryTypeCount() > 0);
17258
17259 uint32_t memoryTypeBits = UINT32_MAX;
17260
17261 if(!m_UseAmdDeviceCoherentMemory)
17262 {
17263 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17264 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17265 {
17266 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17267 {
17268 memoryTypeBits &= ~(1u << memTypeIndex);
17269 }
17270 }
17271 }
17272
17273 return memoryTypeBits;
17274 }
17275
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17276 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17277 VmaAllocation allocation,
17278 VkDeviceSize offset, VkDeviceSize size,
17279 VkMappedMemoryRange& outRange) const
17280 {
17281 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17282 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17283 {
17284 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17285 const VkDeviceSize allocationSize = allocation->GetSize();
17286 VMA_ASSERT(offset <= allocationSize);
17287
17288 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17289 outRange.pNext = VMA_NULL;
17290 outRange.memory = allocation->GetMemory();
17291
17292 switch(allocation->GetType())
17293 {
17294 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17295 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17296 if(size == VK_WHOLE_SIZE)
17297 {
17298 outRange.size = allocationSize - outRange.offset;
17299 }
17300 else
17301 {
17302 VMA_ASSERT(offset + size <= allocationSize);
17303 outRange.size = VMA_MIN(
17304 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17305 allocationSize - outRange.offset);
17306 }
17307 break;
17308 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17309 {
17310 // 1. Still within this allocation.
17311 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17312 if(size == VK_WHOLE_SIZE)
17313 {
17314 size = allocationSize - offset;
17315 }
17316 else
17317 {
17318 VMA_ASSERT(offset + size <= allocationSize);
17319 }
17320 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17321
17322 // 2. Adjust to whole block.
17323 const VkDeviceSize allocationOffset = allocation->GetOffset();
17324 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17325 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17326 outRange.offset += allocationOffset;
17327 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17328
17329 break;
17330 }
17331 default:
17332 VMA_ASSERT(0);
17333 }
17334 return true;
17335 }
17336 return false;
17337 }
17338
17339 #if VMA_MEMORY_BUDGET
17340
UpdateVulkanBudget()17341 void VmaAllocator_T::UpdateVulkanBudget()
17342 {
17343 VMA_ASSERT(m_UseExtMemoryBudget);
17344
17345 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17346
17347 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17348 VmaPnextChainPushFront(&memProps, &budgetProps);
17349
17350 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17351
17352 {
17353 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17354
17355 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17356 {
17357 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17358 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17359 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17360
17361 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17362 if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17363 {
17364 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17365 }
17366 else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17367 {
17368 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17369 }
17370 if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17371 {
17372 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17373 }
17374 }
17375 m_Budget.m_OperationsSinceBudgetFetch = 0;
17376 }
17377 }
17378
17379 #endif // #if VMA_MEMORY_BUDGET
17380
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17381 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17382 {
17383 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17384 !hAllocation->CanBecomeLost() &&
17385 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17386 {
17387 void* pData = VMA_NULL;
17388 VkResult res = Map(hAllocation, &pData);
17389 if(res == VK_SUCCESS)
17390 {
17391 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17392 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17393 Unmap(hAllocation);
17394 }
17395 else
17396 {
17397 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17398 }
17399 }
17400 }
17401
GetGpuDefragmentationMemoryTypeBits()17402 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17403 {
17404 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17405 if(memoryTypeBits == UINT32_MAX)
17406 {
17407 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17408 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17409 }
17410 return memoryTypeBits;
17411 }
17412
17413 #if VMA_STATS_STRING_ENABLED
17414
PrintDetailedMap(VmaJsonWriter & json)17415 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17416 {
17417 bool dedicatedAllocationsStarted = false;
17418 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17419 {
17420 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17421 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17422 VMA_ASSERT(pDedicatedAllocVector);
17423 if(pDedicatedAllocVector->empty() == false)
17424 {
17425 if(dedicatedAllocationsStarted == false)
17426 {
17427 dedicatedAllocationsStarted = true;
17428 json.WriteString("DedicatedAllocations");
17429 json.BeginObject();
17430 }
17431
17432 json.BeginString("Type ");
17433 json.ContinueString(memTypeIndex);
17434 json.EndString();
17435
17436 json.BeginArray();
17437
17438 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17439 {
17440 json.BeginObject(true);
17441 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17442 hAlloc->PrintParameters(json);
17443 json.EndObject();
17444 }
17445
17446 json.EndArray();
17447 }
17448 }
17449 if(dedicatedAllocationsStarted)
17450 {
17451 json.EndObject();
17452 }
17453
17454 {
17455 bool allocationsStarted = false;
17456 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17457 {
17458 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17459 {
17460 if(allocationsStarted == false)
17461 {
17462 allocationsStarted = true;
17463 json.WriteString("DefaultPools");
17464 json.BeginObject();
17465 }
17466
17467 json.BeginString("Type ");
17468 json.ContinueString(memTypeIndex);
17469 json.EndString();
17470
17471 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17472 }
17473 }
17474 if(allocationsStarted)
17475 {
17476 json.EndObject();
17477 }
17478 }
17479
17480 // Custom pools
17481 {
17482 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17483 const size_t poolCount = m_Pools.size();
17484 if(poolCount > 0)
17485 {
17486 json.WriteString("Pools");
17487 json.BeginObject();
17488 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17489 {
17490 json.BeginString();
17491 json.ContinueString(m_Pools[poolIndex]->GetId());
17492 json.EndString();
17493
17494 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17495 }
17496 json.EndObject();
17497 }
17498 }
17499 }
17500
17501 #endif // #if VMA_STATS_STRING_ENABLED
17502
17503 ////////////////////////////////////////////////////////////////////////////////
17504 // Public interface
17505
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17506 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17507 const VmaAllocatorCreateInfo* pCreateInfo,
17508 VmaAllocator* pAllocator)
17509 {
17510 VMA_ASSERT(pCreateInfo && pAllocator);
17511 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17512 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17513 VMA_DEBUG_LOG("vmaCreateAllocator");
17514 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17515 return (*pAllocator)->Init(pCreateInfo);
17516 }
17517
vmaDestroyAllocator(VmaAllocator allocator)17518 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17519 VmaAllocator allocator)
17520 {
17521 if(allocator != VK_NULL_HANDLE)
17522 {
17523 VMA_DEBUG_LOG("vmaDestroyAllocator");
17524 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17525 vma_delete(&allocationCallbacks, allocator);
17526 }
17527 }
17528
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17529 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17530 {
17531 VMA_ASSERT(allocator && pAllocatorInfo);
17532 pAllocatorInfo->instance = allocator->m_hInstance;
17533 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17534 pAllocatorInfo->device = allocator->m_hDevice;
17535 }
17536
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17537 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17538 VmaAllocator allocator,
17539 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17540 {
17541 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17542 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17543 }
17544
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17545 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17546 VmaAllocator allocator,
17547 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17548 {
17549 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17550 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17551 }
17552
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17553 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17554 VmaAllocator allocator,
17555 uint32_t memoryTypeIndex,
17556 VkMemoryPropertyFlags* pFlags)
17557 {
17558 VMA_ASSERT(allocator && pFlags);
17559 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17560 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17561 }
17562
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17563 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17564 VmaAllocator allocator,
17565 uint32_t frameIndex)
17566 {
17567 VMA_ASSERT(allocator);
17568 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17569
17570 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17571
17572 allocator->SetCurrentFrameIndex(frameIndex);
17573 }
17574
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17575 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17576 VmaAllocator allocator,
17577 VmaStats* pStats)
17578 {
17579 VMA_ASSERT(allocator && pStats);
17580 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17581 allocator->CalculateStats(pStats);
17582 }
17583
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17584 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17585 VmaAllocator allocator,
17586 VmaBudget* pBudget)
17587 {
17588 VMA_ASSERT(allocator && pBudget);
17589 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17590 allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17591 }
17592
17593 #if VMA_STATS_STRING_ENABLED
17594
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17595 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17596 VmaAllocator allocator,
17597 char** ppStatsString,
17598 VkBool32 detailedMap)
17599 {
17600 VMA_ASSERT(allocator && ppStatsString);
17601 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17602
17603 VmaStringBuilder sb(allocator);
17604 {
17605 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17606 json.BeginObject();
17607
17608 VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17609 allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17610
17611 VmaStats stats;
17612 allocator->CalculateStats(&stats);
17613
17614 json.WriteString("Total");
17615 VmaPrintStatInfo(json, stats.total);
17616
17617 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17618 {
17619 json.BeginString("Heap ");
17620 json.ContinueString(heapIndex);
17621 json.EndString();
17622 json.BeginObject();
17623
17624 json.WriteString("Size");
17625 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17626
17627 json.WriteString("Flags");
17628 json.BeginArray(true);
17629 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17630 {
17631 json.WriteString("DEVICE_LOCAL");
17632 }
17633 json.EndArray();
17634
17635 json.WriteString("Budget");
17636 json.BeginObject();
17637 {
17638 json.WriteString("BlockBytes");
17639 json.WriteNumber(budget[heapIndex].blockBytes);
17640 json.WriteString("AllocationBytes");
17641 json.WriteNumber(budget[heapIndex].allocationBytes);
17642 json.WriteString("Usage");
17643 json.WriteNumber(budget[heapIndex].usage);
17644 json.WriteString("Budget");
17645 json.WriteNumber(budget[heapIndex].budget);
17646 }
17647 json.EndObject();
17648
17649 if(stats.memoryHeap[heapIndex].blockCount > 0)
17650 {
17651 json.WriteString("Stats");
17652 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
17653 }
17654
17655 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
17656 {
17657 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
17658 {
17659 json.BeginString("Type ");
17660 json.ContinueString(typeIndex);
17661 json.EndString();
17662
17663 json.BeginObject();
17664
17665 json.WriteString("Flags");
17666 json.BeginArray(true);
17667 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
17668 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
17669 {
17670 json.WriteString("DEVICE_LOCAL");
17671 }
17672 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17673 {
17674 json.WriteString("HOST_VISIBLE");
17675 }
17676 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
17677 {
17678 json.WriteString("HOST_COHERENT");
17679 }
17680 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
17681 {
17682 json.WriteString("HOST_CACHED");
17683 }
17684 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
17685 {
17686 json.WriteString("LAZILY_ALLOCATED");
17687 }
17688 if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
17689 {
17690 json.WriteString(" PROTECTED");
17691 }
17692 if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17693 {
17694 json.WriteString(" DEVICE_COHERENT");
17695 }
17696 if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
17697 {
17698 json.WriteString(" DEVICE_UNCACHED");
17699 }
17700 json.EndArray();
17701
17702 if(stats.memoryType[typeIndex].blockCount > 0)
17703 {
17704 json.WriteString("Stats");
17705 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
17706 }
17707
17708 json.EndObject();
17709 }
17710 }
17711
17712 json.EndObject();
17713 }
17714 if(detailedMap == VK_TRUE)
17715 {
17716 allocator->PrintDetailedMap(json);
17717 }
17718
17719 json.EndObject();
17720 }
17721
17722 const size_t len = sb.GetLength();
17723 char* const pChars = vma_new_array(allocator, char, len + 1);
17724 if(len > 0)
17725 {
17726 memcpy(pChars, sb.GetData(), len);
17727 }
17728 pChars[len] = '\0';
17729 *ppStatsString = pChars;
17730 }
17731
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)17732 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
17733 VmaAllocator allocator,
17734 char* pStatsString)
17735 {
17736 if(pStatsString != VMA_NULL)
17737 {
17738 VMA_ASSERT(allocator);
17739 size_t len = strlen(pStatsString);
17740 vma_delete_array(allocator, pStatsString, len + 1);
17741 }
17742 }
17743
17744 #endif // #if VMA_STATS_STRING_ENABLED
17745
17746 /*
17747 This function is not protected by any mutex because it just reads immutable data.
17748 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
17750 VmaAllocator allocator,
17751 uint32_t memoryTypeBits,
17752 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17753 uint32_t* pMemoryTypeIndex)
17754 {
17755 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17756 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17757 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17758
17759 memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
17760
17761 if(pAllocationCreateInfo->memoryTypeBits != 0)
17762 {
17763 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
17764 }
17765
17766 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
17767 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
17768 uint32_t notPreferredFlags = 0;
17769
17770 // Convert usage to requiredFlags and preferredFlags.
17771 switch(pAllocationCreateInfo->usage)
17772 {
17773 case VMA_MEMORY_USAGE_UNKNOWN:
17774 break;
17775 case VMA_MEMORY_USAGE_GPU_ONLY:
17776 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17777 {
17778 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17779 }
17780 break;
17781 case VMA_MEMORY_USAGE_CPU_ONLY:
17782 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
17783 break;
17784 case VMA_MEMORY_USAGE_CPU_TO_GPU:
17785 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17786 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
17787 {
17788 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17789 }
17790 break;
17791 case VMA_MEMORY_USAGE_GPU_TO_CPU:
17792 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17793 preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17794 break;
17795 case VMA_MEMORY_USAGE_CPU_COPY:
17796 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
17797 break;
17798 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
17799 requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
17800 break;
17801 default:
17802 VMA_ASSERT(0);
17803 break;
17804 }
17805
17806 // Avoid DEVICE_COHERENT unless explicitly requested.
17807 if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
17808 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
17809 {
17810 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
17811 }
17812
17813 *pMemoryTypeIndex = UINT32_MAX;
17814 uint32_t minCost = UINT32_MAX;
17815 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
17816 memTypeIndex < allocator->GetMemoryTypeCount();
17817 ++memTypeIndex, memTypeBit <<= 1)
17818 {
17819 // This memory type is acceptable according to memoryTypeBits bitmask.
17820 if((memTypeBit & memoryTypeBits) != 0)
17821 {
17822 const VkMemoryPropertyFlags currFlags =
17823 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
17824 // This memory type contains requiredFlags.
17825 if((requiredFlags & ~currFlags) == 0)
17826 {
17827 // Calculate cost as number of bits from preferredFlags not present in this memory type.
17828 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
17829 VmaCountBitsSet(currFlags & notPreferredFlags);
17830 // Remember memory type with lowest cost.
17831 if(currCost < minCost)
17832 {
17833 *pMemoryTypeIndex = memTypeIndex;
17834 if(currCost == 0)
17835 {
17836 return VK_SUCCESS;
17837 }
17838 minCost = currCost;
17839 }
17840 }
17841 }
17842 }
17843 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
17844 }
17845
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17846 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
17847 VmaAllocator allocator,
17848 const VkBufferCreateInfo* pBufferCreateInfo,
17849 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17850 uint32_t* pMemoryTypeIndex)
17851 {
17852 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17853 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
17854 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17855 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17856
17857 const VkDevice hDev = allocator->m_hDevice;
17858 VkBuffer hBuffer = VK_NULL_HANDLE;
17859 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
17860 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
17861 if(res == VK_SUCCESS)
17862 {
17863 VkMemoryRequirements memReq = {};
17864 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
17865 hDev, hBuffer, &memReq);
17866
17867 res = vmaFindMemoryTypeIndex(
17868 allocator,
17869 memReq.memoryTypeBits,
17870 pAllocationCreateInfo,
17871 pMemoryTypeIndex);
17872
17873 allocator->GetVulkanFunctions().vkDestroyBuffer(
17874 hDev, hBuffer, allocator->GetAllocationCallbacks());
17875 }
17876 return res;
17877 }
17878
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)17879 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
17880 VmaAllocator allocator,
17881 const VkImageCreateInfo* pImageCreateInfo,
17882 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17883 uint32_t* pMemoryTypeIndex)
17884 {
17885 VMA_ASSERT(allocator != VK_NULL_HANDLE);
17886 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
17887 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
17888 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
17889
17890 const VkDevice hDev = allocator->m_hDevice;
17891 VkImage hImage = VK_NULL_HANDLE;
17892 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
17893 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
17894 if(res == VK_SUCCESS)
17895 {
17896 VkMemoryRequirements memReq = {};
17897 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
17898 hDev, hImage, &memReq);
17899
17900 res = vmaFindMemoryTypeIndex(
17901 allocator,
17902 memReq.memoryTypeBits,
17903 pAllocationCreateInfo,
17904 pMemoryTypeIndex);
17905
17906 allocator->GetVulkanFunctions().vkDestroyImage(
17907 hDev, hImage, allocator->GetAllocationCallbacks());
17908 }
17909 return res;
17910 }
17911
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17912 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
17913 VmaAllocator allocator,
17914 const VmaPoolCreateInfo* pCreateInfo,
17915 VmaPool* pPool)
17916 {
17917 VMA_ASSERT(allocator && pCreateInfo && pPool);
17918
17919 VMA_DEBUG_LOG("vmaCreatePool");
17920
17921 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17922
17923 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
17924
17925 #if VMA_RECORDING_ENABLED
17926 if(allocator->GetRecorder() != VMA_NULL)
17927 {
17928 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
17929 }
17930 #endif
17931
17932 return res;
17933 }
17934
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)17935 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
17936 VmaAllocator allocator,
17937 VmaPool pool)
17938 {
17939 VMA_ASSERT(allocator);
17940
17941 if(pool == VK_NULL_HANDLE)
17942 {
17943 return;
17944 }
17945
17946 VMA_DEBUG_LOG("vmaDestroyPool");
17947
17948 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17949
17950 #if VMA_RECORDING_ENABLED
17951 if(allocator->GetRecorder() != VMA_NULL)
17952 {
17953 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
17954 }
17955 #endif
17956
17957 allocator->DestroyPool(pool);
17958 }
17959
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)17960 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
17961 VmaAllocator allocator,
17962 VmaPool pool,
17963 VmaPoolStats* pPoolStats)
17964 {
17965 VMA_ASSERT(allocator && pool && pPoolStats);
17966
17967 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17968
17969 allocator->GetPoolStats(pool, pPoolStats);
17970 }
17971
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)17972 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
17973 VmaAllocator allocator,
17974 VmaPool pool,
17975 size_t* pLostAllocationCount)
17976 {
17977 VMA_ASSERT(allocator && pool);
17978
17979 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17980
17981 #if VMA_RECORDING_ENABLED
17982 if(allocator->GetRecorder() != VMA_NULL)
17983 {
17984 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
17985 }
17986 #endif
17987
17988 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
17989 }
17990
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)17991 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
17992 {
17993 VMA_ASSERT(allocator && pool);
17994
17995 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17996
17997 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
17998
17999 return allocator->CheckPoolCorruption(pool);
18000 }
18001
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18002 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18003 VmaAllocator allocator,
18004 VmaPool pool,
18005 const char** ppName)
18006 {
18007 VMA_ASSERT(allocator && pool && ppName);
18008
18009 VMA_DEBUG_LOG("vmaGetPoolName");
18010
18011 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18012
18013 *ppName = pool->GetName();
18014 }
18015
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18016 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18017 VmaAllocator allocator,
18018 VmaPool pool,
18019 const char* pName)
18020 {
18021 VMA_ASSERT(allocator && pool);
18022
18023 VMA_DEBUG_LOG("vmaSetPoolName");
18024
18025 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18026
18027 pool->SetName(pName);
18028
18029 #if VMA_RECORDING_ENABLED
18030 if(allocator->GetRecorder() != VMA_NULL)
18031 {
18032 allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18033 }
18034 #endif
18035 }
18036
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18037 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18038 VmaAllocator allocator,
18039 const VkMemoryRequirements* pVkMemoryRequirements,
18040 const VmaAllocationCreateInfo* pCreateInfo,
18041 VmaAllocation* pAllocation,
18042 VmaAllocationInfo* pAllocationInfo)
18043 {
18044 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18045
18046 VMA_DEBUG_LOG("vmaAllocateMemory");
18047
18048 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18049
18050 VkResult result = allocator->AllocateMemory(
18051 *pVkMemoryRequirements,
18052 false, // requiresDedicatedAllocation
18053 false, // prefersDedicatedAllocation
18054 VK_NULL_HANDLE, // dedicatedBuffer
18055 UINT32_MAX, // dedicatedBufferUsage
18056 VK_NULL_HANDLE, // dedicatedImage
18057 *pCreateInfo,
18058 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18059 1, // allocationCount
18060 pAllocation);
18061
18062 #if VMA_RECORDING_ENABLED
18063 if(allocator->GetRecorder() != VMA_NULL)
18064 {
18065 allocator->GetRecorder()->RecordAllocateMemory(
18066 allocator->GetCurrentFrameIndex(),
18067 *pVkMemoryRequirements,
18068 *pCreateInfo,
18069 *pAllocation);
18070 }
18071 #endif
18072
18073 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18074 {
18075 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18076 }
18077
18078 return result;
18079 }
18080
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18081 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18082 VmaAllocator allocator,
18083 const VkMemoryRequirements* pVkMemoryRequirements,
18084 const VmaAllocationCreateInfo* pCreateInfo,
18085 size_t allocationCount,
18086 VmaAllocation* pAllocations,
18087 VmaAllocationInfo* pAllocationInfo)
18088 {
18089 if(allocationCount == 0)
18090 {
18091 return VK_SUCCESS;
18092 }
18093
18094 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18095
18096 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18097
18098 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18099
18100 VkResult result = allocator->AllocateMemory(
18101 *pVkMemoryRequirements,
18102 false, // requiresDedicatedAllocation
18103 false, // prefersDedicatedAllocation
18104 VK_NULL_HANDLE, // dedicatedBuffer
18105 UINT32_MAX, // dedicatedBufferUsage
18106 VK_NULL_HANDLE, // dedicatedImage
18107 *pCreateInfo,
18108 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18109 allocationCount,
18110 pAllocations);
18111
18112 #if VMA_RECORDING_ENABLED
18113 if(allocator->GetRecorder() != VMA_NULL)
18114 {
18115 allocator->GetRecorder()->RecordAllocateMemoryPages(
18116 allocator->GetCurrentFrameIndex(),
18117 *pVkMemoryRequirements,
18118 *pCreateInfo,
18119 (uint64_t)allocationCount,
18120 pAllocations);
18121 }
18122 #endif
18123
18124 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18125 {
18126 for(size_t i = 0; i < allocationCount; ++i)
18127 {
18128 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18129 }
18130 }
18131
18132 return result;
18133 }
18134
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18135 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18136 VmaAllocator allocator,
18137 VkBuffer buffer,
18138 const VmaAllocationCreateInfo* pCreateInfo,
18139 VmaAllocation* pAllocation,
18140 VmaAllocationInfo* pAllocationInfo)
18141 {
18142 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18143
18144 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18145
18146 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18147
18148 VkMemoryRequirements vkMemReq = {};
18149 bool requiresDedicatedAllocation = false;
18150 bool prefersDedicatedAllocation = false;
18151 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18152 requiresDedicatedAllocation,
18153 prefersDedicatedAllocation);
18154
18155 VkResult result = allocator->AllocateMemory(
18156 vkMemReq,
18157 requiresDedicatedAllocation,
18158 prefersDedicatedAllocation,
18159 buffer, // dedicatedBuffer
18160 UINT32_MAX, // dedicatedBufferUsage
18161 VK_NULL_HANDLE, // dedicatedImage
18162 *pCreateInfo,
18163 VMA_SUBALLOCATION_TYPE_BUFFER,
18164 1, // allocationCount
18165 pAllocation);
18166
18167 #if VMA_RECORDING_ENABLED
18168 if(allocator->GetRecorder() != VMA_NULL)
18169 {
18170 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18171 allocator->GetCurrentFrameIndex(),
18172 vkMemReq,
18173 requiresDedicatedAllocation,
18174 prefersDedicatedAllocation,
18175 *pCreateInfo,
18176 *pAllocation);
18177 }
18178 #endif
18179
18180 if(pAllocationInfo && result == VK_SUCCESS)
18181 {
18182 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18183 }
18184
18185 return result;
18186 }
18187
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18188 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18189 VmaAllocator allocator,
18190 VkImage image,
18191 const VmaAllocationCreateInfo* pCreateInfo,
18192 VmaAllocation* pAllocation,
18193 VmaAllocationInfo* pAllocationInfo)
18194 {
18195 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18196
18197 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18198
18199 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18200
18201 VkMemoryRequirements vkMemReq = {};
18202 bool requiresDedicatedAllocation = false;
18203 bool prefersDedicatedAllocation = false;
18204 allocator->GetImageMemoryRequirements(image, vkMemReq,
18205 requiresDedicatedAllocation, prefersDedicatedAllocation);
18206
18207 VkResult result = allocator->AllocateMemory(
18208 vkMemReq,
18209 requiresDedicatedAllocation,
18210 prefersDedicatedAllocation,
18211 VK_NULL_HANDLE, // dedicatedBuffer
18212 UINT32_MAX, // dedicatedBufferUsage
18213 image, // dedicatedImage
18214 *pCreateInfo,
18215 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18216 1, // allocationCount
18217 pAllocation);
18218
18219 #if VMA_RECORDING_ENABLED
18220 if(allocator->GetRecorder() != VMA_NULL)
18221 {
18222 allocator->GetRecorder()->RecordAllocateMemoryForImage(
18223 allocator->GetCurrentFrameIndex(),
18224 vkMemReq,
18225 requiresDedicatedAllocation,
18226 prefersDedicatedAllocation,
18227 *pCreateInfo,
18228 *pAllocation);
18229 }
18230 #endif
18231
18232 if(pAllocationInfo && result == VK_SUCCESS)
18233 {
18234 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18235 }
18236
18237 return result;
18238 }
18239
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18240 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18241 VmaAllocator allocator,
18242 VmaAllocation allocation)
18243 {
18244 VMA_ASSERT(allocator);
18245
18246 if(allocation == VK_NULL_HANDLE)
18247 {
18248 return;
18249 }
18250
18251 VMA_DEBUG_LOG("vmaFreeMemory");
18252
18253 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18254
18255 #if VMA_RECORDING_ENABLED
18256 if(allocator->GetRecorder() != VMA_NULL)
18257 {
18258 allocator->GetRecorder()->RecordFreeMemory(
18259 allocator->GetCurrentFrameIndex(),
18260 allocation);
18261 }
18262 #endif
18263
18264 allocator->FreeMemory(
18265 1, // allocationCount
18266 &allocation);
18267 }
18268
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18269 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18270 VmaAllocator allocator,
18271 size_t allocationCount,
18272 const VmaAllocation* pAllocations)
18273 {
18274 if(allocationCount == 0)
18275 {
18276 return;
18277 }
18278
18279 VMA_ASSERT(allocator);
18280
18281 VMA_DEBUG_LOG("vmaFreeMemoryPages");
18282
18283 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18284
18285 #if VMA_RECORDING_ENABLED
18286 if(allocator->GetRecorder() != VMA_NULL)
18287 {
18288 allocator->GetRecorder()->RecordFreeMemoryPages(
18289 allocator->GetCurrentFrameIndex(),
18290 (uint64_t)allocationCount,
18291 pAllocations);
18292 }
18293 #endif
18294
18295 allocator->FreeMemory(allocationCount, pAllocations);
18296 }
18297
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18298 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18299 VmaAllocator allocator,
18300 VmaAllocation allocation,
18301 VkDeviceSize newSize)
18302 {
18303 VMA_ASSERT(allocator && allocation);
18304
18305 VMA_DEBUG_LOG("vmaResizeAllocation");
18306
18307 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18308
18309 return allocator->ResizeAllocation(allocation, newSize);
18310 }
18311
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18312 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18313 VmaAllocator allocator,
18314 VmaAllocation allocation,
18315 VmaAllocationInfo* pAllocationInfo)
18316 {
18317 VMA_ASSERT(allocator && allocation && pAllocationInfo);
18318
18319 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18320
18321 #if VMA_RECORDING_ENABLED
18322 if(allocator->GetRecorder() != VMA_NULL)
18323 {
18324 allocator->GetRecorder()->RecordGetAllocationInfo(
18325 allocator->GetCurrentFrameIndex(),
18326 allocation);
18327 }
18328 #endif
18329
18330 allocator->GetAllocationInfo(allocation, pAllocationInfo);
18331 }
18332
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18333 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18334 VmaAllocator allocator,
18335 VmaAllocation allocation)
18336 {
18337 VMA_ASSERT(allocator && allocation);
18338
18339 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18340
18341 #if VMA_RECORDING_ENABLED
18342 if(allocator->GetRecorder() != VMA_NULL)
18343 {
18344 allocator->GetRecorder()->RecordTouchAllocation(
18345 allocator->GetCurrentFrameIndex(),
18346 allocation);
18347 }
18348 #endif
18349
18350 return allocator->TouchAllocation(allocation);
18351 }
18352
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18353 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18354 VmaAllocator allocator,
18355 VmaAllocation allocation,
18356 void* pUserData)
18357 {
18358 VMA_ASSERT(allocator && allocation);
18359
18360 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18361
18362 allocation->SetUserData(allocator, pUserData);
18363
18364 #if VMA_RECORDING_ENABLED
18365 if(allocator->GetRecorder() != VMA_NULL)
18366 {
18367 allocator->GetRecorder()->RecordSetAllocationUserData(
18368 allocator->GetCurrentFrameIndex(),
18369 allocation,
18370 pUserData);
18371 }
18372 #endif
18373 }
18374
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18375 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18376 VmaAllocator allocator,
18377 VmaAllocation* pAllocation)
18378 {
18379 VMA_ASSERT(allocator && pAllocation);
18380
18381 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18382
18383 allocator->CreateLostAllocation(pAllocation);
18384
18385 #if VMA_RECORDING_ENABLED
18386 if(allocator->GetRecorder() != VMA_NULL)
18387 {
18388 allocator->GetRecorder()->RecordCreateLostAllocation(
18389 allocator->GetCurrentFrameIndex(),
18390 *pAllocation);
18391 }
18392 #endif
18393 }
18394
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18395 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18396 VmaAllocator allocator,
18397 VmaAllocation allocation,
18398 void** ppData)
18399 {
18400 VMA_ASSERT(allocator && allocation && ppData);
18401
18402 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18403
18404 VkResult res = allocator->Map(allocation, ppData);
18405
18406 #if VMA_RECORDING_ENABLED
18407 if(allocator->GetRecorder() != VMA_NULL)
18408 {
18409 allocator->GetRecorder()->RecordMapMemory(
18410 allocator->GetCurrentFrameIndex(),
18411 allocation);
18412 }
18413 #endif
18414
18415 return res;
18416 }
18417
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18418 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18419 VmaAllocator allocator,
18420 VmaAllocation allocation)
18421 {
18422 VMA_ASSERT(allocator && allocation);
18423
18424 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18425
18426 #if VMA_RECORDING_ENABLED
18427 if(allocator->GetRecorder() != VMA_NULL)
18428 {
18429 allocator->GetRecorder()->RecordUnmapMemory(
18430 allocator->GetCurrentFrameIndex(),
18431 allocation);
18432 }
18433 #endif
18434
18435 allocator->Unmap(allocation);
18436 }
18437
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18438 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18439 {
18440 VMA_ASSERT(allocator && allocation);
18441
18442 VMA_DEBUG_LOG("vmaFlushAllocation");
18443
18444 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18445
18446 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18447
18448 #if VMA_RECORDING_ENABLED
18449 if(allocator->GetRecorder() != VMA_NULL)
18450 {
18451 allocator->GetRecorder()->RecordFlushAllocation(
18452 allocator->GetCurrentFrameIndex(),
18453 allocation, offset, size);
18454 }
18455 #endif
18456
18457 return res;
18458 }
18459
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18460 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18461 {
18462 VMA_ASSERT(allocator && allocation);
18463
18464 VMA_DEBUG_LOG("vmaInvalidateAllocation");
18465
18466 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18467
18468 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18469
18470 #if VMA_RECORDING_ENABLED
18471 if(allocator->GetRecorder() != VMA_NULL)
18472 {
18473 allocator->GetRecorder()->RecordInvalidateAllocation(
18474 allocator->GetCurrentFrameIndex(),
18475 allocation, offset, size);
18476 }
18477 #endif
18478
18479 return res;
18480 }
18481
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18482 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18483 VmaAllocator allocator,
18484 uint32_t allocationCount,
18485 const VmaAllocation* allocations,
18486 const VkDeviceSize* offsets,
18487 const VkDeviceSize* sizes)
18488 {
18489 VMA_ASSERT(allocator);
18490
18491 if(allocationCount == 0)
18492 {
18493 return VK_SUCCESS;
18494 }
18495
18496 VMA_ASSERT(allocations);
18497
18498 VMA_DEBUG_LOG("vmaFlushAllocations");
18499
18500 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18501
18502 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18503
18504 #if VMA_RECORDING_ENABLED
18505 if(allocator->GetRecorder() != VMA_NULL)
18506 {
18507 //TODO
18508 }
18509 #endif
18510
18511 return res;
18512 }
18513
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18514 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18515 VmaAllocator allocator,
18516 uint32_t allocationCount,
18517 const VmaAllocation* allocations,
18518 const VkDeviceSize* offsets,
18519 const VkDeviceSize* sizes)
18520 {
18521 VMA_ASSERT(allocator);
18522
18523 if(allocationCount == 0)
18524 {
18525 return VK_SUCCESS;
18526 }
18527
18528 VMA_ASSERT(allocations);
18529
18530 VMA_DEBUG_LOG("vmaInvalidateAllocations");
18531
18532 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18533
18534 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18535
18536 #if VMA_RECORDING_ENABLED
18537 if(allocator->GetRecorder() != VMA_NULL)
18538 {
18539 //TODO
18540 }
18541 #endif
18542
18543 return res;
18544 }
18545
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)18546 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
18547 {
18548 VMA_ASSERT(allocator);
18549
18550 VMA_DEBUG_LOG("vmaCheckCorruption");
18551
18552 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18553
18554 return allocator->CheckCorruption(memoryTypeBits);
18555 }
18556
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)18557 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
18558 VmaAllocator allocator,
18559 const VmaAllocation* pAllocations,
18560 size_t allocationCount,
18561 VkBool32* pAllocationsChanged,
18562 const VmaDefragmentationInfo *pDefragmentationInfo,
18563 VmaDefragmentationStats* pDefragmentationStats)
18564 {
18565 // Deprecated interface, reimplemented using new one.
18566
18567 VmaDefragmentationInfo2 info2 = {};
18568 info2.allocationCount = (uint32_t)allocationCount;
18569 info2.pAllocations = pAllocations;
18570 info2.pAllocationsChanged = pAllocationsChanged;
18571 if(pDefragmentationInfo != VMA_NULL)
18572 {
18573 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
18574 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
18575 }
18576 else
18577 {
18578 info2.maxCpuAllocationsToMove = UINT32_MAX;
18579 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
18580 }
18581 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
18582
18583 VmaDefragmentationContext ctx;
18584 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
18585 if(res == VK_NOT_READY)
18586 {
18587 res = vmaDefragmentationEnd( allocator, ctx);
18588 }
18589 return res;
18590 }
18591
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)18592 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
18593 VmaAllocator allocator,
18594 const VmaDefragmentationInfo2* pInfo,
18595 VmaDefragmentationStats* pStats,
18596 VmaDefragmentationContext *pContext)
18597 {
18598 VMA_ASSERT(allocator && pInfo && pContext);
18599
18600 // Degenerate case: Nothing to defragment.
18601 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
18602 {
18603 return VK_SUCCESS;
18604 }
18605
18606 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
18607 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
18608 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
18609 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
18610
18611 VMA_DEBUG_LOG("vmaDefragmentationBegin");
18612
18613 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18614
18615 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
18616
18617 #if VMA_RECORDING_ENABLED
18618 if(allocator->GetRecorder() != VMA_NULL)
18619 {
18620 allocator->GetRecorder()->RecordDefragmentationBegin(
18621 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
18622 }
18623 #endif
18624
18625 return res;
18626 }
18627
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)18628 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
18629 VmaAllocator allocator,
18630 VmaDefragmentationContext context)
18631 {
18632 VMA_ASSERT(allocator);
18633
18634 VMA_DEBUG_LOG("vmaDefragmentationEnd");
18635
18636 if(context != VK_NULL_HANDLE)
18637 {
18638 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18639
18640 #if VMA_RECORDING_ENABLED
18641 if(allocator->GetRecorder() != VMA_NULL)
18642 {
18643 allocator->GetRecorder()->RecordDefragmentationEnd(
18644 allocator->GetCurrentFrameIndex(), context);
18645 }
18646 #endif
18647
18648 return allocator->DefragmentationEnd(context);
18649 }
18650 else
18651 {
18652 return VK_SUCCESS;
18653 }
18654 }
18655
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)18656 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
18657 VmaAllocator allocator,
18658 VmaDefragmentationContext context,
18659 VmaDefragmentationPassInfo* pInfo
18660 )
18661 {
18662 VMA_ASSERT(allocator);
18663 VMA_ASSERT(pInfo);
18664 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
18665
18666 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
18667
18668 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18669
18670 if(context == VK_NULL_HANDLE)
18671 {
18672 pInfo->moveCount = 0;
18673 return VK_SUCCESS;
18674 }
18675
18676 return allocator->DefragmentationPassBegin(pInfo, context);
18677 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)18678 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
18679 VmaAllocator allocator,
18680 VmaDefragmentationContext context)
18681 {
18682 VMA_ASSERT(allocator);
18683
18684 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
18685 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18686
18687 if(context == VK_NULL_HANDLE)
18688 return VK_SUCCESS;
18689
18690 return allocator->DefragmentationPassEnd(context);
18691 }
18692
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)18693 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
18694 VmaAllocator allocator,
18695 VmaAllocation allocation,
18696 VkBuffer buffer)
18697 {
18698 VMA_ASSERT(allocator && allocation && buffer);
18699
18700 VMA_DEBUG_LOG("vmaBindBufferMemory");
18701
18702 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18703
18704 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
18705 }
18706
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)18707 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
18708 VmaAllocator allocator,
18709 VmaAllocation allocation,
18710 VkDeviceSize allocationLocalOffset,
18711 VkBuffer buffer,
18712 const void* pNext)
18713 {
18714 VMA_ASSERT(allocator && allocation && buffer);
18715
18716 VMA_DEBUG_LOG("vmaBindBufferMemory2");
18717
18718 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18719
18720 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
18721 }
18722
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)18723 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
18724 VmaAllocator allocator,
18725 VmaAllocation allocation,
18726 VkImage image)
18727 {
18728 VMA_ASSERT(allocator && allocation && image);
18729
18730 VMA_DEBUG_LOG("vmaBindImageMemory");
18731
18732 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18733
18734 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
18735 }
18736
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)18737 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
18738 VmaAllocator allocator,
18739 VmaAllocation allocation,
18740 VkDeviceSize allocationLocalOffset,
18741 VkImage image,
18742 const void* pNext)
18743 {
18744 VMA_ASSERT(allocator && allocation && image);
18745
18746 VMA_DEBUG_LOG("vmaBindImageMemory2");
18747
18748 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18749
18750 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
18751 }
18752
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18753 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
18754 VmaAllocator allocator,
18755 const VkBufferCreateInfo* pBufferCreateInfo,
18756 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18757 VkBuffer* pBuffer,
18758 VmaAllocation* pAllocation,
18759 VmaAllocationInfo* pAllocationInfo)
18760 {
18761 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
18762
18763 if(pBufferCreateInfo->size == 0)
18764 {
18765 return VK_ERROR_VALIDATION_FAILED_EXT;
18766 }
18767 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
18768 !allocator->m_UseKhrBufferDeviceAddress)
18769 {
18770 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
18771 return VK_ERROR_VALIDATION_FAILED_EXT;
18772 }
18773
18774 VMA_DEBUG_LOG("vmaCreateBuffer");
18775
18776 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18777
18778 *pBuffer = VK_NULL_HANDLE;
18779 *pAllocation = VK_NULL_HANDLE;
18780
18781 // 1. Create VkBuffer.
18782 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
18783 allocator->m_hDevice,
18784 pBufferCreateInfo,
18785 allocator->GetAllocationCallbacks(),
18786 pBuffer);
18787 if(res >= 0)
18788 {
18789 // 2. vkGetBufferMemoryRequirements.
18790 VkMemoryRequirements vkMemReq = {};
18791 bool requiresDedicatedAllocation = false;
18792 bool prefersDedicatedAllocation = false;
18793 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
18794 requiresDedicatedAllocation, prefersDedicatedAllocation);
18795
18796 // 3. Allocate memory using allocator.
18797 res = allocator->AllocateMemory(
18798 vkMemReq,
18799 requiresDedicatedAllocation,
18800 prefersDedicatedAllocation,
18801 *pBuffer, // dedicatedBuffer
18802 pBufferCreateInfo->usage, // dedicatedBufferUsage
18803 VK_NULL_HANDLE, // dedicatedImage
18804 *pAllocationCreateInfo,
18805 VMA_SUBALLOCATION_TYPE_BUFFER,
18806 1, // allocationCount
18807 pAllocation);
18808
18809 #if VMA_RECORDING_ENABLED
18810 if(allocator->GetRecorder() != VMA_NULL)
18811 {
18812 allocator->GetRecorder()->RecordCreateBuffer(
18813 allocator->GetCurrentFrameIndex(),
18814 *pBufferCreateInfo,
18815 *pAllocationCreateInfo,
18816 *pAllocation);
18817 }
18818 #endif
18819
18820 if(res >= 0)
18821 {
18822 // 3. Bind buffer with memory.
18823 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18824 {
18825 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
18826 }
18827 if(res >= 0)
18828 {
18829 // All steps succeeded.
18830 #if VMA_STATS_STRING_ENABLED
18831 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
18832 #endif
18833 if(pAllocationInfo != VMA_NULL)
18834 {
18835 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18836 }
18837
18838 return VK_SUCCESS;
18839 }
18840 allocator->FreeMemory(
18841 1, // allocationCount
18842 pAllocation);
18843 *pAllocation = VK_NULL_HANDLE;
18844 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18845 *pBuffer = VK_NULL_HANDLE;
18846 return res;
18847 }
18848 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
18849 *pBuffer = VK_NULL_HANDLE;
18850 return res;
18851 }
18852 return res;
18853 }
18854
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)18855 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
18856 VmaAllocator allocator,
18857 VkBuffer buffer,
18858 VmaAllocation allocation)
18859 {
18860 VMA_ASSERT(allocator);
18861
18862 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
18863 {
18864 return;
18865 }
18866
18867 VMA_DEBUG_LOG("vmaDestroyBuffer");
18868
18869 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18870
18871 #if VMA_RECORDING_ENABLED
18872 if(allocator->GetRecorder() != VMA_NULL)
18873 {
18874 allocator->GetRecorder()->RecordDestroyBuffer(
18875 allocator->GetCurrentFrameIndex(),
18876 allocation);
18877 }
18878 #endif
18879
18880 if(buffer != VK_NULL_HANDLE)
18881 {
18882 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
18883 }
18884
18885 if(allocation != VK_NULL_HANDLE)
18886 {
18887 allocator->FreeMemory(
18888 1, // allocationCount
18889 &allocation);
18890 }
18891 }
18892
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18893 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
18894 VmaAllocator allocator,
18895 const VkImageCreateInfo* pImageCreateInfo,
18896 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18897 VkImage* pImage,
18898 VmaAllocation* pAllocation,
18899 VmaAllocationInfo* pAllocationInfo)
18900 {
18901 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
18902
18903 if(pImageCreateInfo->extent.width == 0 ||
18904 pImageCreateInfo->extent.height == 0 ||
18905 pImageCreateInfo->extent.depth == 0 ||
18906 pImageCreateInfo->mipLevels == 0 ||
18907 pImageCreateInfo->arrayLayers == 0)
18908 {
18909 return VK_ERROR_VALIDATION_FAILED_EXT;
18910 }
18911
18912 VMA_DEBUG_LOG("vmaCreateImage");
18913
18914 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18915
18916 *pImage = VK_NULL_HANDLE;
18917 *pAllocation = VK_NULL_HANDLE;
18918
18919 // 1. Create VkImage.
18920 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
18921 allocator->m_hDevice,
18922 pImageCreateInfo,
18923 allocator->GetAllocationCallbacks(),
18924 pImage);
18925 if(res >= 0)
18926 {
18927 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
18928 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
18929 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
18930
18931 // 2. Allocate memory using allocator.
18932 VkMemoryRequirements vkMemReq = {};
18933 bool requiresDedicatedAllocation = false;
18934 bool prefersDedicatedAllocation = false;
18935 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
18936 requiresDedicatedAllocation, prefersDedicatedAllocation);
18937
18938 res = allocator->AllocateMemory(
18939 vkMemReq,
18940 requiresDedicatedAllocation,
18941 prefersDedicatedAllocation,
18942 VK_NULL_HANDLE, // dedicatedBuffer
18943 UINT32_MAX, // dedicatedBufferUsage
18944 *pImage, // dedicatedImage
18945 *pAllocationCreateInfo,
18946 suballocType,
18947 1, // allocationCount
18948 pAllocation);
18949
18950 #if VMA_RECORDING_ENABLED
18951 if(allocator->GetRecorder() != VMA_NULL)
18952 {
18953 allocator->GetRecorder()->RecordCreateImage(
18954 allocator->GetCurrentFrameIndex(),
18955 *pImageCreateInfo,
18956 *pAllocationCreateInfo,
18957 *pAllocation);
18958 }
18959 #endif
18960
18961 if(res >= 0)
18962 {
18963 // 3. Bind image with memory.
18964 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
18965 {
18966 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
18967 }
18968 if(res >= 0)
18969 {
18970 // All steps succeeded.
18971 #if VMA_STATS_STRING_ENABLED
18972 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
18973 #endif
18974 if(pAllocationInfo != VMA_NULL)
18975 {
18976 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18977 }
18978
18979 return VK_SUCCESS;
18980 }
18981 allocator->FreeMemory(
18982 1, // allocationCount
18983 pAllocation);
18984 *pAllocation = VK_NULL_HANDLE;
18985 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18986 *pImage = VK_NULL_HANDLE;
18987 return res;
18988 }
18989 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
18990 *pImage = VK_NULL_HANDLE;
18991 return res;
18992 }
18993 return res;
18994 }
18995
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)18996 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
18997 VmaAllocator allocator,
18998 VkImage image,
18999 VmaAllocation allocation)
19000 {
19001 VMA_ASSERT(allocator);
19002
19003 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19004 {
19005 return;
19006 }
19007
19008 VMA_DEBUG_LOG("vmaDestroyImage");
19009
19010 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19011
19012 #if VMA_RECORDING_ENABLED
19013 if(allocator->GetRecorder() != VMA_NULL)
19014 {
19015 allocator->GetRecorder()->RecordDestroyImage(
19016 allocator->GetCurrentFrameIndex(),
19017 allocation);
19018 }
19019 #endif
19020
19021 if(image != VK_NULL_HANDLE)
19022 {
19023 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19024 }
19025 if(allocation != VK_NULL_HANDLE)
19026 {
19027 allocator->FreeMemory(
19028 1, // allocationCount
19029 &allocation);
19030 }
19031 }
19032
19033 #endif // #ifdef VMA_IMPLEMENTATION
19034