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 size_t maxBlockCount = SIZE_MAX;
2358 } VmaAllocatorCreateInfo;
2359
2360 /// Creates Allocator object.
2361 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
2362 const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
2363 VmaAllocator VMA_NULLABLE * VMA_NOT_NULL pAllocator);
2364
2365 /// Destroys allocator object.
2366 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
2367 VmaAllocator VMA_NULLABLE allocator);
2368
2369 /** \brief Information about existing #VmaAllocator object.
2370 */
2371 typedef struct VmaAllocatorInfo
2372 {
2373 /** \brief Handle to Vulkan instance object.
2374
2375 This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
2376 */
2377 VkInstance VMA_NOT_NULL instance;
2378 /** \brief Handle to Vulkan physical device object.
2379
2380 This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
2381 */
2382 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
2383 /** \brief Handle to Vulkan device object.
2384
2385 This is the same value as has been passed through VmaAllocatorCreateInfo::device.
2386 */
2387 VkDevice VMA_NOT_NULL device;
2388 } VmaAllocatorInfo;
2389
2390 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
2391
2392 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
2393 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
2394 */
2395 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator VMA_NOT_NULL allocator, VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
2396
2397 /**
2398 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
2399 You can access it here, without fetching it again on your own.
2400 */
2401 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
2402 VmaAllocator VMA_NOT_NULL allocator,
2403 const VkPhysicalDeviceProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceProperties);
2404
2405 /**
2406 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
2407 You can access it here, without fetching it again on your own.
2408 */
2409 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
2410 VmaAllocator VMA_NOT_NULL allocator,
2411 const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE * VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
2412
2413 /**
2414 \brief Given Memory Type Index, returns Property Flags of this memory type.
2415
2416 This is just a convenience function. Same information can be obtained using
2417 vmaGetMemoryProperties().
2418 */
2419 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
2420 VmaAllocator VMA_NOT_NULL allocator,
2421 uint32_t memoryTypeIndex,
2422 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
2423
2424 /** \brief Sets index of the current frame.
2425
2426 This function must be used if you make allocations with
2427 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
2428 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
2429 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
2430 become lost in the current frame.
2431 */
2432 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
2433 VmaAllocator VMA_NOT_NULL allocator,
2434 uint32_t frameIndex);
2435
2436 /** \brief Calculated statistics of memory usage in entire allocator.
2437 */
2438 typedef struct VmaStatInfo
2439 {
2440 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
2441 uint32_t blockCount;
2442 /// Number of #VmaAllocation allocation objects allocated.
2443 uint32_t allocationCount;
2444 /// Number of free ranges of memory between allocations.
2445 uint32_t unusedRangeCount;
2446 /// Total number of bytes occupied by all allocations.
2447 VkDeviceSize usedBytes;
2448 /// Total number of bytes occupied by unused ranges.
2449 VkDeviceSize unusedBytes;
2450 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
2451 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
2452 } VmaStatInfo;
2453
2454 /// General statistics from current state of Allocator.
2455 typedef struct VmaStats
2456 {
2457 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
2458 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
2459 VmaStatInfo total;
2460 } VmaStats;
2461
2462 /** \brief Retrieves statistics from current state of the Allocator.
2463
2464 This function is called "calculate" not "get" because it has to traverse all
2465 internal data structures, so it may be quite slow. For faster but more brief statistics
2466 suitable to be called every frame or every allocation, use vmaGetBudget().
2467
2468 Note that when using allocator from multiple threads, returned information may immediately
2469 become outdated.
2470 */
2471 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
2472 VmaAllocator VMA_NOT_NULL allocator,
2473 VmaStats* VMA_NOT_NULL pStats);
2474
2475 /** \brief Free empty block of the Allocator.
2476 */
2477 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
2478 VmaAllocator VMA_NOT_NULL allocator);
2479
2480 /** \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
2481 */
2482 typedef struct VmaBudget
2483 {
2484 /** \brief Sum size of all `VkDeviceMemory` blocks allocated from particular heap, in bytes.
2485 */
2486 VkDeviceSize blockBytes;
2487
2488 /** \brief Sum size of all allocations created in particular heap, in bytes.
2489
2490 Usually less or equal than `blockBytes`.
2491 Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
2492 available for new allocations or wasted due to fragmentation.
2493
2494 It might be greater than `blockBytes` if there are some allocations in lost state, as they account
2495 to this value as well.
2496 */
2497 VkDeviceSize allocationBytes;
2498
2499 /** \brief Estimated current memory usage of the program, in bytes.
2500
2501 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2502
2503 It might be different than `blockBytes` (usually higher) due to additional implicit objects
2504 also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
2505 `VkDeviceMemory` blocks allocated outside of this library, if any.
2506 */
2507 VkDeviceSize usage;
2508
2509 /** \brief Estimated amount of memory available to the program, in bytes.
2510
2511 Fetched from system using `VK_EXT_memory_budget` extension if enabled.
2512
2513 It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
2514 external to the program, like other programs also consuming system resources.
2515 Difference `budget - usage` is the amount of additional memory that can probably
2516 be allocated without problems. Exceeding the budget may result in various problems.
2517 */
2518 VkDeviceSize budget;
2519 } VmaBudget;
2520
2521 /** \brief Retrieves information about current memory budget for all memory heaps.
2522
2523 \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
2524
2525 This function is called "get" not "calculate" because it is very fast, suitable to be called
2526 every frame or every allocation. For more detailed statistics use vmaCalculateStats().
2527
2528 Note that when using allocator from multiple threads, returned information may immediately
2529 become outdated.
2530 */
2531 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
2532 VmaAllocator VMA_NOT_NULL allocator,
2533 VmaBudget* VMA_NOT_NULL pBudget);
2534
2535 #ifndef VMA_STATS_STRING_ENABLED
2536 #define VMA_STATS_STRING_ENABLED 1
2537 #endif
2538
2539 #if VMA_STATS_STRING_ENABLED
2540
2541 /// Builds and returns statistics as string in JSON format.
2542 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2543 */
2544 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2545 VmaAllocator VMA_NOT_NULL allocator,
2546 char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString,
2547 VkBool32 detailedMap);
2548
2549 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2550 VmaAllocator VMA_NOT_NULL allocator,
2551 char* VMA_NULLABLE pStatsString);
2552
2553 #endif // #if VMA_STATS_STRING_ENABLED
2554
2555 /** \struct VmaPool
2556 \brief Represents custom memory pool
2557
2558 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
2559 Call function vmaDestroyPool() to destroy it.
2560
2561 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
2562 */
2563 VK_DEFINE_HANDLE(VmaPool)
2564
2565 typedef enum VmaMemoryUsage
2566 {
2567 /** No intended memory usage specified.
2568 Use other members of VmaAllocationCreateInfo to specify your requirements.
2569 */
2570 VMA_MEMORY_USAGE_UNKNOWN = 0,
2571 /** Memory will be used on device only, so fast access from the device is preferred.
2572 It usually means device-local GPU (video) memory.
2573 No need to be mappable on host.
2574 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
2575
2576 Usage:
2577
2578 - Resources written and read by device, e.g. images used as attachments.
2579 - Resources transferred from host once (immutable) or infrequently and read by
2580 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
2581 (constant) buffers, and majority of other types of resources used on GPU.
2582
2583 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
2584 In such case, you are free to map it.
2585 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
2586 */
2587 VMA_MEMORY_USAGE_GPU_ONLY = 1,
2588 /** Memory will be mappable on host.
2589 It usually means CPU (system) memory.
2590 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
2591 CPU access is typically uncached. Writes may be write-combined.
2592 Resources created in this pool may still be accessible to the device, but access to them can be slow.
2593 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2594
2595 Usage: Staging copy of resources used as transfer source.
2596 */
2597 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2598 /**
2599 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2600 CPU access is typically uncached. Writes may be write-combined.
2601
2602 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures (with LINEAR layout), vertex buffers, uniform buffers updated every frame or every draw call.
2603 */
2604 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2605 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2606 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2607
2608 Usage:
2609
2610 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2611 - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
2612 */
2613 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2614 /** CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
2615
2616 Usage: Staging copy of resources moved from GPU memory to CPU memory as part
2617 of custom paging/residency mechanism, to be moved back to GPU memory when needed.
2618 */
2619 VMA_MEMORY_USAGE_CPU_COPY = 5,
2620 /** Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
2621 Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
2622
2623 Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
2624
2625 Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2626 */
2627 VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
2628
2629 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2630 } VmaMemoryUsage;
2631
2632 /// Flags to be passed as VmaAllocationCreateInfo::flags.
2633 typedef enum VmaAllocationCreateFlagBits {
2634 /** \brief Set this flag if the allocation should have its own memory block.
2635
2636 Use it for special, big resources, like fullscreen images used as attachments.
2637
2638 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2639 */
2640 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2641
2642 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2643
2644 If new allocation cannot be placed in any of the existing blocks, allocation
2645 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2646
2647 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2648 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2649
2650 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2651 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2652 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2653
2654 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2655
2656 Is it valid to use this flag for allocation made from memory type that is not
2657 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2658 useful if you need an allocation that is efficient to use on GPU
2659 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2660 support it (e.g. Intel GPU).
2661
2662 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2663 */
2664 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2665 /** Allocation created with this flag can become lost as a result of another
2666 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2667 must check it before use.
2668
2669 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2670 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2671
2672 For details about supporting lost allocations, see Lost Allocations
2673 chapter of User Guide on Main Page.
2674
2675 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2676 */
2677 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2678 /** While creating allocation using this flag, other allocations that were
2679 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2680
2681 For details about supporting lost allocations, see Lost Allocations
2682 chapter of User Guide on Main Page.
2683 */
2684 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2685 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2686 null-terminated string. Instead of copying pointer value, a local copy of the
2687 string is made and stored in allocation's `pUserData`. The string is automatically
2688 freed together with the allocation. It is also used in vmaBuildStatsString().
2689 */
2690 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2691 /** Allocation will be created from upper stack in a double stack pool.
2692
2693 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2694 */
2695 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2696 /** Create both buffer/image and allocation, but don't bind them together.
2697 It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
2698 The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
2699 Otherwise it is ignored.
2700 */
2701 VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
2702 /** Create allocation only if additional device memory required for it, if any, won't exceed
2703 memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
2704 */
2705 VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
2706
2707 /** Allocation strategy that chooses smallest possible free range for the
2708 allocation.
2709 */
2710 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2711 /** Allocation strategy that chooses biggest possible free range for the
2712 allocation.
2713 */
2714 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2715 /** Allocation strategy that chooses first suitable free range for the
2716 allocation.
2717
2718 "First" doesn't necessarily means the one with smallest offset in memory,
2719 but rather the one that is easiest and fastest to find.
2720 */
2721 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2722
2723 /** Allocation strategy that tries to minimize memory usage.
2724 */
2725 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2726 /** Allocation strategy that tries to minimize allocation time.
2727 */
2728 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2729 /** Allocation strategy that tries to minimize memory fragmentation.
2730 */
2731 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2732
2733 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2734 */
2735 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2736 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2737 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2738 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2739
2740 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2741 } VmaAllocationCreateFlagBits;
2742 typedef VkFlags VmaAllocationCreateFlags;
2743
2744 typedef struct VmaAllocationCreateInfo
2745 {
2746 /// Use #VmaAllocationCreateFlagBits enum.
2747 VmaAllocationCreateFlags flags;
2748 /** \brief Intended usage of memory.
2749
2750 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2751 If `pool` is not null, this member is ignored.
2752 */
2753 VmaMemoryUsage usage;
2754 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2755
2756 Leave 0 if you specify memory requirements in other way. \n
2757 If `pool` is not null, this member is ignored.*/
2758 VkMemoryPropertyFlags requiredFlags;
2759 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2760
2761 Set to 0 if no additional flags are prefered. \n
2762 If `pool` is not null, this member is ignored. */
2763 VkMemoryPropertyFlags preferredFlags;
2764 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2765
2766 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2767 it meets other requirements specified by this structure, with no further
2768 restrictions on memory type index. \n
2769 If `pool` is not null, this member is ignored.
2770 */
2771 uint32_t memoryTypeBits;
2772 /** \brief Pool that this allocation should be created in.
2773
2774 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2775 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2776 */
2777 VmaPool VMA_NULLABLE pool;
2778 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2779
2780 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2781 null or pointer to a null-terminated string. The string will be then copied to
2782 internal buffer, so it doesn't need to be valid after allocation call.
2783 */
2784 void* VMA_NULLABLE pUserData;
2785 } VmaAllocationCreateInfo;
2786
2787 /**
2788 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2789
2790 This algorithm tries to find a memory type that:
2791
2792 - Is allowed by memoryTypeBits.
2793 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
2794 - Matches intended usage.
2795 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2796
2797 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2798 from this function or any other allocating function probably means that your
2799 device doesn't support any memory type with requested features for the specific
2800 type of resource you want to use it for. Please check parameters of your
2801 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2802 */
2803 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
2804 VmaAllocator VMA_NOT_NULL allocator,
2805 uint32_t memoryTypeBits,
2806 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2807 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2808
2809 /**
2810 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2811
2812 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2813 It internally creates a temporary, dummy buffer that never has memory bound.
2814 It is just a convenience function, equivalent to calling:
2815
2816 - `vkCreateBuffer`
2817 - `vkGetBufferMemoryRequirements`
2818 - `vmaFindMemoryTypeIndex`
2819 - `vkDestroyBuffer`
2820 */
2821 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
2822 VmaAllocator VMA_NOT_NULL allocator,
2823 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2824 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2825 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2826
2827 /**
2828 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2829
2830 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2831 It internally creates a temporary, dummy image that never has memory bound.
2832 It is just a convenience function, equivalent to calling:
2833
2834 - `vkCreateImage`
2835 - `vkGetImageMemoryRequirements`
2836 - `vmaFindMemoryTypeIndex`
2837 - `vkDestroyImage`
2838 */
2839 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
2840 VmaAllocator VMA_NOT_NULL allocator,
2841 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2842 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2843 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
2844
2845 /// Flags to be passed as VmaPoolCreateInfo::flags.
2846 typedef enum VmaPoolCreateFlagBits {
2847 /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored.
2848
2849 This is an optional optimization flag.
2850
2851 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2852 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2853 knows exact type of your allocations so it can handle Buffer-Image Granularity
2854 in the optimal way.
2855
2856 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2857 exact type of such allocations is not known, so allocator must be conservative
2858 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2859 (wasted memory). In that case, if you can make sure you always allocate only
2860 buffers and linear images or only optimal images out of this pool, use this flag
2861 to make allocator disregard Buffer-Image Granularity and so make allocations
2862 faster and more optimal.
2863 */
2864 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2865
2866 /** \brief Enables alternative, linear allocation algorithm in this pool.
2867
2868 Specify this flag to enable linear allocation algorithm, which always creates
2869 new allocations after last one and doesn't reuse space from allocations freed in
2870 between. It trades memory consumption for simplified algorithm and data
2871 structure, which has better performance and uses less memory for metadata.
2872
2873 By using this flag, you can achieve behavior of free-at-once, stack,
2874 ring buffer, and double stack. For details, see documentation chapter
2875 \ref linear_algorithm.
2876
2877 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2878
2879 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2880 */
2881 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2882
2883 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2884
2885 It operates on a tree of blocks, each having size that is a power of two and
2886 a half of its parent's size. Comparing to default algorithm, this one provides
2887 faster allocation and deallocation and decreased external fragmentation,
2888 at the expense of more memory wasted (internal fragmentation).
2889
2890 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2891 */
2892 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2893
2894 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2895 */
2896 VMA_POOL_CREATE_ALGORITHM_MASK =
2897 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2898 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2899
2900 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2901 } VmaPoolCreateFlagBits;
2902 typedef VkFlags VmaPoolCreateFlags;
2903
2904 /** \brief Describes parameter of created #VmaPool.
2905 */
2906 typedef struct VmaPoolCreateInfo {
2907 /** \brief Vulkan memory type index to allocate this pool from.
2908 */
2909 uint32_t memoryTypeIndex;
2910 /** \brief Use combination of #VmaPoolCreateFlagBits.
2911 */
2912 VmaPoolCreateFlags flags;
2913 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2914
2915 Specify nonzero to set explicit, constant size of memory blocks used by this
2916 pool.
2917
2918 Leave 0 to use default and let the library manage block sizes automatically.
2919 Sizes of particular blocks may vary.
2920 */
2921 VkDeviceSize blockSize;
2922 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2923
2924 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2925 */
2926 size_t minBlockCount;
2927 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2928
2929 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2930
2931 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2932 throughout whole lifetime of this pool.
2933 */
2934 size_t maxBlockCount;
2935 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2936
2937 This value is used only when you make allocations with
2938 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2939 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2940
2941 For example, if you double-buffer your command buffers, so resources used for
2942 rendering in previous frame may still be in use by the GPU at the moment you
2943 allocate resources needed for the current frame, set this value to 1.
2944
2945 If you want to allow any allocations other than used in the current frame to
2946 become lost, set this value to 0.
2947 */
2948 uint32_t frameInUseCount;
2949 } VmaPoolCreateInfo;
2950
2951 /** \brief Describes parameter of existing #VmaPool.
2952 */
2953 typedef struct VmaPoolStats {
2954 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2955 */
2956 VkDeviceSize size;
2957 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2958 */
2959 VkDeviceSize unusedSize;
2960 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2961 */
2962 size_t allocationCount;
2963 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2964 */
2965 size_t unusedRangeCount;
2966 /** \brief Size of the largest continuous free memory region available for new allocation.
2967
2968 Making a new allocation of that size is not guaranteed to succeed because of
2969 possible additional margin required to respect alignment and buffer/image
2970 granularity.
2971 */
2972 VkDeviceSize unusedRangeSizeMax;
2973 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2974 */
2975 size_t blockCount;
2976 } VmaPoolStats;
2977
2978 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
2979
2980 @param allocator Allocator object.
2981 @param pCreateInfo Parameters of pool to create.
2982 @param[out] pPool Handle to created pool.
2983 */
2984 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
2985 VmaAllocator VMA_NOT_NULL allocator,
2986 const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
2987 VmaPool VMA_NULLABLE * VMA_NOT_NULL pPool);
2988
2989 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
2990 */
2991 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
2992 VmaAllocator VMA_NOT_NULL allocator,
2993 VmaPool VMA_NULLABLE pool);
2994
2995 /** \brief Retrieves statistics of existing #VmaPool object.
2996
2997 @param allocator Allocator object.
2998 @param pool Pool object.
2999 @param[out] pPoolStats Statistics of specified pool.
3000 */
3001 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
3002 VmaAllocator VMA_NOT_NULL allocator,
3003 VmaPool VMA_NOT_NULL pool,
3004 VmaPoolStats* VMA_NOT_NULL pPoolStats);
3005
3006 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
3007
3008 @param allocator Allocator object.
3009 @param pool Pool.
3010 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
3011 */
3012 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
3013 VmaAllocator VMA_NOT_NULL allocator,
3014 VmaPool VMA_NOT_NULL pool,
3015 size_t* VMA_NULLABLE pLostAllocationCount);
3016
3017 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
3018
3019 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3020 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
3021 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3022
3023 Possible return values:
3024
3025 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
3026 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3027 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3028 `VMA_ASSERT` is also fired in that case.
3029 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3030 */
3031 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator VMA_NOT_NULL allocator, VmaPool VMA_NOT_NULL pool);
3032
3033 /** \brief Retrieves name of a custom pool.
3034
3035 After the call `ppName` is either null or points to an internally-owned null-terminated string
3036 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
3037 destroyed or its name is changed using vmaSetPoolName().
3038 */
3039 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
3040 VmaAllocator VMA_NOT_NULL allocator,
3041 VmaPool VMA_NOT_NULL pool,
3042 const char* VMA_NULLABLE * VMA_NOT_NULL ppName);
3043
3044 /** \brief Sets name of a custom pool.
3045
3046 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
3047 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
3048 */
3049 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
3050 VmaAllocator VMA_NOT_NULL allocator,
3051 VmaPool VMA_NOT_NULL pool,
3052 const char* VMA_NULLABLE pName);
3053
3054 /** \struct VmaAllocation
3055 \brief Represents single memory allocation.
3056
3057 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
3058 plus unique offset.
3059
3060 There are multiple ways to create such object.
3061 You need to fill structure VmaAllocationCreateInfo.
3062 For more information see [Choosing memory type](@ref choosing_memory_type).
3063
3064 Although the library provides convenience functions that create Vulkan buffer or image,
3065 allocate memory for it and bind them together,
3066 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
3067 Allocation object can exist without buffer/image bound,
3068 binding can be done manually by the user, and destruction of it can be done
3069 independently of destruction of the allocation.
3070
3071 The object also remembers its size and some other information.
3072 To retrieve this information, use function vmaGetAllocationInfo() and inspect
3073 returned structure VmaAllocationInfo.
3074
3075 Some kinds allocations can be in lost state.
3076 For more information, see [Lost allocations](@ref lost_allocations).
3077 */
3078 VK_DEFINE_HANDLE(VmaAllocation)
3079
3080 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
3081 */
3082 typedef struct VmaAllocationInfo {
3083 /** \brief Memory type index that this allocation was allocated from.
3084
3085 It never changes.
3086 */
3087 uint32_t memoryType;
3088 /** \brief Handle to Vulkan memory object.
3089
3090 Same memory object can be shared by multiple allocations.
3091
3092 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3093
3094 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
3095 */
3096 VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
3097 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
3098
3099 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
3100 */
3101 VkDeviceSize offset;
3102 /** \brief Size of this allocation, in bytes.
3103
3104 It never changes, unless allocation is lost.
3105 */
3106 VkDeviceSize size;
3107 /** \brief Pointer to the beginning of this allocation as mapped data.
3108
3109 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
3110 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
3111
3112 It can change after call to vmaMapMemory(), vmaUnmapMemory().
3113 It can also change after call to vmaDefragment() if this allocation is passed to the function.
3114 */
3115 void* VMA_NULLABLE pMappedData;
3116 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
3117
3118 It can change after call to vmaSetAllocationUserData() for this allocation.
3119 */
3120 void* VMA_NULLABLE pUserData;
3121 } VmaAllocationInfo;
3122
3123 /** \brief General purpose memory allocation.
3124
3125 @param[out] pAllocation Handle to allocated memory.
3126 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3127
3128 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3129
3130 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
3131 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
3132 */
3133 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
3134 VmaAllocator VMA_NOT_NULL allocator,
3135 const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
3136 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3137 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3138 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3139
3140 /** \brief General purpose memory allocation for multiple allocation objects at once.
3141
3142 @param allocator Allocator object.
3143 @param pVkMemoryRequirements Memory requirements for each allocation.
3144 @param pCreateInfo Creation parameters for each alloction.
3145 @param allocationCount Number of allocations to make.
3146 @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
3147 @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
3148
3149 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
3150
3151 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
3152 It is just a general purpose allocation function able to make multiple allocations at once.
3153 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
3154
3155 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
3156 If any allocation fails, all allocations already made within this function call are also freed, so that when
3157 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
3158 */
3159 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
3160 VmaAllocator VMA_NOT_NULL allocator,
3161 const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
3162 const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
3163 size_t allocationCount,
3164 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3165 VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
3166
3167 /**
3168 @param[out] pAllocation Handle to allocated memory.
3169 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3170
3171 You should free the memory using vmaFreeMemory().
3172 */
3173 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
3174 VmaAllocator VMA_NOT_NULL allocator,
3175 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3176 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3177 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3178 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3179
3180 /// Function similar to vmaAllocateMemoryForBuffer().
3181 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
3182 VmaAllocator VMA_NOT_NULL allocator,
3183 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3184 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3185 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3186 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3187
3188 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
3189
3190 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
3191 */
3192 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
3193 VmaAllocator VMA_NOT_NULL allocator,
3194 const VmaAllocation VMA_NULLABLE allocation);
3195
3196 /** \brief Frees memory and destroys multiple allocations.
3197
3198 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
3199 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
3200 vmaAllocateMemoryPages() and other functions.
3201 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
3202
3203 Allocations in `pAllocations` array can come from any memory pools and types.
3204 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
3205 */
3206 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
3207 VmaAllocator VMA_NOT_NULL allocator,
3208 size_t allocationCount,
3209 const VmaAllocation VMA_NULLABLE * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
3210
3211 // OH ISSUE: VMA preAlloc
3212 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
3213 VmaAllocator VMA_NOT_NULL allocator,
3214 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3215 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3216 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3217 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3218
3219 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
3220 VmaAllocation VMA_NOT_NULL allocation,
3221 bool* VMA_NULLABLE pStats);
3222
3223 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
3224 VmaAllocation VMA_NOT_NULL allocation);
3225
3226 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
3227 VmaAllocator VMA_NOT_NULL allocator,
3228 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3229 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
3230 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3231 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3232
3233 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
3234 VmaAllocator VMA_NOT_NULL allocator,
3235 const VmaAllocation VMA_NULLABLE allocation);
3236
3237 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(
3238 VmaAllocator VMA_NOT_NULL allocator,
3239 uint32_t* VMA_NULLABLE pStats);
3240
3241 /** \brief Deprecated.
3242
3243 \deprecated
3244 In version 2.2.0 it used to try to change allocation's size without moving or reallocating it.
3245 In current version it returns `VK_SUCCESS` only if `newSize` equals current allocation's size.
3246 Otherwise returns `VK_ERROR_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed.
3247 */
3248 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
3249 VmaAllocator VMA_NOT_NULL allocator,
3250 VmaAllocation VMA_NOT_NULL allocation,
3251 VkDeviceSize newSize);
3252
3253 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
3254
3255 Current paramters of given allocation are returned in `pAllocationInfo`.
3256
3257 This function also atomically "touches" allocation - marks it as used in current frame,
3258 just like vmaTouchAllocation().
3259 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
3260
3261 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
3262 you can avoid calling it too often.
3263
3264 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
3265 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
3266 (e.g. due to defragmentation or allocation becoming lost).
3267 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
3268 */
3269 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
3270 VmaAllocator VMA_NOT_NULL allocator,
3271 VmaAllocation VMA_NOT_NULL allocation,
3272 VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
3273
3274 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
3275
3276 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3277 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
3278 It then also atomically "touches" the allocation - marks it as used in current frame,
3279 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
3280
3281 If the allocation is in lost state, the function returns `VK_FALSE`.
3282 Memory of such allocation, as well as buffer or image bound to it, should not be used.
3283 Lost allocation and the buffer/image still need to be destroyed.
3284
3285 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
3286 this function always returns `VK_TRUE`.
3287 */
3288 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
3289 VmaAllocator VMA_NOT_NULL allocator,
3290 VmaAllocation VMA_NOT_NULL allocation);
3291
3292 /** \brief Sets pUserData in given allocation to new value.
3293
3294 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
3295 pUserData must be either null, or pointer to a null-terminated string. The function
3296 makes local copy of the string and sets it as allocation's `pUserData`. String
3297 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
3298 you can free it after this call. String previously pointed by allocation's
3299 pUserData is freed from memory.
3300
3301 If the flag was not used, the value of pointer `pUserData` is just copied to
3302 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
3303 as a pointer, ordinal number or some handle to you own data.
3304 */
3305 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
3306 VmaAllocator VMA_NOT_NULL allocator,
3307 VmaAllocation VMA_NOT_NULL allocation,
3308 void* VMA_NULLABLE pUserData);
3309
3310 /** \brief Creates new allocation that is in lost state from the beginning.
3311
3312 It can be useful if you need a dummy, non-null allocation.
3313
3314 You still need to destroy created object using vmaFreeMemory().
3315
3316 Returned allocation is not tied to any specific memory pool or memory type and
3317 not bound to any image or buffer. It has size = 0. It cannot be turned into
3318 a real, non-empty allocation.
3319 */
3320 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
3321 VmaAllocator VMA_NOT_NULL allocator,
3322 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation);
3323
3324 /** \brief Maps memory represented by given allocation and returns pointer to it.
3325
3326 Maps memory represented by given allocation to make it accessible to CPU code.
3327 When succeeded, `*ppData` contains pointer to first byte of this memory.
3328 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
3329 correctly offseted to the beginning of region assigned to this particular
3330 allocation.
3331
3332 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
3333 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
3334 multiple times simultaneously, it is safe to call this function on allocations
3335 assigned to the same memory block. Actual Vulkan memory will be mapped on first
3336 mapping and unmapped on last unmapping.
3337
3338 If the function succeeded, you must call vmaUnmapMemory() to unmap the
3339 allocation when mapping is no longer needed or before freeing the allocation, at
3340 the latest.
3341
3342 It also safe to call this function multiple times on the same allocation. You
3343 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
3344
3345 It is also safe to call this function on allocation created with
3346 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
3347 You must still call vmaUnmapMemory() same number of times as you called
3348 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
3349 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
3350
3351 This function fails when used on allocation made in memory type that is not
3352 `HOST_VISIBLE`.
3353
3354 This function always fails when called for allocation that was created with
3355 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
3356 mapped.
3357
3358 This function doesn't automatically flush or invalidate caches.
3359 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3360 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3361 */
3362 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
3363 VmaAllocator VMA_NOT_NULL allocator,
3364 VmaAllocation VMA_NOT_NULL allocation,
3365 void* VMA_NULLABLE * VMA_NOT_NULL ppData);
3366
3367 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
3368
3369 For details, see description of vmaMapMemory().
3370
3371 This function doesn't automatically flush or invalidate caches.
3372 If the allocation is made from a memory types that is not `HOST_COHERENT`,
3373 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
3374 */
3375 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
3376 VmaAllocator VMA_NOT_NULL allocator,
3377 VmaAllocation VMA_NOT_NULL allocation);
3378
3379 /** \brief Flushes memory of given allocation.
3380
3381 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
3382 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
3383 Unmap operation doesn't do that automatically.
3384
3385 - `offset` must be relative to the beginning of allocation.
3386 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3387 - `offset` and `size` don't have to be aligned.
3388 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3389 - If `size` is 0, this call is ignored.
3390 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3391 this call is ignored.
3392
3393 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3394 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3395 Do not pass allocation's offset as `offset`!!!
3396
3397 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3398 called, otherwise `VK_SUCCESS`.
3399 */
3400 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
3401 VmaAllocator VMA_NOT_NULL allocator,
3402 VmaAllocation VMA_NOT_NULL allocation,
3403 VkDeviceSize offset,
3404 VkDeviceSize size);
3405
3406 /** \brief Invalidates memory of given allocation.
3407
3408 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
3409 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
3410 Map operation doesn't do that automatically.
3411
3412 - `offset` must be relative to the beginning of allocation.
3413 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
3414 - `offset` and `size` don't have to be aligned.
3415 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
3416 - If `size` is 0, this call is ignored.
3417 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
3418 this call is ignored.
3419
3420 Warning! `offset` and `size` are relative to the contents of given `allocation`.
3421 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
3422 Do not pass allocation's offset as `offset`!!!
3423
3424 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
3425 it is called, otherwise `VK_SUCCESS`.
3426 */
3427 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
3428 VmaAllocator VMA_NOT_NULL allocator,
3429 VmaAllocation VMA_NOT_NULL allocation,
3430 VkDeviceSize offset,
3431 VkDeviceSize size);
3432
3433 /** \brief Flushes memory of given set of allocations.
3434
3435 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3436 For more information, see documentation of vmaFlushAllocation().
3437
3438 \param allocator
3439 \param allocationCount
3440 \param allocations
3441 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
3442 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
3443
3444 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
3445 called, otherwise `VK_SUCCESS`.
3446 */
3447 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
3448 VmaAllocator VMA_NOT_NULL allocator,
3449 uint32_t allocationCount,
3450 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3451 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3452 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3453
3454 /** \brief Invalidates memory of given set of allocations.
3455
3456 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
3457 For more information, see documentation of vmaInvalidateAllocation().
3458
3459 \param allocator
3460 \param allocationCount
3461 \param allocations
3462 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
3463 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
3464
3465 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
3466 called, otherwise `VK_SUCCESS`.
3467 */
3468 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
3469 VmaAllocator VMA_NOT_NULL allocator,
3470 uint32_t allocationCount,
3471 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
3472 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
3473 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
3474
3475 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
3476
3477 @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
3478
3479 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
3480 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
3481 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
3482
3483 Possible return values:
3484
3485 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
3486 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
3487 - `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
3488 `VMA_ASSERT` is also fired in that case.
3489 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
3490 */
3491 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator VMA_NOT_NULL allocator, uint32_t memoryTypeBits);
3492
3493 /** \struct VmaDefragmentationContext
3494 \brief Represents Opaque object that represents started defragmentation process.
3495
3496 Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
3497 Call function vmaDefragmentationEnd() to destroy it.
3498 */
3499 VK_DEFINE_HANDLE(VmaDefragmentationContext)
3500
3501 /// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
3502 typedef enum VmaDefragmentationFlagBits {
3503 VMA_DEFRAGMENTATION_FLAG_INCREMENTAL = 0x1,
3504 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
3505 } VmaDefragmentationFlagBits;
3506 typedef VkFlags VmaDefragmentationFlags;
3507
3508 /** \brief Parameters for defragmentation.
3509
3510 To be used with function vmaDefragmentationBegin().
3511 */
3512 typedef struct VmaDefragmentationInfo2 {
3513 /** \brief Reserved for future use. Should be 0.
3514 */
3515 VmaDefragmentationFlags flags;
3516 /** \brief Number of allocations in `pAllocations` array.
3517 */
3518 uint32_t allocationCount;
3519 /** \brief Pointer to array of allocations that can be defragmented.
3520
3521 The array should have `allocationCount` elements.
3522 The array should not contain nulls.
3523 Elements in the array should be unique - same allocation cannot occur twice.
3524 It is safe to pass allocations that are in the lost state - they are ignored.
3525 All allocations not present in this array are considered non-moveable during this defragmentation.
3526 */
3527 const VmaAllocation VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations;
3528 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
3529
3530 The array should have `allocationCount` elements.
3531 You can pass null if you are not interested in this information.
3532 */
3533 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged;
3534 /** \brief Numer of pools in `pPools` array.
3535 */
3536 uint32_t poolCount;
3537 /** \brief Either null or pointer to array of pools to be defragmented.
3538
3539 All the allocations in the specified pools can be moved during defragmentation
3540 and there is no way to check if they were really moved as in `pAllocationsChanged`,
3541 so you must query all the allocations in all these pools for new `VkDeviceMemory`
3542 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
3543 and images bound to them.
3544
3545 The array should have `poolCount` elements.
3546 The array should not contain nulls.
3547 Elements in the array should be unique - same pool cannot occur twice.
3548
3549 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
3550 It might be more efficient.
3551 */
3552 const VmaPool VMA_NOT_NULL * VMA_NULLABLE VMA_LEN_IF_NOT_NULL(poolCount) pPools;
3553 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
3554
3555 `VK_WHOLE_SIZE` means no limit.
3556 */
3557 VkDeviceSize maxCpuBytesToMove;
3558 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
3559
3560 `UINT32_MAX` means no limit.
3561 */
3562 uint32_t maxCpuAllocationsToMove;
3563 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
3564
3565 `VK_WHOLE_SIZE` means no limit.
3566 */
3567 VkDeviceSize maxGpuBytesToMove;
3568 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
3569
3570 `UINT32_MAX` means no limit.
3571 */
3572 uint32_t maxGpuAllocationsToMove;
3573 /** \brief Optional. Command buffer where GPU copy commands will be posted.
3574
3575 If not null, it must be a valid command buffer handle that supports Transfer queue type.
3576 It must be in the recording state and outside of a render pass instance.
3577 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
3578
3579 Passing null means that only CPU defragmentation will be performed.
3580 */
3581 VkCommandBuffer VMA_NULLABLE commandBuffer;
3582 } VmaDefragmentationInfo2;
3583
3584 typedef struct VmaDefragmentationPassMoveInfo {
3585 VmaAllocation VMA_NOT_NULL allocation;
3586 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory;
3587 VkDeviceSize offset;
3588 } VmaDefragmentationPassMoveInfo;
3589
3590 /** \brief Parameters for incremental defragmentation steps.
3591
3592 To be used with function vmaBeginDefragmentationPass().
3593 */
3594 typedef struct VmaDefragmentationPassInfo {
3595 uint32_t moveCount;
3596 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
3597 } VmaDefragmentationPassInfo;
3598
3599 /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
3600
3601 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3602 */
3603 typedef struct VmaDefragmentationInfo {
3604 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
3605
3606 Default is `VK_WHOLE_SIZE`, which means no limit.
3607 */
3608 VkDeviceSize maxBytesToMove;
3609 /** \brief Maximum number of allocations that can be moved to different place.
3610
3611 Default is `UINT32_MAX`, which means no limit.
3612 */
3613 uint32_t maxAllocationsToMove;
3614 } VmaDefragmentationInfo;
3615
3616 /** \brief Statistics returned by function vmaDefragment(). */
3617 typedef struct VmaDefragmentationStats {
3618 /// Total number of bytes that have been copied while moving allocations to different places.
3619 VkDeviceSize bytesMoved;
3620 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
3621 VkDeviceSize bytesFreed;
3622 /// Number of allocations that have been moved to different places.
3623 uint32_t allocationsMoved;
3624 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
3625 uint32_t deviceMemoryBlocksFreed;
3626 } VmaDefragmentationStats;
3627
3628 /** \brief Begins defragmentation process.
3629
3630 @param allocator Allocator object.
3631 @param pInfo Structure filled with parameters of defragmentation.
3632 @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
3633 @param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
3634 @return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error.
3635
3636 Use this function instead of old, deprecated vmaDefragment().
3637
3638 Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
3639
3640 - You should not use any of allocations passed as `pInfo->pAllocations` or
3641 any allocations that belong to pools passed as `pInfo->pPools`,
3642 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
3643 their data.
3644 - Some mutexes protecting internal data structures may be locked, so trying to
3645 make or free any allocations, bind buffers or images, map memory, or launch
3646 another simultaneous defragmentation in between may cause stall (when done on
3647 another thread) or deadlock (when done on the same thread), unless you are
3648 100% sure that defragmented allocations are in different pools.
3649 - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
3650 They become valid after call to vmaDefragmentationEnd().
3651 - If `pInfo->commandBuffer` is not null, you must submit that command buffer
3652 and make sure it finished execution before calling vmaDefragmentationEnd().
3653
3654 For more information and important limitations regarding defragmentation, see documentation chapter:
3655 [Defragmentation](@ref defragmentation).
3656 */
3657 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
3658 VmaAllocator VMA_NOT_NULL allocator,
3659 const VmaDefragmentationInfo2* VMA_NOT_NULL pInfo,
3660 VmaDefragmentationStats* VMA_NULLABLE pStats,
3661 VmaDefragmentationContext VMA_NULLABLE * VMA_NOT_NULL pContext);
3662
3663 /** \brief Ends defragmentation process.
3664
3665 Use this function to finish defragmentation started by vmaDefragmentationBegin().
3666 It is safe to pass `context == null`. The function then does nothing.
3667 */
3668 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
3669 VmaAllocator VMA_NOT_NULL allocator,
3670 VmaDefragmentationContext VMA_NULLABLE context);
3671
3672 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
3673 VmaAllocator VMA_NOT_NULL allocator,
3674 VmaDefragmentationContext VMA_NULLABLE context,
3675 VmaDefragmentationPassInfo* VMA_NOT_NULL pInfo
3676 );
3677 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
3678 VmaAllocator VMA_NOT_NULL allocator,
3679 VmaDefragmentationContext VMA_NULLABLE context
3680 );
3681
3682 /** \brief Deprecated. Compacts memory by moving allocations.
3683
3684 @param pAllocations Array of allocations that can be moved during this compation.
3685 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
3686 @param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information.
3687 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
3688 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
3689 @return `VK_SUCCESS` if completed, negative error code in case of error.
3690
3691 \deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
3692
3693 This function works by moving allocations to different places (different
3694 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
3695 usage. Only allocations that are in `pAllocations` array can be moved. All other
3696 allocations are considered nonmovable in this call. Basic rules:
3697
3698 - Only allocations made in memory types that have
3699 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
3700 flags can be compacted. You may pass other allocations but it makes no sense -
3701 these will never be moved.
3702 - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
3703 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
3704 passed to this function that come from such pools are ignored.
3705 - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
3706 created as dedicated allocations for any other reason are also ignored.
3707 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
3708 flag can be compacted. If not persistently mapped, memory will be mapped
3709 temporarily inside this function if needed.
3710 - You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
3711
3712 The function also frees empty `VkDeviceMemory` blocks.
3713
3714 Warning: This function may be time-consuming, so you shouldn't call it too often
3715 (like after every resource creation/destruction).
3716 You can call it on special occasions (like when reloading a game level or
3717 when you just destroyed a lot of objects). Calling it every frame may be OK, but
3718 you should measure that on your platform.
3719
3720 For more information, see [Defragmentation](@ref defragmentation) chapter.
3721 */
3722 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
3723 VmaAllocator VMA_NOT_NULL allocator,
3724 const VmaAllocation VMA_NOT_NULL * VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
3725 size_t allocationCount,
3726 VkBool32* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationsChanged,
3727 const VmaDefragmentationInfo* VMA_NULLABLE pDefragmentationInfo,
3728 VmaDefragmentationStats* VMA_NULLABLE pDefragmentationStats);
3729
3730 /** \brief Binds buffer to allocation.
3731
3732 Binds specified buffer to region of memory represented by specified allocation.
3733 Gets `VkDeviceMemory` handle and offset from the allocation.
3734 If you want to create a buffer, allocate memory for it and bind them together separately,
3735 you should use this function for binding instead of standard `vkBindBufferMemory()`,
3736 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3737 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3738 (which is illegal in Vulkan).
3739
3740 It is recommended to use function vmaCreateBuffer() instead of this one.
3741 */
3742 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
3743 VmaAllocator VMA_NOT_NULL allocator,
3744 VmaAllocation VMA_NOT_NULL allocation,
3745 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
3746
3747 /** \brief Binds buffer to allocation with additional parameters.
3748
3749 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3750 @param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
3751
3752 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
3753
3754 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3755 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3756 */
3757 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
3758 VmaAllocator VMA_NOT_NULL allocator,
3759 VmaAllocation VMA_NOT_NULL allocation,
3760 VkDeviceSize allocationLocalOffset,
3761 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
3762 const void* VMA_NULLABLE pNext);
3763
3764 /** \brief Binds image to allocation.
3765
3766 Binds specified image to region of memory represented by specified allocation.
3767 Gets `VkDeviceMemory` handle and offset from the allocation.
3768 If you want to create an image, allocate memory for it and bind them together separately,
3769 you should use this function for binding instead of standard `vkBindImageMemory()`,
3770 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
3771 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3772 (which is illegal in Vulkan).
3773
3774 It is recommended to use function vmaCreateImage() instead of this one.
3775 */
3776 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
3777 VmaAllocator VMA_NOT_NULL allocator,
3778 VmaAllocation VMA_NOT_NULL allocation,
3779 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
3780
3781 /** \brief Binds image to allocation with additional parameters.
3782
3783 @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
3784 @param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
3785
3786 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
3787
3788 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
3789 or with VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_1`. Otherwise the call fails.
3790 */
3791 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
3792 VmaAllocator VMA_NOT_NULL allocator,
3793 VmaAllocation VMA_NOT_NULL allocation,
3794 VkDeviceSize allocationLocalOffset,
3795 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
3796 const void* VMA_NULLABLE pNext);
3797
3798 /**
3799 @param[out] pBuffer Buffer that was created.
3800 @param[out] pAllocation Allocation that was created.
3801 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3802
3803 This function automatically:
3804
3805 -# Creates buffer.
3806 -# Allocates appropriate memory for it.
3807 -# Binds the buffer with the memory.
3808
3809 If any of these operations fail, buffer and allocation are not created,
3810 returned value is negative error code, *pBuffer and *pAllocation are null.
3811
3812 If the function succeeded, you must destroy both buffer and allocation when you
3813 no longer need them using either convenience function vmaDestroyBuffer() or
3814 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3815
3816 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3817 VK_KHR_dedicated_allocation extension is used internally to query driver whether
3818 it requires or prefers the new buffer to have dedicated allocation. If yes,
3819 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3820 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3821 allocation for this buffer, just like when using
3822 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3823 */
3824 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
3825 VmaAllocator VMA_NOT_NULL allocator,
3826 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
3827 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3828 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer,
3829 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3830 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3831
3832 /** \brief Destroys Vulkan buffer and frees allocated memory.
3833
3834 This is just a convenience function equivalent to:
3835
3836 \code
3837 vkDestroyBuffer(device, buffer, allocationCallbacks);
3838 vmaFreeMemory(allocator, allocation);
3839 \endcode
3840
3841 It it safe to pass null as buffer and/or allocation.
3842 */
3843 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
3844 VmaAllocator VMA_NOT_NULL allocator,
3845 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
3846 VmaAllocation VMA_NULLABLE allocation);
3847
3848 /// Function similar to vmaCreateBuffer().
3849 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
3850 VmaAllocator VMA_NOT_NULL allocator,
3851 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
3852 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
3853 VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage,
3854 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
3855 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
3856
3857 /** \brief Destroys Vulkan image and frees allocated memory.
3858
3859 This is just a convenience function equivalent to:
3860
3861 \code
3862 vkDestroyImage(device, image, allocationCallbacks);
3863 vmaFreeMemory(allocator, allocation);
3864 \endcode
3865
3866 It it safe to pass null as image and/or allocation.
3867 */
3868 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
3869 VmaAllocator VMA_NOT_NULL allocator,
3870 VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
3871 VmaAllocation VMA_NULLABLE allocation);
3872
3873 // OH ISSUE: VMA preAlloc
3874 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
3875 VmaAllocator VMA_NOT_NULL allocator,
3876 VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage);
3877
3878 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
3879 VmaAllocator VMA_NOT_NULL allocator,
3880 VkImage VMA_NULLABLE_NON_DISPATCHABLE image);
3881
3882 #ifdef __cplusplus
3883 }
3884 #endif
3885
3886 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3887
3888 // For Visual Studio IntelliSense.
3889 #if defined(__cplusplus) && defined(__INTELLISENSE__)
3890 #define VMA_IMPLEMENTATION
3891 #endif
3892
3893 #ifdef VMA_IMPLEMENTATION
3894 #undef VMA_IMPLEMENTATION
3895
3896 #include <cstdint>
3897 #include <cstdlib>
3898 #include <cstring>
3899 #include <utility>
3900
3901 /*******************************************************************************
3902 CONFIGURATION SECTION
3903
3904 Define some of these macros before each #include of this header or change them
3905 here if you need other then default behavior depending on your environment.
3906 */
3907
3908 /*
3909 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3910 internally, like:
3911
3912 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3913 */
3914 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3915 #define VMA_STATIC_VULKAN_FUNCTIONS 1
3916 #endif
3917
3918 /*
3919 Define this macro to 1 to make the library fetch pointers to Vulkan functions
3920 internally, like:
3921
3922 vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(m_hDevice, vkAllocateMemory);
3923 */
3924 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
3925 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
3926 #endif
3927
3928 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
3929 //#define VMA_USE_STL_CONTAINERS 1
3930
3931 /* Set this macro to 1 to make the library including and using STL containers:
3932 std::pair, std::vector, std::list, std::unordered_map.
3933
3934 Set it to 0 or undefined to make the library using its own implementation of
3935 the containers.
3936 */
3937 #if VMA_USE_STL_CONTAINERS
3938 #define VMA_USE_STL_VECTOR 1
3939 #define VMA_USE_STL_UNORDERED_MAP 1
3940 #define VMA_USE_STL_LIST 1
3941 #endif
3942
3943 #ifndef VMA_USE_STL_SHARED_MUTEX
3944 // Compiler conforms to C++17.
3945 #if __cplusplus >= 201703L
3946 #define VMA_USE_STL_SHARED_MUTEX 1
3947 // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
3948 // Otherwise it's always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
3949 // See: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
3950 #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
3951 #define VMA_USE_STL_SHARED_MUTEX 1
3952 #else
3953 #define VMA_USE_STL_SHARED_MUTEX 0
3954 #endif
3955 #endif
3956
3957 /*
3958 THESE INCLUDES ARE NOT ENABLED BY DEFAULT.
3959 Library has its own container implementation.
3960 */
3961 #if VMA_USE_STL_VECTOR
3962 #include <vector>
3963 #endif
3964
3965 #if VMA_USE_STL_UNORDERED_MAP
3966 #include <unordered_map>
3967 #endif
3968
3969 #if VMA_USE_STL_LIST
3970 #include <list>
3971 #endif
3972
3973 /*
3974 Following headers are used in this CONFIGURATION section only, so feel free to
3975 remove them if not needed.
3976 */
3977 #include <cassert> // for assert
3978 #include <algorithm> // for min, max
3979 #include <mutex>
3980
3981 #ifndef VMA_NULL
3982 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3983 #define VMA_NULL nullptr
3984 #endif
3985
3986 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3987 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)3988 void *aligned_alloc(size_t alignment, size_t size)
3989 {
3990 // alignment must be >= sizeof(void*)
3991 if(alignment < sizeof(void*))
3992 {
3993 alignment = sizeof(void*);
3994 }
3995
3996 return memalign(alignment, size);
3997 }
3998 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
3999 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)4000 void *aligned_alloc(size_t alignment, size_t size)
4001 {
4002 // alignment must be >= sizeof(void*)
4003 if(alignment < sizeof(void*))
4004 {
4005 alignment = sizeof(void*);
4006 }
4007
4008 void *pointer;
4009 if(posix_memalign(&pointer, alignment, size) == 0)
4010 return pointer;
4011 return VMA_NULL;
4012 }
4013 #endif
4014
4015 // If your compiler is not compatible with C++11 and definition of
4016 // aligned_alloc() function is missing, uncommeting following line may help:
4017
4018 //#include <malloc.h>
4019
4020 // Normal assert to check for programmer's errors, especially in Debug configuration.
4021 #ifndef VMA_ASSERT
4022 #ifdef NDEBUG
4023 #define VMA_ASSERT(expr)
4024 #else
4025 #define VMA_ASSERT(expr) assert(expr)
4026 #endif
4027 #endif
4028
4029 // Assert that will be called very often, like inside data structures e.g. operator[].
4030 // Making it non-empty can make program slow.
4031 #ifndef VMA_HEAVY_ASSERT
4032 #ifdef NDEBUG
4033 #define VMA_HEAVY_ASSERT(expr)
4034 #else
4035 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
4036 #endif
4037 #endif
4038
4039 #ifndef VMA_ALIGN_OF
4040 #define VMA_ALIGN_OF(type) (__alignof(type))
4041 #endif
4042
4043 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
4044 #if defined(_WIN32)
4045 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
4046 #else
4047 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
4048 #endif
4049 #endif
4050
4051 #ifndef VMA_SYSTEM_FREE
4052 #if defined(_WIN32)
4053 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
4054 #else
4055 #define VMA_SYSTEM_FREE(ptr) free(ptr)
4056 #endif
4057 #endif
4058
4059 #ifndef VMA_MIN
4060 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
4061 #endif
4062
4063 #ifndef VMA_MAX
4064 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
4065 #endif
4066
4067 #ifndef VMA_SWAP
4068 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
4069 #endif
4070
4071 #ifndef VMA_SORT
4072 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
4073 #endif
4074
4075 #ifndef VMA_DEBUG_LOG
4076 #define VMA_DEBUG_LOG(format, ...)
4077 /*
4078 #define VMA_DEBUG_LOG(format, ...) do { \
4079 printf(format, __VA_ARGS__); \
4080 printf("\n"); \
4081 } while(false)
4082 */
4083 #endif
4084
4085 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
4086 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)4087 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
4088 {
4089 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
4090 }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)4091 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
4092 {
4093 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
4094 }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)4095 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
4096 {
4097 snprintf(outStr, strLen, "%p", ptr);
4098 }
4099 #endif
4100
4101 #ifndef VMA_MUTEX
4102 class VmaMutex
4103 {
4104 public:
Lock()4105 void Lock() { m_Mutex.lock(); }
Unlock()4106 void Unlock() { m_Mutex.unlock(); }
TryLock()4107 bool TryLock() { return m_Mutex.try_lock(); }
4108 private:
4109 std::mutex m_Mutex;
4110 };
4111 #define VMA_MUTEX VmaMutex
4112 #endif
4113
4114 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
4115 #ifndef VMA_RW_MUTEX
4116 #if VMA_USE_STL_SHARED_MUTEX
4117 // Use std::shared_mutex from C++17.
4118 #include <shared_mutex>
4119 class VmaRWMutex
4120 {
4121 public:
LockRead()4122 void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()4123 void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()4124 bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()4125 void LockWrite() { m_Mutex.lock(); }
UnlockWrite()4126 void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()4127 bool TryLockWrite() { return m_Mutex.try_lock(); }
4128 private:
4129 std::shared_mutex m_Mutex;
4130 };
4131 #define VMA_RW_MUTEX VmaRWMutex
4132 #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
4133 // Use SRWLOCK from WinAPI.
4134 // Minimum supported client = Windows Vista, server = Windows Server 2008.
4135 class VmaRWMutex
4136 {
4137 public:
VmaRWMutex()4138 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()4139 void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()4140 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()4141 bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()4142 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()4143 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()4144 bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
4145 private:
4146 SRWLOCK m_Lock;
4147 };
4148 #define VMA_RW_MUTEX VmaRWMutex
4149 #else
4150 // Less efficient fallback: Use normal mutex.
4151 class VmaRWMutex
4152 {
4153 public:
LockRead()4154 void LockRead() { m_Mutex.Lock(); }
UnlockRead()4155 void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()4156 bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()4157 void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()4158 void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()4159 bool TryLockWrite() { return m_Mutex.TryLock(); }
4160 private:
4161 VMA_MUTEX m_Mutex;
4162 };
4163 #define VMA_RW_MUTEX VmaRWMutex
4164 #endif // #if VMA_USE_STL_SHARED_MUTEX
4165 #endif // #ifndef VMA_RW_MUTEX
4166
4167 /*
4168 If providing your own implementation, you need to implement a subset of std::atomic.
4169 */
4170 #ifndef VMA_ATOMIC_UINT32
4171 #include <atomic>
4172 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
4173 #endif
4174
4175 #ifndef VMA_ATOMIC_UINT64
4176 #include <atomic>
4177 #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
4178 #endif
4179
4180 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
4181 /**
4182 Every allocation will have its own memory block.
4183 Define to 1 for debugging purposes only.
4184 */
4185 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
4186 #endif
4187
4188 #ifndef VMA_DEBUG_ALIGNMENT
4189 /**
4190 Minimum alignment of all allocations, in bytes.
4191 Set to more than 1 for debugging purposes only. Must be power of two.
4192 */
4193 #define VMA_DEBUG_ALIGNMENT (1)
4194 #endif
4195
4196 #ifndef VMA_DEBUG_MARGIN
4197 /**
4198 Minimum margin before and after every allocation, in bytes.
4199 Set nonzero for debugging purposes only.
4200 */
4201 #define VMA_DEBUG_MARGIN (0)
4202 #endif
4203
4204 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
4205 /**
4206 Define this macro to 1 to automatically fill new allocations and destroyed
4207 allocations with some bit pattern.
4208 */
4209 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
4210 #endif
4211
4212 #ifndef VMA_DEBUG_DETECT_CORRUPTION
4213 /**
4214 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
4215 enable writing magic value to the margin before and after every allocation and
4216 validating it, so that memory corruptions (out-of-bounds writes) are detected.
4217 */
4218 #define VMA_DEBUG_DETECT_CORRUPTION (0)
4219 #endif
4220
4221 #ifndef VMA_DEBUG_GLOBAL_MUTEX
4222 /**
4223 Set this to 1 for debugging purposes only, to enable single mutex protecting all
4224 entry calls to the library. Can be useful for debugging multithreading issues.
4225 */
4226 #define VMA_DEBUG_GLOBAL_MUTEX (0)
4227 #endif
4228
4229 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
4230 /**
4231 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
4232 Set to more than 1 for debugging purposes only. Must be power of two.
4233 */
4234 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
4235 #endif
4236
4237 #ifndef VMA_SMALL_HEAP_MAX_SIZE
4238 /// Maximum size of a memory heap in Vulkan to consider it "small".
4239 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
4240 #endif
4241
4242 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
4243 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
4244 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
4245 #endif
4246
4247 #ifndef VMA_CLASS_NO_COPY
4248 #define VMA_CLASS_NO_COPY(className) \
4249 private: \
4250 className(const className&) = delete; \
4251 className& operator=(const className&) = delete;
4252 #endif
4253
4254 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
4255
4256 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
4257 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
4258
4259 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
4260 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
4261
4262 /*******************************************************************************
4263 END OF CONFIGURATION
4264 */
4265
4266 // # Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
4267
4268 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
4269 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
4270 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
4271
4272 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
4273
4274 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
4275 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
4276
4277 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)4278 static inline uint32_t VmaCountBitsSet(uint32_t v)
4279 {
4280 uint32_t c = v - ((v >> 1) & 0x55555555);
4281 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
4282 c = ((c >> 4) + c) & 0x0F0F0F0F;
4283 c = ((c >> 8) + c) & 0x00FF00FF;
4284 c = ((c >> 16) + c) & 0x0000FFFF;
4285 return c;
4286 }
4287
4288 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
4289 // Use types like uint32_t, uint64_t as T.
4290 template <typename T>
VmaAlignUp(T val,T align)4291 static inline T VmaAlignUp(T val, T align)
4292 {
4293 return (val + align - 1) / align * align;
4294 }
4295 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
4296 // Use types like uint32_t, uint64_t as T.
4297 template <typename T>
VmaAlignDown(T val,T align)4298 static inline T VmaAlignDown(T val, T align)
4299 {
4300 return val / align * align;
4301 }
4302
4303 // Division with mathematical rounding to nearest number.
4304 template <typename T>
VmaRoundDiv(T x,T y)4305 static inline T VmaRoundDiv(T x, T y)
4306 {
4307 return (x + (y / (T)2)) / y;
4308 }
4309
4310 /*
4311 Returns true if given number is a power of two.
4312 T must be unsigned integer number or signed integer but always nonnegative.
4313 For 0 returns true.
4314 */
4315 template <typename T>
VmaIsPow2(T x)4316 inline bool VmaIsPow2(T x)
4317 {
4318 return (x & (x-1)) == 0;
4319 }
4320
4321 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)4322 static inline uint32_t VmaNextPow2(uint32_t v)
4323 {
4324 v--;
4325 v |= v >> 1;
4326 v |= v >> 2;
4327 v |= v >> 4;
4328 v |= v >> 8;
4329 v |= v >> 16;
4330 v++;
4331 return v;
4332 }
VmaNextPow2(uint64_t v)4333 static inline uint64_t VmaNextPow2(uint64_t v)
4334 {
4335 v--;
4336 v |= v >> 1;
4337 v |= v >> 2;
4338 v |= v >> 4;
4339 v |= v >> 8;
4340 v |= v >> 16;
4341 v |= v >> 32;
4342 v++;
4343 return v;
4344 }
4345
4346 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)4347 static inline uint32_t VmaPrevPow2(uint32_t v)
4348 {
4349 v |= v >> 1;
4350 v |= v >> 2;
4351 v |= v >> 4;
4352 v |= v >> 8;
4353 v |= v >> 16;
4354 v = v ^ (v >> 1);
4355 return v;
4356 }
VmaPrevPow2(uint64_t v)4357 static inline uint64_t VmaPrevPow2(uint64_t v)
4358 {
4359 v |= v >> 1;
4360 v |= v >> 2;
4361 v |= v >> 4;
4362 v |= v >> 8;
4363 v |= v >> 16;
4364 v |= v >> 32;
4365 v = v ^ (v >> 1);
4366 return v;
4367 }
4368
VmaStrIsEmpty(const char * pStr)4369 static inline bool VmaStrIsEmpty(const char* pStr)
4370 {
4371 return pStr == VMA_NULL || *pStr == '\0';
4372 }
4373
4374 #if VMA_STATS_STRING_ENABLED
4375
VmaAlgorithmToStr(uint32_t algorithm)4376 static const char* VmaAlgorithmToStr(uint32_t algorithm)
4377 {
4378 switch(algorithm)
4379 {
4380 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
4381 return "Linear";
4382 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
4383 return "Buddy";
4384 case 0:
4385 return "Default";
4386 default:
4387 VMA_ASSERT(0);
4388 return "";
4389 }
4390 }
4391
4392 #endif // #if VMA_STATS_STRING_ENABLED
4393
4394 #ifndef VMA_SORT
4395
4396 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)4397 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
4398 {
4399 Iterator centerValue = end; --centerValue;
4400 Iterator insertIndex = beg;
4401 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
4402 {
4403 if(cmp(*memTypeIndex, *centerValue))
4404 {
4405 if(insertIndex != memTypeIndex)
4406 {
4407 VMA_SWAP(*memTypeIndex, *insertIndex);
4408 }
4409 ++insertIndex;
4410 }
4411 }
4412 if(insertIndex != centerValue)
4413 {
4414 VMA_SWAP(*insertIndex, *centerValue);
4415 }
4416 return insertIndex;
4417 }
4418
4419 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)4420 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
4421 {
4422 if(beg < end)
4423 {
4424 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
4425 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
4426 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
4427 }
4428 }
4429
4430 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
4431
4432 #endif // #ifndef VMA_SORT
4433
4434 /*
4435 Returns true if two memory blocks occupy overlapping pages.
4436 ResourceA must be in less memory offset than ResourceB.
4437
4438 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
4439 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
4440 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)4441 static inline bool VmaBlocksOnSamePage(
4442 VkDeviceSize resourceAOffset,
4443 VkDeviceSize resourceASize,
4444 VkDeviceSize resourceBOffset,
4445 VkDeviceSize pageSize)
4446 {
4447 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
4448 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
4449 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
4450 VkDeviceSize resourceBStart = resourceBOffset;
4451 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
4452 return resourceAEndPage == resourceBStartPage;
4453 }
4454
4455 enum VmaSuballocationType
4456 {
4457 VMA_SUBALLOCATION_TYPE_FREE = 0,
4458 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
4459 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
4460 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
4461 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
4462 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
4463 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
4464 };
4465
4466 /*
4467 Returns true if given suballocation types could conflict and must respect
4468 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
4469 or linear image and another one is optimal image. If type is unknown, behave
4470 conservatively.
4471 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)4472 static inline bool VmaIsBufferImageGranularityConflict(
4473 VmaSuballocationType suballocType1,
4474 VmaSuballocationType suballocType2)
4475 {
4476 if(suballocType1 > suballocType2)
4477 {
4478 VMA_SWAP(suballocType1, suballocType2);
4479 }
4480
4481 switch(suballocType1)
4482 {
4483 case VMA_SUBALLOCATION_TYPE_FREE:
4484 return false;
4485 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
4486 return true;
4487 case VMA_SUBALLOCATION_TYPE_BUFFER:
4488 return
4489 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4490 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4491 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
4492 return
4493 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
4494 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
4495 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4496 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
4497 return
4498 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
4499 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
4500 return false;
4501 default:
4502 VMA_ASSERT(0);
4503 return true;
4504 }
4505 }
4506
VmaWriteMagicValue(void * pData,VkDeviceSize offset)4507 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
4508 {
4509 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4510 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
4511 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4512 for(size_t i = 0; i < numberCount; ++i, ++pDst)
4513 {
4514 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
4515 }
4516 #else
4517 // no-op
4518 #endif
4519 }
4520
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)4521 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
4522 {
4523 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
4524 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
4525 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
4526 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
4527 {
4528 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
4529 {
4530 return false;
4531 }
4532 }
4533 #endif
4534 return true;
4535 }
4536
4537 /*
4538 Fills structure with parameters of an example buffer to be used for transfers
4539 during GPU memory defragmentation.
4540 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)4541 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
4542 {
4543 memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
4544 outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4545 outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4546 outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
4547 }
4548
4549 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
4550 struct VmaMutexLock
4551 {
VMA_CLASS_NO_COPYVmaMutexLock4552 VMA_CLASS_NO_COPY(VmaMutexLock)
4553 public:
4554 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
4555 m_pMutex(useMutex ? &mutex : VMA_NULL)
4556 { if(m_pMutex) { m_pMutex->Lock(); } }
~VmaMutexLockVmaMutexLock4557 ~VmaMutexLock()
4558 { if(m_pMutex) { m_pMutex->Unlock(); } }
4559 private:
4560 VMA_MUTEX* m_pMutex;
4561 };
4562
4563 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
4564 struct VmaMutexLockRead
4565 {
VMA_CLASS_NO_COPYVmaMutexLockRead4566 VMA_CLASS_NO_COPY(VmaMutexLockRead)
4567 public:
4568 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
4569 m_pMutex(useMutex ? &mutex : VMA_NULL)
4570 { if(m_pMutex) { m_pMutex->LockRead(); } }
~VmaMutexLockReadVmaMutexLockRead4571 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
4572 private:
4573 VMA_RW_MUTEX* m_pMutex;
4574 };
4575
4576 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
4577 struct VmaMutexLockWrite
4578 {
VMA_CLASS_NO_COPYVmaMutexLockWrite4579 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
4580 public:
4581 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
4582 m_pMutex(useMutex ? &mutex : VMA_NULL)
4583 { if(m_pMutex) { m_pMutex->LockWrite(); } }
~VmaMutexLockWriteVmaMutexLockWrite4584 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
4585 private:
4586 VMA_RW_MUTEX* m_pMutex;
4587 };
4588
4589 #if VMA_DEBUG_GLOBAL_MUTEX
4590 static VMA_MUTEX gDebugGlobalMutex;
4591 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
4592 #else
4593 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
4594 #endif
4595
4596 // Minimum size of a free suballocation to register it in the free suballocation collection.
4597 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
4598
4599 /*
4600 Performs binary search and returns iterator to first element that is greater or
4601 equal to (key), according to comparison (cmp).
4602
4603 Cmp should return true if first argument is less than second argument.
4604
4605 Returned value is the found element, if present in the collection or place where
4606 new element with value (key) should be inserted.
4607 */
4608 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)4609 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
4610 {
4611 size_t down = 0, up = (end - beg);
4612 while(down < up)
4613 {
4614 const size_t mid = (down + up) / 2;
4615 if(cmp(*(beg+mid), key))
4616 {
4617 down = mid + 1;
4618 }
4619 else
4620 {
4621 up = mid;
4622 }
4623 }
4624 return beg + down;
4625 }
4626
4627 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)4628 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
4629 {
4630 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4631 beg, end, value, cmp);
4632 if(it == end ||
4633 (!cmp(*it, value) && !cmp(value, *it)))
4634 {
4635 return it;
4636 }
4637 return end;
4638 }
4639
4640 /*
4641 Returns true if all pointers in the array are not-null and unique.
4642 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
4643 T must be pointer type, e.g. VmaAllocation, VmaPool.
4644 */
4645 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)4646 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
4647 {
4648 for(uint32_t i = 0; i < count; ++i)
4649 {
4650 const T iPtr = arr[i];
4651 if(iPtr == VMA_NULL)
4652 {
4653 return false;
4654 }
4655 for(uint32_t j = i + 1; j < count; ++j)
4656 {
4657 if(iPtr == arr[j])
4658 {
4659 return false;
4660 }
4661 }
4662 }
4663 return true;
4664 }
4665
4666 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)4667 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
4668 {
4669 newStruct->pNext = mainStruct->pNext;
4670 mainStruct->pNext = newStruct;
4671 }
4672
4673 ////////////////////////////////////////////////////////////////////////////////
4674 // Memory allocation
4675
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)4676 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
4677 {
4678 if((pAllocationCallbacks != VMA_NULL) &&
4679 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
4680 {
4681 return (*pAllocationCallbacks->pfnAllocation)(
4682 pAllocationCallbacks->pUserData,
4683 size,
4684 alignment,
4685 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
4686 }
4687 else
4688 {
4689 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
4690 }
4691 }
4692
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)4693 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
4694 {
4695 if((pAllocationCallbacks != VMA_NULL) &&
4696 (pAllocationCallbacks->pfnFree != VMA_NULL))
4697 {
4698 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
4699 }
4700 else
4701 {
4702 VMA_SYSTEM_FREE(ptr);
4703 }
4704 }
4705
4706 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)4707 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
4708 {
4709 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
4710 }
4711
4712 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)4713 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
4714 {
4715 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
4716 }
4717
4718 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
4719
4720 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
4721
4722 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)4723 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
4724 {
4725 ptr->~T();
4726 VmaFree(pAllocationCallbacks, ptr);
4727 }
4728
4729 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)4730 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
4731 {
4732 if(ptr != VMA_NULL)
4733 {
4734 for(size_t i = count; i--; )
4735 {
4736 ptr[i].~T();
4737 }
4738 VmaFree(pAllocationCallbacks, ptr);
4739 }
4740 }
4741
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)4742 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
4743 {
4744 if(srcStr != VMA_NULL)
4745 {
4746 const size_t len = strlen(srcStr);
4747 char* const result = vma_new_array(allocs, char, len + 1);
4748 memcpy(result, srcStr, len + 1);
4749 return result;
4750 }
4751 else
4752 {
4753 return VMA_NULL;
4754 }
4755 }
4756
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)4757 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
4758 {
4759 if(str != VMA_NULL)
4760 {
4761 const size_t len = strlen(str);
4762 vma_delete_array(allocs, str, len + 1);
4763 }
4764 }
4765
4766 // STL-compatible allocator.
4767 template<typename T>
4768 class VmaStlAllocator
4769 {
4770 public:
4771 const VkAllocationCallbacks* const m_pCallbacks;
4772 typedef T value_type;
4773
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)4774 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)4775 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
4776
allocate(size_t n)4777 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)4778 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4779
4780 template<typename U>
4781 bool operator==(const VmaStlAllocator<U>& rhs) const
4782 {
4783 return m_pCallbacks == rhs.m_pCallbacks;
4784 }
4785 template<typename U>
4786 bool operator!=(const VmaStlAllocator<U>& rhs) const
4787 {
4788 return m_pCallbacks != rhs.m_pCallbacks;
4789 }
4790
4791 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
4792 };
4793
4794 #if VMA_USE_STL_VECTOR
4795
4796 #define VmaVector std::vector
4797
4798 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)4799 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
4800 {
4801 vec.insert(vec.begin() + index, item);
4802 }
4803
4804 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)4805 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
4806 {
4807 vec.erase(vec.begin() + index);
4808 }
4809
4810 #else // #if VMA_USE_STL_VECTOR
4811
4812 /* Class with interface compatible with subset of std::vector.
4813 T must be POD because constructors and destructors are not called and memcpy is
4814 used for these objects. */
4815 template<typename T, typename AllocatorT>
4816 class VmaVector
4817 {
4818 public:
4819 typedef T value_type;
4820
VmaVector(const AllocatorT & allocator)4821 VmaVector(const AllocatorT& allocator) :
4822 m_Allocator(allocator),
4823 m_pArray(VMA_NULL),
4824 m_Count(0),
4825 m_Capacity(0)
4826 {
4827 }
4828
VmaVector(size_t count,const AllocatorT & allocator)4829 VmaVector(size_t count, const AllocatorT& allocator) :
4830 m_Allocator(allocator),
4831 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4832 m_Count(count),
4833 m_Capacity(count)
4834 {
4835 }
4836
4837 // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4838 // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)4839 VmaVector(size_t count, const T& value, const AllocatorT& allocator)
4840 : VmaVector(count, allocator) {}
4841
VmaVector(const VmaVector<T,AllocatorT> & src)4842 VmaVector(const VmaVector<T, AllocatorT>& src) :
4843 m_Allocator(src.m_Allocator),
4844 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4845 m_Count(src.m_Count),
4846 m_Capacity(src.m_Count)
4847 {
4848 if(m_Count != 0)
4849 {
4850 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4851 }
4852 }
4853
~VmaVector()4854 ~VmaVector()
4855 {
4856 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4857 }
4858
4859 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
4860 {
4861 if(&rhs != this)
4862 {
4863 resize(rhs.m_Count);
4864 if(m_Count != 0)
4865 {
4866 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4867 }
4868 }
4869 return *this;
4870 }
4871
empty()4872 bool empty() const { return m_Count == 0; }
size()4873 size_t size() const { return m_Count; }
data()4874 T* data() { return m_pArray; }
data()4875 const T* data() const { return m_pArray; }
4876
4877 T& operator[](size_t index)
4878 {
4879 VMA_HEAVY_ASSERT(index < m_Count);
4880 return m_pArray[index];
4881 }
4882 const T& operator[](size_t index) const
4883 {
4884 VMA_HEAVY_ASSERT(index < m_Count);
4885 return m_pArray[index];
4886 }
4887
front()4888 T& front()
4889 {
4890 VMA_HEAVY_ASSERT(m_Count > 0);
4891 return m_pArray[0];
4892 }
front()4893 const T& front() const
4894 {
4895 VMA_HEAVY_ASSERT(m_Count > 0);
4896 return m_pArray[0];
4897 }
back()4898 T& back()
4899 {
4900 VMA_HEAVY_ASSERT(m_Count > 0);
4901 return m_pArray[m_Count - 1];
4902 }
back()4903 const T& back() const
4904 {
4905 VMA_HEAVY_ASSERT(m_Count > 0);
4906 return m_pArray[m_Count - 1];
4907 }
4908
4909 void reserve(size_t newCapacity, bool freeMemory = false)
4910 {
4911 newCapacity = VMA_MAX(newCapacity, m_Count);
4912
4913 if((newCapacity < m_Capacity) && !freeMemory)
4914 {
4915 newCapacity = m_Capacity;
4916 }
4917
4918 if(newCapacity != m_Capacity)
4919 {
4920 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4921 if(m_Count != 0)
4922 {
4923 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4924 }
4925 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4926 m_Capacity = newCapacity;
4927 m_pArray = newArray;
4928 }
4929 }
4930
4931 void resize(size_t newCount, bool freeMemory = false)
4932 {
4933 size_t newCapacity = m_Capacity;
4934 if(newCount > m_Capacity)
4935 {
4936 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4937 }
4938 else if(freeMemory)
4939 {
4940 newCapacity = newCount;
4941 }
4942
4943 if(newCapacity != m_Capacity)
4944 {
4945 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4946 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4947 if(elementsToCopy != 0)
4948 {
4949 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4950 }
4951 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4952 m_Capacity = newCapacity;
4953 m_pArray = newArray;
4954 }
4955
4956 m_Count = newCount;
4957 }
4958
4959 void clear(bool freeMemory = false)
4960 {
4961 resize(0, freeMemory);
4962 }
4963
insert(size_t index,const T & src)4964 void insert(size_t index, const T& src)
4965 {
4966 VMA_HEAVY_ASSERT(index <= m_Count);
4967 const size_t oldCount = size();
4968 resize(oldCount + 1);
4969 if(index < oldCount)
4970 {
4971 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4972 }
4973 m_pArray[index] = src;
4974 }
4975
remove(size_t index)4976 void remove(size_t index)
4977 {
4978 VMA_HEAVY_ASSERT(index < m_Count);
4979 const size_t oldCount = size();
4980 if(index < oldCount - 1)
4981 {
4982 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4983 }
4984 resize(oldCount - 1);
4985 }
4986
push_back(const T & src)4987 void push_back(const T& src)
4988 {
4989 const size_t newIndex = size();
4990 resize(newIndex + 1);
4991 m_pArray[newIndex] = src;
4992 }
4993
pop_back()4994 void pop_back()
4995 {
4996 VMA_HEAVY_ASSERT(m_Count > 0);
4997 resize(size() - 1);
4998 }
4999
push_front(const T & src)5000 void push_front(const T& src)
5001 {
5002 insert(0, src);
5003 }
5004
pop_front()5005 void pop_front()
5006 {
5007 VMA_HEAVY_ASSERT(m_Count > 0);
5008 remove(0);
5009 }
5010
5011 typedef T* iterator;
5012
begin()5013 iterator begin() { return m_pArray; }
end()5014 iterator end() { return m_pArray + m_Count; }
5015
5016 private:
5017 AllocatorT m_Allocator;
5018 T* m_pArray;
5019 size_t m_Count;
5020 size_t m_Capacity;
5021 };
5022
5023 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)5024 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
5025 {
5026 vec.insert(index, item);
5027 }
5028
5029 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)5030 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
5031 {
5032 vec.remove(index);
5033 }
5034
5035 #endif // #if VMA_USE_STL_VECTOR
5036
5037 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)5038 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
5039 {
5040 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5041 vector.data(),
5042 vector.data() + vector.size(),
5043 value,
5044 CmpLess()) - vector.data();
5045 VmaVectorInsert(vector, indexToInsert, value);
5046 return indexToInsert;
5047 }
5048
5049 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)5050 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
5051 {
5052 CmpLess comparator;
5053 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
5054 vector.begin(),
5055 vector.end(),
5056 value,
5057 comparator);
5058 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
5059 {
5060 size_t indexToRemove = it - vector.begin();
5061 VmaVectorRemove(vector, indexToRemove);
5062 return true;
5063 }
5064 return false;
5065 }
5066
5067 ////////////////////////////////////////////////////////////////////////////////
5068 // class VmaSmallVector
5069
5070 /*
5071 This is a vector (a variable-sized array), optimized for the case when the array is small.
5072
5073 It contains some number of elements in-place, which allows it to avoid heap allocation
5074 when the actual number of elements is below that threshold. This allows normal "small"
5075 cases to be fast without losing generality for large inputs.
5076 */
5077
5078 template<typename T, typename AllocatorT, size_t N>
5079 class VmaSmallVector
5080 {
5081 public:
5082 typedef T value_type;
5083
VmaSmallVector(const AllocatorT & allocator)5084 VmaSmallVector(const AllocatorT& allocator) :
5085 m_Count(0),
5086 m_DynamicArray(allocator)
5087 {
5088 }
VmaSmallVector(size_t count,const AllocatorT & allocator)5089 VmaSmallVector(size_t count, const AllocatorT& allocator) :
5090 m_Count(count),
5091 m_DynamicArray(count > N ? count : 0, allocator)
5092 {
5093 }
5094 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5095 VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& src) = delete;
5096 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
5097 VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>& rhs) = delete;
5098
empty()5099 bool empty() const { return m_Count == 0; }
size()5100 size_t size() const { return m_Count; }
data()5101 T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
data()5102 const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
5103
5104 T& operator[](size_t index)
5105 {
5106 VMA_HEAVY_ASSERT(index < m_Count);
5107 return data()[index];
5108 }
5109 const T& operator[](size_t index) const
5110 {
5111 VMA_HEAVY_ASSERT(index < m_Count);
5112 return data()[index];
5113 }
5114
front()5115 T& front()
5116 {
5117 VMA_HEAVY_ASSERT(m_Count > 0);
5118 return data()[0];
5119 }
front()5120 const T& front() const
5121 {
5122 VMA_HEAVY_ASSERT(m_Count > 0);
5123 return data()[0];
5124 }
back()5125 T& back()
5126 {
5127 VMA_HEAVY_ASSERT(m_Count > 0);
5128 return data()[m_Count - 1];
5129 }
back()5130 const T& back() const
5131 {
5132 VMA_HEAVY_ASSERT(m_Count > 0);
5133 return data()[m_Count - 1];
5134 }
5135
5136 void resize(size_t newCount, bool freeMemory = false)
5137 {
5138 if(newCount > N && m_Count > N)
5139 {
5140 // Any direction, staying in m_DynamicArray
5141 m_DynamicArray.resize(newCount, freeMemory);
5142 }
5143 else if(newCount > N && m_Count <= N)
5144 {
5145 // Growing, moving from m_StaticArray to m_DynamicArray
5146 m_DynamicArray.resize(newCount, freeMemory);
5147 if(m_Count > 0)
5148 {
5149 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
5150 }
5151 }
5152 else if(newCount <= N && m_Count > N)
5153 {
5154 // Shrinking, moving from m_DynamicArray to m_StaticArray
5155 if(newCount > 0)
5156 {
5157 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
5158 }
5159 m_DynamicArray.resize(0, freeMemory);
5160 }
5161 else
5162 {
5163 // Any direction, staying in m_StaticArray - nothing to do here
5164 }
5165 m_Count = newCount;
5166 }
5167
5168 void clear(bool freeMemory = false)
5169 {
5170 m_DynamicArray.clear(freeMemory);
5171 m_Count = 0;
5172 }
5173
insert(size_t index,const T & src)5174 void insert(size_t index, const T& src)
5175 {
5176 VMA_HEAVY_ASSERT(index <= m_Count);
5177 const size_t oldCount = size();
5178 resize(oldCount + 1);
5179 T* const dataPtr = data();
5180 if(index < oldCount)
5181 {
5182 // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
5183 memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
5184 }
5185 dataPtr[index] = src;
5186 }
5187
remove(size_t index)5188 void remove(size_t index)
5189 {
5190 VMA_HEAVY_ASSERT(index < m_Count);
5191 const size_t oldCount = size();
5192 if(index < oldCount - 1)
5193 {
5194 // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
5195 T* const dataPtr = data();
5196 memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
5197 }
5198 resize(oldCount - 1);
5199 }
5200
push_back(const T & src)5201 void push_back(const T& src)
5202 {
5203 const size_t newIndex = size();
5204 resize(newIndex + 1);
5205 data()[newIndex] = src;
5206 }
5207
pop_back()5208 void pop_back()
5209 {
5210 VMA_HEAVY_ASSERT(m_Count > 0);
5211 resize(size() - 1);
5212 }
5213
push_front(const T & src)5214 void push_front(const T& src)
5215 {
5216 insert(0, src);
5217 }
5218
pop_front()5219 void pop_front()
5220 {
5221 VMA_HEAVY_ASSERT(m_Count > 0);
5222 remove(0);
5223 }
5224
5225 typedef T* iterator;
5226
begin()5227 iterator begin() { return data(); }
end()5228 iterator end() { return data() + m_Count; }
5229
5230 private:
5231 size_t m_Count;
5232 T m_StaticArray[N]; // Used when m_Size <= N
5233 VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
5234 };
5235
5236 ////////////////////////////////////////////////////////////////////////////////
5237 // class VmaPoolAllocator
5238
5239 /*
5240 Allocator for objects of type T using a list of arrays (pools) to speed up
5241 allocation. Number of elements that can be allocated is not bounded because
5242 allocator can create multiple blocks.
5243 */
5244 template<typename T>
5245 class VmaPoolAllocator
5246 {
5247 VMA_CLASS_NO_COPY(VmaPoolAllocator)
5248 public:
5249 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
5250 ~VmaPoolAllocator();
5251 template<typename... Types> T* Alloc(Types... args);
5252 void Free(T* ptr);
5253
5254 private:
5255 union Item
5256 {
5257 uint32_t NextFreeIndex;
5258 alignas(T) char Value[sizeof(T)];
5259 };
5260
5261 struct ItemBlock
5262 {
5263 Item* pItems;
5264 uint32_t Capacity;
5265 uint32_t FirstFreeIndex;
5266 };
5267
5268 const VkAllocationCallbacks* m_pAllocationCallbacks;
5269 const uint32_t m_FirstBlockCapacity;
5270 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
5271
5272 ItemBlock& CreateNewBlock();
5273 };
5274
5275 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)5276 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) :
5277 m_pAllocationCallbacks(pAllocationCallbacks),
5278 m_FirstBlockCapacity(firstBlockCapacity),
5279 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
5280 {
5281 VMA_ASSERT(m_FirstBlockCapacity > 1);
5282 }
5283
5284 template<typename T>
~VmaPoolAllocator()5285 VmaPoolAllocator<T>::~VmaPoolAllocator()
5286 {
5287 for(size_t i = m_ItemBlocks.size(); i--; )
5288 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
5289 m_ItemBlocks.clear();
5290 }
5291
5292 template<typename T>
Alloc(Types...args)5293 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types... args)
5294 {
5295 for(size_t i = m_ItemBlocks.size(); i--; )
5296 {
5297 ItemBlock& block = m_ItemBlocks[i];
5298 // This block has some free items: Use first one.
5299 if(block.FirstFreeIndex != UINT32_MAX)
5300 {
5301 Item* const pItem = &block.pItems[block.FirstFreeIndex];
5302 block.FirstFreeIndex = pItem->NextFreeIndex;
5303 T* result = (T*)&pItem->Value;
5304 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5305 return result;
5306 }
5307 }
5308
5309 // No block has free item: Create new one and use it.
5310 ItemBlock& newBlock = CreateNewBlock();
5311 Item* const pItem = &newBlock.pItems[0];
5312 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
5313 T* result = (T*)&pItem->Value;
5314 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
5315 return result;
5316 }
5317
5318 template<typename T>
Free(T * ptr)5319 void VmaPoolAllocator<T>::Free(T* ptr)
5320 {
5321 // Search all memory blocks to find ptr.
5322 for(size_t i = m_ItemBlocks.size(); i--; )
5323 {
5324 ItemBlock& block = m_ItemBlocks[i];
5325
5326 // Casting to union.
5327 Item* pItemPtr;
5328 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
5329
5330 // Check if pItemPtr is in address range of this block.
5331 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
5332 {
5333 ptr->~T(); // Explicit destructor call.
5334 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
5335 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
5336 block.FirstFreeIndex = index;
5337 return;
5338 }
5339 }
5340 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
5341 }
5342
5343 template<typename T>
CreateNewBlock()5344 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
5345 {
5346 const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
5347 m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
5348
5349 const ItemBlock newBlock = {
5350 vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
5351 newBlockCapacity,
5352 0 };
5353
5354 m_ItemBlocks.push_back(newBlock);
5355
5356 // Setup singly-linked list of all free items in this block.
5357 for(uint32_t i = 0; i < newBlockCapacity - 1; ++i)
5358 newBlock.pItems[i].NextFreeIndex = i + 1;
5359 newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
5360 return m_ItemBlocks.back();
5361 }
5362
5363 ////////////////////////////////////////////////////////////////////////////////
5364 // class VmaRawList, VmaList
5365
5366 #if VMA_USE_STL_LIST
5367
5368 #define VmaList std::list
5369
5370 #else // #if VMA_USE_STL_LIST
5371
5372 template<typename T>
5373 struct VmaListItem
5374 {
5375 VmaListItem* pPrev;
5376 VmaListItem* pNext;
5377 T Value;
5378 };
5379
5380 // Doubly linked list.
5381 template<typename T>
5382 class VmaRawList
5383 {
5384 VMA_CLASS_NO_COPY(VmaRawList)
5385 public:
5386 typedef VmaListItem<T> ItemType;
5387
5388 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
5389 ~VmaRawList();
5390 void Clear();
5391
GetCount()5392 size_t GetCount() const { return m_Count; }
IsEmpty()5393 bool IsEmpty() const { return m_Count == 0; }
5394
Front()5395 ItemType* Front() { return m_pFront; }
Front()5396 const ItemType* Front() const { return m_pFront; }
Back()5397 ItemType* Back() { return m_pBack; }
Back()5398 const ItemType* Back() const { return m_pBack; }
5399
5400 ItemType* PushBack();
5401 ItemType* PushFront();
5402 ItemType* PushBack(const T& value);
5403 ItemType* PushFront(const T& value);
5404 void PopBack();
5405 void PopFront();
5406
5407 // Item can be null - it means PushBack.
5408 ItemType* InsertBefore(ItemType* pItem);
5409 // Item can be null - it means PushFront.
5410 ItemType* InsertAfter(ItemType* pItem);
5411
5412 ItemType* InsertBefore(ItemType* pItem, const T& value);
5413 ItemType* InsertAfter(ItemType* pItem, const T& value);
5414
5415 void Remove(ItemType* pItem);
5416
5417 private:
5418 const VkAllocationCallbacks* const m_pAllocationCallbacks;
5419 VmaPoolAllocator<ItemType> m_ItemAllocator;
5420 ItemType* m_pFront;
5421 ItemType* m_pBack;
5422 size_t m_Count;
5423 };
5424
5425 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)5426 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
5427 m_pAllocationCallbacks(pAllocationCallbacks),
5428 m_ItemAllocator(pAllocationCallbacks, 128),
5429 m_pFront(VMA_NULL),
5430 m_pBack(VMA_NULL),
5431 m_Count(0)
5432 {
5433 }
5434
5435 template<typename T>
~VmaRawList()5436 VmaRawList<T>::~VmaRawList()
5437 {
5438 // Intentionally not calling Clear, because that would be unnecessary
5439 // computations to return all items to m_ItemAllocator as free.
5440 }
5441
5442 template<typename T>
Clear()5443 void VmaRawList<T>::Clear()
5444 {
5445 if(IsEmpty() == false)
5446 {
5447 ItemType* pItem = m_pBack;
5448 while(pItem != VMA_NULL)
5449 {
5450 ItemType* const pPrevItem = pItem->pPrev;
5451 m_ItemAllocator.Free(pItem);
5452 pItem = pPrevItem;
5453 }
5454 m_pFront = VMA_NULL;
5455 m_pBack = VMA_NULL;
5456 m_Count = 0;
5457 }
5458 }
5459
5460 template<typename T>
PushBack()5461 VmaListItem<T>* VmaRawList<T>::PushBack()
5462 {
5463 ItemType* const pNewItem = m_ItemAllocator.Alloc();
5464 pNewItem->pNext = VMA_NULL;
5465 if(IsEmpty())
5466 {
5467 pNewItem->pPrev = VMA_NULL;
5468 m_pFront = pNewItem;
5469 m_pBack = pNewItem;
5470 m_Count = 1;
5471 }
5472 else
5473 {
5474 pNewItem->pPrev = m_pBack;
5475 m_pBack->pNext = pNewItem;
5476 m_pBack = pNewItem;
5477 ++m_Count;
5478 }
5479 return pNewItem;
5480 }
5481
5482 template<typename T>
PushFront()5483 VmaListItem<T>* VmaRawList<T>::PushFront()
5484 {
5485 ItemType* const pNewItem = m_ItemAllocator.Alloc();
5486 pNewItem->pPrev = VMA_NULL;
5487 if(IsEmpty())
5488 {
5489 pNewItem->pNext = VMA_NULL;
5490 m_pFront = pNewItem;
5491 m_pBack = pNewItem;
5492 m_Count = 1;
5493 }
5494 else
5495 {
5496 pNewItem->pNext = m_pFront;
5497 m_pFront->pPrev = pNewItem;
5498 m_pFront = pNewItem;
5499 ++m_Count;
5500 }
5501 return pNewItem;
5502 }
5503
5504 template<typename T>
PushBack(const T & value)5505 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
5506 {
5507 ItemType* const pNewItem = PushBack();
5508 pNewItem->Value = value;
5509 return pNewItem;
5510 }
5511
5512 template<typename T>
PushFront(const T & value)5513 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
5514 {
5515 ItemType* const pNewItem = PushFront();
5516 pNewItem->Value = value;
5517 return pNewItem;
5518 }
5519
5520 template<typename T>
PopBack()5521 void VmaRawList<T>::PopBack()
5522 {
5523 VMA_HEAVY_ASSERT(m_Count > 0);
5524 ItemType* const pBackItem = m_pBack;
5525 ItemType* const pPrevItem = pBackItem->pPrev;
5526 if(pPrevItem != VMA_NULL)
5527 {
5528 pPrevItem->pNext = VMA_NULL;
5529 }
5530 m_pBack = pPrevItem;
5531 m_ItemAllocator.Free(pBackItem);
5532 --m_Count;
5533 }
5534
5535 template<typename T>
PopFront()5536 void VmaRawList<T>::PopFront()
5537 {
5538 VMA_HEAVY_ASSERT(m_Count > 0);
5539 ItemType* const pFrontItem = m_pFront;
5540 ItemType* const pNextItem = pFrontItem->pNext;
5541 if(pNextItem != VMA_NULL)
5542 {
5543 pNextItem->pPrev = VMA_NULL;
5544 }
5545 m_pFront = pNextItem;
5546 m_ItemAllocator.Free(pFrontItem);
5547 --m_Count;
5548 }
5549
5550 template<typename T>
Remove(ItemType * pItem)5551 void VmaRawList<T>::Remove(ItemType* pItem)
5552 {
5553 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
5554 VMA_HEAVY_ASSERT(m_Count > 0);
5555
5556 if(pItem->pPrev != VMA_NULL)
5557 {
5558 pItem->pPrev->pNext = pItem->pNext;
5559 }
5560 else
5561 {
5562 VMA_HEAVY_ASSERT(m_pFront == pItem);
5563 m_pFront = pItem->pNext;
5564 }
5565
5566 if(pItem->pNext != VMA_NULL)
5567 {
5568 pItem->pNext->pPrev = pItem->pPrev;
5569 }
5570 else
5571 {
5572 VMA_HEAVY_ASSERT(m_pBack == pItem);
5573 m_pBack = pItem->pPrev;
5574 }
5575
5576 m_ItemAllocator.Free(pItem);
5577 --m_Count;
5578 }
5579
5580 template<typename T>
InsertBefore(ItemType * pItem)5581 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
5582 {
5583 if(pItem != VMA_NULL)
5584 {
5585 ItemType* const prevItem = pItem->pPrev;
5586 ItemType* const newItem = m_ItemAllocator.Alloc();
5587 newItem->pPrev = prevItem;
5588 newItem->pNext = pItem;
5589 pItem->pPrev = newItem;
5590 if(prevItem != VMA_NULL)
5591 {
5592 prevItem->pNext = newItem;
5593 }
5594 else
5595 {
5596 VMA_HEAVY_ASSERT(m_pFront == pItem);
5597 m_pFront = newItem;
5598 }
5599 ++m_Count;
5600 return newItem;
5601 }
5602 else
5603 return PushBack();
5604 }
5605
5606 template<typename T>
InsertAfter(ItemType * pItem)5607 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
5608 {
5609 if(pItem != VMA_NULL)
5610 {
5611 ItemType* const nextItem = pItem->pNext;
5612 ItemType* const newItem = m_ItemAllocator.Alloc();
5613 newItem->pNext = nextItem;
5614 newItem->pPrev = pItem;
5615 pItem->pNext = newItem;
5616 if(nextItem != VMA_NULL)
5617 {
5618 nextItem->pPrev = newItem;
5619 }
5620 else
5621 {
5622 VMA_HEAVY_ASSERT(m_pBack == pItem);
5623 m_pBack = newItem;
5624 }
5625 ++m_Count;
5626 return newItem;
5627 }
5628 else
5629 return PushFront();
5630 }
5631
5632 template<typename T>
InsertBefore(ItemType * pItem,const T & value)5633 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
5634 {
5635 ItemType* const newItem = InsertBefore(pItem);
5636 newItem->Value = value;
5637 return newItem;
5638 }
5639
5640 template<typename T>
InsertAfter(ItemType * pItem,const T & value)5641 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
5642 {
5643 ItemType* const newItem = InsertAfter(pItem);
5644 newItem->Value = value;
5645 return newItem;
5646 }
5647
5648 template<typename T, typename AllocatorT>
5649 class VmaList
5650 {
VMA_CLASS_NO_COPY(VmaList)5651 VMA_CLASS_NO_COPY(VmaList)
5652 public:
5653 class iterator
5654 {
5655 public:
5656 iterator() :
5657 m_pList(VMA_NULL),
5658 m_pItem(VMA_NULL)
5659 {
5660 }
5661
5662 T& operator*() const
5663 {
5664 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5665 return m_pItem->Value;
5666 }
5667 T* operator->() const
5668 {
5669 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5670 return &m_pItem->Value;
5671 }
5672
5673 iterator& operator++()
5674 {
5675 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5676 m_pItem = m_pItem->pNext;
5677 return *this;
5678 }
5679 iterator& operator--()
5680 {
5681 if(m_pItem != VMA_NULL)
5682 {
5683 m_pItem = m_pItem->pPrev;
5684 }
5685 else
5686 {
5687 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5688 m_pItem = m_pList->Back();
5689 }
5690 return *this;
5691 }
5692
5693 iterator operator++(int)
5694 {
5695 iterator result = *this;
5696 ++*this;
5697 return result;
5698 }
5699 iterator operator--(int)
5700 {
5701 iterator result = *this;
5702 --*this;
5703 return result;
5704 }
5705
5706 bool operator==(const iterator& rhs) const
5707 {
5708 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5709 return m_pItem == rhs.m_pItem;
5710 }
5711 bool operator!=(const iterator& rhs) const
5712 {
5713 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5714 return m_pItem != rhs.m_pItem;
5715 }
5716
5717 private:
5718 VmaRawList<T>* m_pList;
5719 VmaListItem<T>* m_pItem;
5720
5721 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
5722 m_pList(pList),
5723 m_pItem(pItem)
5724 {
5725 }
5726
5727 friend class VmaList<T, AllocatorT>;
5728 };
5729
5730 class const_iterator
5731 {
5732 public:
const_iterator()5733 const_iterator() :
5734 m_pList(VMA_NULL),
5735 m_pItem(VMA_NULL)
5736 {
5737 }
5738
const_iterator(const iterator & src)5739 const_iterator(const iterator& src) :
5740 m_pList(src.m_pList),
5741 m_pItem(src.m_pItem)
5742 {
5743 }
5744
5745 const T& operator*() const
5746 {
5747 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5748 return m_pItem->Value;
5749 }
5750 const T* operator->() const
5751 {
5752 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5753 return &m_pItem->Value;
5754 }
5755
5756 const_iterator& operator++()
5757 {
5758 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
5759 m_pItem = m_pItem->pNext;
5760 return *this;
5761 }
5762 const_iterator& operator--()
5763 {
5764 if(m_pItem != VMA_NULL)
5765 {
5766 m_pItem = m_pItem->pPrev;
5767 }
5768 else
5769 {
5770 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
5771 m_pItem = m_pList->Back();
5772 }
5773 return *this;
5774 }
5775
5776 const_iterator operator++(int)
5777 {
5778 const_iterator result = *this;
5779 ++*this;
5780 return result;
5781 }
5782 const_iterator operator--(int)
5783 {
5784 const_iterator result = *this;
5785 --*this;
5786 return result;
5787 }
5788
5789 bool operator==(const const_iterator& rhs) const
5790 {
5791 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5792 return m_pItem == rhs.m_pItem;
5793 }
5794 bool operator!=(const const_iterator& rhs) const
5795 {
5796 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
5797 return m_pItem != rhs.m_pItem;
5798 }
5799
5800 private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)5801 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
5802 m_pList(pList),
5803 m_pItem(pItem)
5804 {
5805 }
5806
5807 const VmaRawList<T>* m_pList;
5808 const VmaListItem<T>* m_pItem;
5809
5810 friend class VmaList<T, AllocatorT>;
5811 };
5812
VmaList(const AllocatorT & allocator)5813 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
5814
empty()5815 bool empty() const { return m_RawList.IsEmpty(); }
size()5816 size_t size() const { return m_RawList.GetCount(); }
5817
begin()5818 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()5819 iterator end() { return iterator(&m_RawList, VMA_NULL); }
5820
cbegin()5821 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()5822 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
5823
clear()5824 void clear() { m_RawList.Clear(); }
push_back(const T & value)5825 void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)5826 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)5827 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
5828
5829 private:
5830 VmaRawList<T> m_RawList;
5831 };
5832
5833 #endif // #if VMA_USE_STL_LIST
5834
5835 ////////////////////////////////////////////////////////////////////////////////
5836 // class VmaMap
5837
5838 // Unused in this version.
5839 #if 0
5840
5841 #if VMA_USE_STL_UNORDERED_MAP
5842
5843 #define VmaPair std::pair
5844
5845 #define VMA_MAP_TYPE(KeyT, ValueT) \
5846 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
5847
5848 #else // #if VMA_USE_STL_UNORDERED_MAP
5849
5850 template<typename T1, typename T2>
5851 struct VmaPair
5852 {
5853 T1 first;
5854 T2 second;
5855
5856 VmaPair() : first(), second() { }
5857 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
5858 };
5859
5860 /* Class compatible with subset of interface of std::unordered_map.
5861 KeyT, ValueT must be POD because they will be stored in VmaVector.
5862 */
5863 template<typename KeyT, typename ValueT>
5864 class VmaMap
5865 {
5866 public:
5867 typedef VmaPair<KeyT, ValueT> PairType;
5868 typedef PairType* iterator;
5869
5870 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
5871
5872 iterator begin() { return m_Vector.begin(); }
5873 iterator end() { return m_Vector.end(); }
5874
5875 void insert(const PairType& pair);
5876 iterator find(const KeyT& key);
5877 void erase(iterator it);
5878
5879 private:
5880 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
5881 };
5882
5883 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
5884
5885 template<typename FirstT, typename SecondT>
5886 struct VmaPairFirstLess
5887 {
5888 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5889 {
5890 return lhs.first < rhs.first;
5891 }
5892 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5893 {
5894 return lhs.first < rhsFirst;
5895 }
5896 };
5897
5898 template<typename KeyT, typename ValueT>
5899 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5900 {
5901 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5902 m_Vector.data(),
5903 m_Vector.data() + m_Vector.size(),
5904 pair,
5905 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5906 VmaVectorInsert(m_Vector, indexToInsert, pair);
5907 }
5908
5909 template<typename KeyT, typename ValueT>
5910 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5911 {
5912 PairType* it = VmaBinaryFindFirstNotLess(
5913 m_Vector.data(),
5914 m_Vector.data() + m_Vector.size(),
5915 key,
5916 VmaPairFirstLess<KeyT, ValueT>());
5917 if((it != m_Vector.end()) && (it->first == key))
5918 {
5919 return it;
5920 }
5921 else
5922 {
5923 return m_Vector.end();
5924 }
5925 }
5926
5927 template<typename KeyT, typename ValueT>
5928 void VmaMap<KeyT, ValueT>::erase(iterator it)
5929 {
5930 VmaVectorRemove(m_Vector, it - m_Vector.begin());
5931 }
5932
5933 #endif // #if VMA_USE_STL_UNORDERED_MAP
5934
5935 #endif // #if 0
5936
5937 ////////////////////////////////////////////////////////////////////////////////
5938
5939 class VmaDeviceMemoryBlock;
5940
5941 enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
5942
5943 struct VmaAllocation_T
5944 {
5945 private:
5946 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
5947
5948 enum FLAGS
5949 {
5950 FLAG_USER_DATA_STRING = 0x01,
5951 };
5952
5953 public:
5954 enum ALLOCATION_TYPE
5955 {
5956 ALLOCATION_TYPE_NONE,
5957 ALLOCATION_TYPE_BLOCK,
5958 ALLOCATION_TYPE_DEDICATED,
5959 };
5960
5961 /*
5962 This struct is allocated using VmaPoolAllocator.
5963 */
5964
VmaAllocation_TVmaAllocation_T5965 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
5966 m_Alignment{1},
5967 m_Size{0},
5968 m_pUserData{VMA_NULL},
5969 m_LastUseFrameIndex{currentFrameIndex},
5970 m_MemoryTypeIndex{0},
5971 m_Type{(uint8_t)ALLOCATION_TYPE_NONE},
5972 m_SuballocationType{(uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN},
5973 m_MapCount{0},
5974 m_Flags{userDataString ? (uint8_t)FLAG_USER_DATA_STRING : (uint8_t)0},
5975 m_NewBlockFlag{false}
5976 {
5977 #if VMA_STATS_STRING_ENABLED
5978 m_CreationFrameIndex = currentFrameIndex;
5979 m_BufferImageUsage = 0;
5980 #endif
5981 }
5982
~VmaAllocation_TVmaAllocation_T5983 ~VmaAllocation_T()
5984 {
5985 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
5986
5987 // Check if owned string was freed.
5988 VMA_ASSERT(m_pUserData == VMA_NULL);
5989 }
5990
5991 void InitBlockAllocation(
5992 VmaDeviceMemoryBlock* block,
5993 VkDeviceSize offset,
5994 VkDeviceSize alignment,
5995 VkDeviceSize size,
5996 uint32_t memoryTypeIndex,
5997 VmaSuballocationType suballocationType,
5998 bool mapped,
5999 bool canBecomeLost,
6000 bool newBlockFlag = false)
6001 {
6002 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6003 VMA_ASSERT(block != VMA_NULL);
6004 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6005 m_Alignment = alignment;
6006 m_Size = size;
6007 m_MemoryTypeIndex = memoryTypeIndex;
6008 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6009 m_SuballocationType = (uint8_t)suballocationType;
6010 m_BlockAllocation.m_Block = block;
6011 m_BlockAllocation.m_Offset = offset;
6012 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
6013 m_NewBlockFlag = newBlockFlag;
6014 }
6015
InitLostVmaAllocation_T6016 void InitLost()
6017 {
6018 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6019 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
6020 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
6021 m_MemoryTypeIndex = 0;
6022 m_BlockAllocation.m_Block = VMA_NULL;
6023 m_BlockAllocation.m_Offset = 0;
6024 m_BlockAllocation.m_CanBecomeLost = true;
6025 }
6026
6027 void ChangeBlockAllocation(
6028 VmaAllocator hAllocator,
6029 VmaDeviceMemoryBlock* block,
6030 VkDeviceSize offset);
6031
6032 void ChangeOffset(VkDeviceSize newOffset);
6033
6034 // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T6035 void InitDedicatedAllocation(
6036 uint32_t memoryTypeIndex,
6037 VkDeviceMemory hMemory,
6038 VmaSuballocationType suballocationType,
6039 void* pMappedData,
6040 VkDeviceSize size)
6041 {
6042 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
6043 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
6044 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
6045 m_Alignment = 0;
6046 m_Size = size;
6047 m_MemoryTypeIndex = memoryTypeIndex;
6048 m_SuballocationType = (uint8_t)suballocationType;
6049 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
6050 m_DedicatedAllocation.m_hMemory = hMemory;
6051 m_DedicatedAllocation.m_pMappedData = pMappedData;
6052 }
6053
GetTypeVmaAllocation_T6054 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T6055 VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T6056 VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T6057 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T6058 void* GetUserData() const { return m_pUserData; }
6059 void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T6060 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
IsNewBlockFlagVmaAllocation_T6061 bool IsNewBlockFlag() const { return m_NewBlockFlag; }
ClearNewBlockFlagVmaAllocation_T6062 void ClearNewBlockFlag() { m_NewBlockFlag = false; }
6063
GetBlockVmaAllocation_T6064 VmaDeviceMemoryBlock* GetBlock() const
6065 {
6066 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
6067 return m_BlockAllocation.m_Block;
6068 }
6069 VkDeviceSize GetOffset() const;
6070 VkDeviceMemory GetMemory() const;
GetMemoryTypeIndexVmaAllocation_T6071 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T6072 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
6073 void* GetMappedData() const;
6074 bool CanBecomeLost() const;
6075
GetLastUseFrameIndexVmaAllocation_T6076 uint32_t GetLastUseFrameIndex() const
6077 {
6078 return m_LastUseFrameIndex.load();
6079 }
CompareExchangeLastUseFrameIndexVmaAllocation_T6080 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
6081 {
6082 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
6083 }
6084 /*
6085 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
6086 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
6087 - Else, returns false.
6088
6089 If hAllocation is already lost, assert - you should not call it then.
6090 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
6091 */
6092 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6093
DedicatedAllocCalcStatsInfoVmaAllocation_T6094 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
6095 {
6096 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
6097 outInfo.blockCount = 1;
6098 outInfo.allocationCount = 1;
6099 outInfo.unusedRangeCount = 0;
6100 outInfo.usedBytes = m_Size;
6101 outInfo.unusedBytes = 0;
6102 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
6103 outInfo.unusedRangeSizeMin = UINT64_MAX;
6104 outInfo.unusedRangeSizeMax = 0;
6105 }
6106
6107 void BlockAllocMap();
6108 void BlockAllocUnmap();
6109 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6110 void DedicatedAllocUnmap(VmaAllocator hAllocator);
6111
6112 #if VMA_STATS_STRING_ENABLED
GetCreationFrameIndexVmaAllocation_T6113 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
GetBufferImageUsageVmaAllocation_T6114 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6115
InitBufferImageUsageVmaAllocation_T6116 void InitBufferImageUsage(uint32_t bufferImageUsage)
6117 {
6118 VMA_ASSERT(m_BufferImageUsage == 0);
6119 m_BufferImageUsage = bufferImageUsage;
6120 }
6121
6122 void PrintParameters(class VmaJsonWriter& json) const;
6123 #endif
6124
6125 private:
6126 VkDeviceSize m_Alignment;
6127 VkDeviceSize m_Size;
6128 void* m_pUserData;
6129 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
6130 uint32_t m_MemoryTypeIndex;
6131 uint8_t m_Type; // ALLOCATION_TYPE
6132 uint8_t m_SuballocationType; // VmaSuballocationType
6133 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
6134 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
6135 uint8_t m_MapCount;
6136 uint8_t m_Flags; // enum FLAGS
6137 bool m_NewBlockFlag;
6138
6139 // Allocation out of VmaDeviceMemoryBlock.
6140 struct BlockAllocation
6141 {
6142 VmaDeviceMemoryBlock* m_Block;
6143 VkDeviceSize m_Offset;
6144 bool m_CanBecomeLost;
6145 };
6146
6147 // Allocation for an object that has its own private VkDeviceMemory.
6148 struct DedicatedAllocation
6149 {
6150 VkDeviceMemory m_hMemory;
6151 void* m_pMappedData; // Not null means memory is mapped.
6152 };
6153
6154 union
6155 {
6156 // Allocation out of VmaDeviceMemoryBlock.
6157 BlockAllocation m_BlockAllocation;
6158 // Allocation for an object that has its own private VkDeviceMemory.
6159 DedicatedAllocation m_DedicatedAllocation;
6160 };
6161
6162 #if VMA_STATS_STRING_ENABLED
6163 uint32_t m_CreationFrameIndex;
6164 uint32_t m_BufferImageUsage; // 0 if unknown.
6165 #endif
6166
6167 void FreeUserDataString(VmaAllocator hAllocator);
6168 };
6169
6170 /*
6171 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6172 allocated memory block or free.
6173 */
6174 struct VmaSuballocation
6175 {
6176 VkDeviceSize offset;
6177 VkDeviceSize size;
6178 VmaAllocation hAllocation;
6179 VmaSuballocationType type;
6180 };
6181
6182 // Comparator for offsets.
6183 struct VmaSuballocationOffsetLess
6184 {
operatorVmaSuballocationOffsetLess6185 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6186 {
6187 return lhs.offset < rhs.offset;
6188 }
6189 };
6190 struct VmaSuballocationOffsetGreater
6191 {
operatorVmaSuballocationOffsetGreater6192 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6193 {
6194 return lhs.offset > rhs.offset;
6195 }
6196 };
6197
6198 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
6199
6200 // Cost of one additional allocation lost, as equivalent in bytes.
6201 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
6202
6203 enum class VmaAllocationRequestType
6204 {
6205 Normal,
6206 // Used by "Linear" algorithm.
6207 UpperAddress,
6208 EndOf1st,
6209 EndOf2nd,
6210 };
6211
6212 /*
6213 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6214
6215 If canMakeOtherLost was false:
6216 - item points to a FREE suballocation.
6217 - itemsToMakeLostCount is 0.
6218
6219 If canMakeOtherLost was true:
6220 - item points to first of sequence of suballocations, which are either FREE,
6221 or point to VmaAllocations that can become lost.
6222 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
6223 the requested allocation to succeed.
6224 */
6225 struct VmaAllocationRequest
6226 {
6227 VkDeviceSize offset;
6228 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
6229 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
6230 VmaSuballocationList::iterator item;
6231 size_t itemsToMakeLostCount;
6232 void* customData;
6233 VmaAllocationRequestType type;
6234
CalcCostVmaAllocationRequest6235 VkDeviceSize CalcCost() const
6236 {
6237 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
6238 }
6239 };
6240
6241 /*
6242 Data structure used for bookkeeping of allocations and unused ranges of memory
6243 in a single VkDeviceMemory block.
6244 */
6245 class VmaBlockMetadata
6246 {
6247 public:
6248 VmaBlockMetadata(VmaAllocator hAllocator);
~VmaBlockMetadata()6249 virtual ~VmaBlockMetadata() { }
Init(VkDeviceSize size)6250 virtual void Init(VkDeviceSize size) { m_Size = size; }
6251
6252 // Validates all data structures inside this object. If not valid, returns false.
6253 virtual bool Validate() const = 0;
GetSize()6254 VkDeviceSize GetSize() const { return m_Size; }
6255 virtual size_t GetAllocationCount() const = 0;
6256 virtual VkDeviceSize GetSumFreeSize() const = 0;
6257 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
6258 // Returns true if this block is empty - contains only single free suballocation.
6259 virtual bool IsEmpty() const = 0;
6260
6261 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
6262 // Shouldn't modify blockCount.
6263 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
6264
6265 #if VMA_STATS_STRING_ENABLED
6266 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6267 #endif
6268
6269 // Tries to find a place for suballocation with given parameters inside this block.
6270 // If succeeded, fills pAllocationRequest and returns true.
6271 // If failed, returns false.
6272 virtual bool CreateAllocationRequest(
6273 uint32_t currentFrameIndex,
6274 uint32_t frameInUseCount,
6275 VkDeviceSize bufferImageGranularity,
6276 VkDeviceSize allocSize,
6277 VkDeviceSize allocAlignment,
6278 bool upperAddress,
6279 VmaSuballocationType allocType,
6280 bool canMakeOtherLost,
6281 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6282 uint32_t strategy,
6283 VmaAllocationRequest* pAllocationRequest) = 0;
6284
6285 virtual bool MakeRequestedAllocationsLost(
6286 uint32_t currentFrameIndex,
6287 uint32_t frameInUseCount,
6288 VmaAllocationRequest* pAllocationRequest) = 0;
6289
6290 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
6291
6292 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6293
6294 // Makes actual allocation based on request. Request must already be checked and valid.
6295 virtual void Alloc(
6296 const VmaAllocationRequest& request,
6297 VmaSuballocationType type,
6298 VkDeviceSize allocSize,
6299 VmaAllocation hAllocation) = 0;
6300
6301 // Frees suballocation assigned to given memory region.
6302 virtual void Free(const VmaAllocation allocation) = 0;
6303 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
6304
6305 protected:
GetAllocationCallbacks()6306 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
6307
6308 #if VMA_STATS_STRING_ENABLED
6309 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6310 VkDeviceSize unusedBytes,
6311 size_t allocationCount,
6312 size_t unusedRangeCount) const;
6313 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6314 VkDeviceSize offset,
6315 VmaAllocation hAllocation) const;
6316 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6317 VkDeviceSize offset,
6318 VkDeviceSize size) const;
6319 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6320 #endif
6321
6322 private:
6323 VkDeviceSize m_Size;
6324 const VkAllocationCallbacks* m_pAllocationCallbacks;
6325 };
6326
6327 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
6328 VMA_ASSERT(0 && "Validation failed: " #cond); \
6329 return false; \
6330 } } while(false)
6331
6332 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6333 {
6334 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6335 public:
6336 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
6337 virtual ~VmaBlockMetadata_Generic();
6338 virtual void Init(VkDeviceSize size);
6339
6340 virtual bool Validate() const;
GetAllocationCount()6341 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()6342 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6343 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
6344 virtual bool IsEmpty() const;
6345
6346 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6347 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6348
6349 #if VMA_STATS_STRING_ENABLED
6350 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6351 #endif
6352
6353 virtual bool CreateAllocationRequest(
6354 uint32_t currentFrameIndex,
6355 uint32_t frameInUseCount,
6356 VkDeviceSize bufferImageGranularity,
6357 VkDeviceSize allocSize,
6358 VkDeviceSize allocAlignment,
6359 bool upperAddress,
6360 VmaSuballocationType allocType,
6361 bool canMakeOtherLost,
6362 uint32_t strategy,
6363 VmaAllocationRequest* pAllocationRequest);
6364
6365 virtual bool MakeRequestedAllocationsLost(
6366 uint32_t currentFrameIndex,
6367 uint32_t frameInUseCount,
6368 VmaAllocationRequest* pAllocationRequest);
6369
6370 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6371
6372 virtual VkResult CheckCorruption(const void* pBlockData);
6373
6374 virtual void Alloc(
6375 const VmaAllocationRequest& request,
6376 VmaSuballocationType type,
6377 VkDeviceSize allocSize,
6378 VmaAllocation hAllocation);
6379
6380 virtual void Free(const VmaAllocation allocation);
6381 virtual void FreeAtOffset(VkDeviceSize offset);
6382
6383 ////////////////////////////////////////////////////////////////////////////////
6384 // For defragmentation
6385
6386 bool IsBufferImageGranularityConflictPossible(
6387 VkDeviceSize bufferImageGranularity,
6388 VmaSuballocationType& inOutPrevSuballocType) const;
6389
6390 private:
6391 friend class VmaDefragmentationAlgorithm_Generic;
6392 friend class VmaDefragmentationAlgorithm_Fast;
6393
6394 uint32_t m_FreeCount;
6395 VkDeviceSize m_SumFreeSize;
6396 VmaSuballocationList m_Suballocations;
6397 // Suballocations that are free and have size greater than certain threshold.
6398 // Sorted by size, ascending.
6399 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
6400
6401 bool ValidateFreeSuballocationList() const;
6402
6403 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6404 // If yes, fills pOffset and returns true. If no, returns false.
6405 bool CheckAllocation(
6406 uint32_t currentFrameIndex,
6407 uint32_t frameInUseCount,
6408 VkDeviceSize bufferImageGranularity,
6409 VkDeviceSize allocSize,
6410 VkDeviceSize allocAlignment,
6411 VmaSuballocationType allocType,
6412 VmaSuballocationList::const_iterator suballocItem,
6413 bool canMakeOtherLost,
6414 VkDeviceSize* pOffset,
6415 size_t* itemsToMakeLostCount,
6416 VkDeviceSize* pSumFreeSize,
6417 VkDeviceSize* pSumItemSize) const;
6418 // Given free suballocation, it merges it with following one, which must also be free.
6419 void MergeFreeWithNext(VmaSuballocationList::iterator item);
6420 // Releases given suballocation, making it free.
6421 // Merges it with adjacent free suballocations if applicable.
6422 // Returns iterator to new free suballocation at this place.
6423 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6424 // Given free suballocation, it inserts it into sorted list of
6425 // m_FreeSuballocationsBySize if it's suitable.
6426 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6427 // Given free suballocation, it removes it from sorted list of
6428 // m_FreeSuballocationsBySize if it's suitable.
6429 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6430 };
6431
6432 /*
6433 Allocations and their references in internal data structure look like this:
6434
6435 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
6436
6437 0 +-------+
6438 | |
6439 | |
6440 | |
6441 +-------+
6442 | Alloc | 1st[m_1stNullItemsBeginCount]
6443 +-------+
6444 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6445 +-------+
6446 | ... |
6447 +-------+
6448 | Alloc | 1st[1st.size() - 1]
6449 +-------+
6450 | |
6451 | |
6452 | |
6453 GetSize() +-------+
6454
6455 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
6456
6457 0 +-------+
6458 | Alloc | 2nd[0]
6459 +-------+
6460 | Alloc | 2nd[1]
6461 +-------+
6462 | ... |
6463 +-------+
6464 | Alloc | 2nd[2nd.size() - 1]
6465 +-------+
6466 | |
6467 | |
6468 | |
6469 +-------+
6470 | Alloc | 1st[m_1stNullItemsBeginCount]
6471 +-------+
6472 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6473 +-------+
6474 | ... |
6475 +-------+
6476 | Alloc | 1st[1st.size() - 1]
6477 +-------+
6478 | |
6479 GetSize() +-------+
6480
6481 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
6482
6483 0 +-------+
6484 | |
6485 | |
6486 | |
6487 +-------+
6488 | Alloc | 1st[m_1stNullItemsBeginCount]
6489 +-------+
6490 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
6491 +-------+
6492 | ... |
6493 +-------+
6494 | Alloc | 1st[1st.size() - 1]
6495 +-------+
6496 | |
6497 | |
6498 | |
6499 +-------+
6500 | Alloc | 2nd[2nd.size() - 1]
6501 +-------+
6502 | ... |
6503 +-------+
6504 | Alloc | 2nd[1]
6505 +-------+
6506 | Alloc | 2nd[0]
6507 GetSize() +-------+
6508
6509 */
6510 class VmaBlockMetadata_Linear : public VmaBlockMetadata
6511 {
6512 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
6513 public:
6514 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
6515 virtual ~VmaBlockMetadata_Linear();
6516 virtual void Init(VkDeviceSize size);
6517
6518 virtual bool Validate() const;
6519 virtual size_t GetAllocationCount() const;
GetSumFreeSize()6520 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
6521 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6522 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
6523
6524 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6525 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6526
6527 #if VMA_STATS_STRING_ENABLED
6528 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6529 #endif
6530
6531 virtual bool CreateAllocationRequest(
6532 uint32_t currentFrameIndex,
6533 uint32_t frameInUseCount,
6534 VkDeviceSize bufferImageGranularity,
6535 VkDeviceSize allocSize,
6536 VkDeviceSize allocAlignment,
6537 bool upperAddress,
6538 VmaSuballocationType allocType,
6539 bool canMakeOtherLost,
6540 uint32_t strategy,
6541 VmaAllocationRequest* pAllocationRequest);
6542
6543 virtual bool MakeRequestedAllocationsLost(
6544 uint32_t currentFrameIndex,
6545 uint32_t frameInUseCount,
6546 VmaAllocationRequest* pAllocationRequest);
6547
6548 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6549
6550 virtual VkResult CheckCorruption(const void* pBlockData);
6551
6552 virtual void Alloc(
6553 const VmaAllocationRequest& request,
6554 VmaSuballocationType type,
6555 VkDeviceSize allocSize,
6556 VmaAllocation hAllocation);
6557
6558 virtual void Free(const VmaAllocation allocation);
6559 virtual void FreeAtOffset(VkDeviceSize offset);
6560
6561 private:
6562 /*
6563 There are two suballocation vectors, used in ping-pong way.
6564 The one with index m_1stVectorIndex is called 1st.
6565 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
6566 2nd can be non-empty only when 1st is not empty.
6567 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
6568 */
6569 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
6570
6571 enum SECOND_VECTOR_MODE
6572 {
6573 SECOND_VECTOR_EMPTY,
6574 /*
6575 Suballocations in 2nd vector are created later than the ones in 1st, but they
6576 all have smaller offset.
6577 */
6578 SECOND_VECTOR_RING_BUFFER,
6579 /*
6580 Suballocations in 2nd vector are upper side of double stack.
6581 They all have offsets higher than those in 1st vector.
6582 Top of this stack means smaller offsets, but higher indices in this vector.
6583 */
6584 SECOND_VECTOR_DOUBLE_STACK,
6585 };
6586
6587 VkDeviceSize m_SumFreeSize;
6588 SuballocationVectorType m_Suballocations0, m_Suballocations1;
6589 uint32_t m_1stVectorIndex;
6590 SECOND_VECTOR_MODE m_2ndVectorMode;
6591
AccessSuballocations1st()6592 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6593 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()6594 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()6595 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
6596
6597 // Number of items in 1st vector with hAllocation = null at the beginning.
6598 size_t m_1stNullItemsBeginCount;
6599 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
6600 size_t m_1stNullItemsMiddleCount;
6601 // Number of items in 2nd vector with hAllocation = null.
6602 size_t m_2ndNullItemsCount;
6603
6604 bool ShouldCompact1st() const;
6605 void CleanupAfterFree();
6606
6607 bool CreateAllocationRequest_LowerAddress(
6608 uint32_t currentFrameIndex,
6609 uint32_t frameInUseCount,
6610 VkDeviceSize bufferImageGranularity,
6611 VkDeviceSize allocSize,
6612 VkDeviceSize allocAlignment,
6613 VmaSuballocationType allocType,
6614 bool canMakeOtherLost,
6615 uint32_t strategy,
6616 VmaAllocationRequest* pAllocationRequest);
6617 bool CreateAllocationRequest_UpperAddress(
6618 uint32_t currentFrameIndex,
6619 uint32_t frameInUseCount,
6620 VkDeviceSize bufferImageGranularity,
6621 VkDeviceSize allocSize,
6622 VkDeviceSize allocAlignment,
6623 VmaSuballocationType allocType,
6624 bool canMakeOtherLost,
6625 uint32_t strategy,
6626 VmaAllocationRequest* pAllocationRequest);
6627 };
6628
6629 /*
6630 - GetSize() is the original size of allocated memory block.
6631 - m_UsableSize is this size aligned down to a power of two.
6632 All allocations and calculations happen relative to m_UsableSize.
6633 - GetUnusableSize() is the difference between them.
6634 It is repoted as separate, unused range, not available for allocations.
6635
6636 Node at level 0 has size = m_UsableSize.
6637 Each next level contains nodes with size 2 times smaller than current level.
6638 m_LevelCount is the maximum number of levels to use in the current object.
6639 */
6640 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
6641 {
6642 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
6643 public:
6644 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
6645 virtual ~VmaBlockMetadata_Buddy();
6646 virtual void Init(VkDeviceSize size);
6647
6648 virtual bool Validate() const;
GetAllocationCount()6649 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
GetSumFreeSize()6650 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
6651 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
IsEmpty()6652 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
6653
6654 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
6655 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
6656
6657 #if VMA_STATS_STRING_ENABLED
6658 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
6659 #endif
6660
6661 virtual bool CreateAllocationRequest(
6662 uint32_t currentFrameIndex,
6663 uint32_t frameInUseCount,
6664 VkDeviceSize bufferImageGranularity,
6665 VkDeviceSize allocSize,
6666 VkDeviceSize allocAlignment,
6667 bool upperAddress,
6668 VmaSuballocationType allocType,
6669 bool canMakeOtherLost,
6670 uint32_t strategy,
6671 VmaAllocationRequest* pAllocationRequest);
6672
6673 virtual bool MakeRequestedAllocationsLost(
6674 uint32_t currentFrameIndex,
6675 uint32_t frameInUseCount,
6676 VmaAllocationRequest* pAllocationRequest);
6677
6678 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
6679
CheckCorruption(const void * pBlockData)6680 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
6681
6682 virtual void Alloc(
6683 const VmaAllocationRequest& request,
6684 VmaSuballocationType type,
6685 VkDeviceSize allocSize,
6686 VmaAllocation hAllocation);
6687
Free(const VmaAllocation allocation)6688 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
FreeAtOffset(VkDeviceSize offset)6689 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
6690
6691 private:
6692 static const VkDeviceSize MIN_NODE_SIZE = 32;
6693 static const size_t MAX_LEVELS = 30;
6694
6695 struct ValidationContext
6696 {
6697 size_t calculatedAllocationCount;
6698 size_t calculatedFreeCount;
6699 VkDeviceSize calculatedSumFreeSize;
6700
ValidationContextValidationContext6701 ValidationContext() :
6702 calculatedAllocationCount(0),
6703 calculatedFreeCount(0),
6704 calculatedSumFreeSize(0) { }
6705 };
6706
6707 struct Node
6708 {
6709 VkDeviceSize offset;
6710 enum TYPE
6711 {
6712 TYPE_FREE,
6713 TYPE_ALLOCATION,
6714 TYPE_SPLIT,
6715 TYPE_COUNT
6716 } type;
6717 Node* parent;
6718 Node* buddy;
6719
6720 union
6721 {
6722 struct
6723 {
6724 Node* prev;
6725 Node* next;
6726 } free;
6727 struct
6728 {
6729 VmaAllocation alloc;
6730 } allocation;
6731 struct
6732 {
6733 Node* leftChild;
6734 } split;
6735 };
6736 };
6737
6738 // Size of the memory block aligned down to a power of two.
6739 VkDeviceSize m_UsableSize;
6740 uint32_t m_LevelCount;
6741
6742 Node* m_Root;
6743 struct {
6744 Node* front;
6745 Node* back;
6746 } m_FreeList[MAX_LEVELS];
6747 // Number of nodes in the tree with type == TYPE_ALLOCATION.
6748 size_t m_AllocationCount;
6749 // Number of nodes in the tree with type == TYPE_FREE.
6750 size_t m_FreeCount;
6751 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
6752 VkDeviceSize m_SumFreeSize;
6753
GetUnusableSize()6754 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
6755 void DeleteNode(Node* node);
6756 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
6757 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
LevelToNodeSize(uint32_t level)6758 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
6759 // Alloc passed just for validation. Can be null.
6760 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
6761 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
6762 // Adds node to the front of FreeList at given level.
6763 // node->type must be FREE.
6764 // node->free.prev, next can be undefined.
6765 void AddToFreeListFront(uint32_t level, Node* node);
6766 // Removes node from FreeList at given level.
6767 // node->type must be FREE.
6768 // node->free.prev, next stay untouched.
6769 void RemoveFromFreeList(uint32_t level, Node* node);
6770
6771 #if VMA_STATS_STRING_ENABLED
6772 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
6773 #endif
6774 };
6775
6776 /*
6777 Represents a single block of device memory (`VkDeviceMemory`) with all the
6778 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
6779
6780 Thread-safety: This class must be externally synchronized.
6781 */
6782 class VmaDeviceMemoryBlock
6783 {
6784 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
6785 public:
6786 VmaBlockMetadata* m_pMetadata;
6787
6788 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
6789
~VmaDeviceMemoryBlock()6790 ~VmaDeviceMemoryBlock()
6791 {
6792 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
6793 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6794 }
6795
6796 // Always call after construction.
6797 void Init(
6798 VmaAllocator hAllocator,
6799 VmaPool hParentPool,
6800 uint32_t newMemoryTypeIndex,
6801 VkDeviceMemory newMemory,
6802 VkDeviceSize newSize,
6803 uint32_t id,
6804 uint32_t algorithm);
6805 // Always call before destruction.
6806 void Destroy(VmaAllocator allocator);
6807
GetParentPool()6808 VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()6809 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()6810 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()6811 uint32_t GetId() const { return m_Id; }
GetMappedData()6812 void* GetMappedData() const { return m_pMappedData; }
6813
6814 // Validates all data structures inside this object. If not valid, returns false.
6815 bool Validate() const;
6816
6817 VkResult CheckCorruption(VmaAllocator hAllocator);
6818
6819 // ppData can be null.
6820 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6821 void Unmap(VmaAllocator hAllocator, uint32_t count);
6822
6823 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6824 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6825
6826 VkResult BindBufferMemory(
6827 const VmaAllocator hAllocator,
6828 const VmaAllocation hAllocation,
6829 VkDeviceSize allocationLocalOffset,
6830 VkBuffer hBuffer,
6831 const void* pNext);
6832 VkResult BindImageMemory(
6833 const VmaAllocator hAllocator,
6834 const VmaAllocation hAllocation,
6835 VkDeviceSize allocationLocalOffset,
6836 VkImage hImage,
6837 const void* pNext);
6838
6839 private:
6840 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6841 uint32_t m_MemoryTypeIndex;
6842 uint32_t m_Id;
6843 VkDeviceMemory m_hMemory;
6844
6845 /*
6846 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6847 Also protects m_MapCount, m_pMappedData.
6848 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6849 */
6850 VMA_MUTEX m_Mutex;
6851 uint32_t m_MapCount;
6852 void* m_pMappedData;
6853 };
6854
6855 struct VmaPointerLess
6856 {
operatorVmaPointerLess6857 bool operator()(const void* lhs, const void* rhs) const
6858 {
6859 return lhs < rhs;
6860 }
6861 };
6862
6863 struct VmaDefragmentationMove
6864 {
6865 size_t srcBlockIndex;
6866 size_t dstBlockIndex;
6867 VkDeviceSize srcOffset;
6868 VkDeviceSize dstOffset;
6869 VkDeviceSize size;
6870 VmaAllocation hAllocation;
6871 VmaDeviceMemoryBlock* pSrcBlock;
6872 VmaDeviceMemoryBlock* pDstBlock;
6873 };
6874
6875 class VmaDefragmentationAlgorithm;
6876
6877 /*
6878 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6879 Vulkan memory type.
6880
6881 Synchronized internally with a mutex.
6882 */
6883 struct VmaBlockVector
6884 {
6885 VMA_CLASS_NO_COPY(VmaBlockVector)
6886 public:
6887 VmaBlockVector(
6888 VmaAllocator hAllocator,
6889 VmaPool hParentPool,
6890 uint32_t memoryTypeIndex,
6891 VkDeviceSize preferredBlockSize,
6892 size_t minBlockCount,
6893 size_t maxBlockCount,
6894 VkDeviceSize bufferImageGranularity,
6895 uint32_t frameInUseCount,
6896 bool explicitBlockSize,
6897 uint32_t algorithm);
6898 ~VmaBlockVector();
6899
6900 VkResult CreateMinBlocks();
6901
GetAllocatorVmaBlockVector6902 VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector6903 VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector6904 bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector6905 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector6906 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector6907 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector6908 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector6909 uint32_t GetAlgorithm() const { return m_Algorithm; }
6910
6911 void GetPoolStats(VmaPoolStats* pStats);
6912
6913 bool IsEmpty();
6914 bool IsCorruptionDetectionEnabled() const;
6915
6916 VkResult Allocate(
6917 uint32_t currentFrameIndex,
6918 VkDeviceSize size,
6919 VkDeviceSize alignment,
6920 const VmaAllocationCreateInfo& createInfo,
6921 VmaSuballocationType suballocType,
6922 size_t allocationCount,
6923 VmaAllocation* pAllocations);
6924
6925 // OH ISSUE: VMA preAlloc
6926 VkResult AllocateReserved(
6927 uint32_t currentFrameIndex,
6928 VkDeviceSize size,
6929 VkDeviceSize alignment,
6930 const VmaAllocationCreateInfo& createInfo,
6931 VmaSuballocationType suballocType,
6932 VmaAllocation* pAllocation);
6933
6934 friend void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2);
6935
6936 void Free(const VmaAllocation hAllocation);
6937
6938 void FreeReserved(const VmaAllocation hAllocation);
6939
6940 // Adds statistics of this BlockVector to pStats.
6941 void AddStats(VmaStats* pStats);
6942
6943 void FreeEmptyBlock();
6944
6945 #if VMA_STATS_STRING_ENABLED
6946 void PrintDetailedMap(class VmaJsonWriter& json);
6947 #endif
6948
6949 void MakePoolAllocationsLost(
6950 uint32_t currentFrameIndex,
6951 size_t* pLostAllocationCount);
6952 VkResult CheckCorruption();
6953
6954 // Saves results in pCtx->res.
6955 void Defragment(
6956 class VmaBlockVectorDefragmentationContext* pCtx,
6957 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6958 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6959 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6960 VkCommandBuffer commandBuffer);
6961 void DefragmentationEnd(
6962 class VmaBlockVectorDefragmentationContext* pCtx,
6963 uint32_t flags,
6964 VmaDefragmentationStats* pStats);
6965
6966 uint32_t ProcessDefragmentations(
6967 class VmaBlockVectorDefragmentationContext *pCtx,
6968 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6969
6970 void CommitDefragmentations(
6971 class VmaBlockVectorDefragmentationContext *pCtx,
6972 VmaDefragmentationStats* pStats);
6973
6974 ////////////////////////////////////////////////////////////////////////////////
6975 // To be used only while the m_Mutex is locked. Used during defragmentation.
6976
GetBlockCountVmaBlockVector6977 size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector6978 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6979 size_t CalcAllocationCount() const;
6980 bool IsBufferImageGranularityConflictPossible() const;
IsNewBlockFlagVmaBlockVector6981 bool IsNewBlockFlag() const { return m_NewBlockFlag; }
ClearNewBlockFlagVmaBlockVector6982 void ClearNewBlockFlag() { m_NewBlockFlag = false; }
6983
6984 private:
6985 friend class VmaDefragmentationAlgorithm_Generic;
6986
6987 const VmaAllocator m_hAllocator;
6988 const VmaPool m_hParentPool;
6989 const uint32_t m_MemoryTypeIndex;
6990 const VkDeviceSize m_PreferredBlockSize;
6991 const size_t m_MinBlockCount;
6992 const size_t m_MaxBlockCount;
6993 const VkDeviceSize m_BufferImageGranularity;
6994 const uint32_t m_FrameInUseCount;
6995 const bool m_ExplicitBlockSize;
6996 const uint32_t m_Algorithm;
6997 VMA_RW_MUTEX m_Mutex;
6998
6999 /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7000 a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7001 bool m_HasEmptyBlock;
7002 // Incrementally sorted by sumFreeSize, ascending.
7003 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7004 uint32_t m_NextBlockId;
7005 bool m_NewBlockFlag = false;
7006
7007 VkDeviceSize CalcMaxBlockSize() const;
7008
7009 // Finds and removes given block from vector.
7010 void Remove(VmaDeviceMemoryBlock* pBlock);
7011
7012 // Performs single step in sorting m_Blocks. They may not be fully sorted
7013 // after this call.
7014 void IncrementallySortBlocks();
7015
7016 VkResult AllocatePage(
7017 uint32_t currentFrameIndex,
7018 VkDeviceSize size,
7019 VkDeviceSize alignment,
7020 const VmaAllocationCreateInfo& createInfo,
7021 VmaSuballocationType suballocType,
7022 VmaAllocation* pAllocation);
7023
7024 // To be used only without CAN_MAKE_OTHER_LOST flag.
7025 VkResult AllocateFromBlock(
7026 VmaDeviceMemoryBlock* pBlock,
7027 uint32_t currentFrameIndex,
7028 VkDeviceSize size,
7029 VkDeviceSize alignment,
7030 VmaAllocationCreateFlags allocFlags,
7031 void* pUserData,
7032 VmaSuballocationType suballocType,
7033 uint32_t strategy,
7034 VmaAllocation* pAllocation);
7035
7036 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7037
7038 // Saves result to pCtx->res.
7039 void ApplyDefragmentationMovesCpu(
7040 class VmaBlockVectorDefragmentationContext* pDefragCtx,
7041 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7042 // Saves result to pCtx->res.
7043 void ApplyDefragmentationMovesGpu(
7044 class VmaBlockVectorDefragmentationContext* pDefragCtx,
7045 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7046 VkCommandBuffer commandBuffer);
7047
7048 /*
7049 Used during defragmentation. pDefragmentationStats is optional. It's in/out
7050 - updated with new data.
7051 */
7052 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7053
7054 void UpdateHasEmptyBlock();
7055 };
7056
7057 struct VmaPool_T
7058 {
7059 VMA_CLASS_NO_COPY(VmaPool_T)
7060 public:
7061 VmaBlockVector m_BlockVector;
7062
7063 VmaPool_T(
7064 VmaAllocator hAllocator,
7065 const VmaPoolCreateInfo& createInfo,
7066 VkDeviceSize preferredBlockSize);
7067 ~VmaPool_T();
7068
GetIdVmaPool_T7069 uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T7070 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7071
GetNameVmaPool_T7072 const char* GetName() const { return m_Name; }
7073 void SetName(const char* pName);
7074
7075 #if VMA_STATS_STRING_ENABLED
7076 //void PrintDetailedMap(class VmaStringBuilder& sb);
7077 #endif
7078
7079 private:
7080 uint32_t m_Id;
7081 char* m_Name;
7082 };
7083
7084 /*
7085 Performs defragmentation:
7086
7087 - Updates `pBlockVector->m_pMetadata`.
7088 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7089 - Does not move actual data, only returns requested moves as `moves`.
7090 */
7091 class VmaDefragmentationAlgorithm
7092 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)7093 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7094 public:
7095 VmaDefragmentationAlgorithm(
7096 VmaAllocator hAllocator,
7097 VmaBlockVector* pBlockVector,
7098 uint32_t currentFrameIndex) :
7099 m_hAllocator(hAllocator),
7100 m_pBlockVector(pBlockVector),
7101 m_CurrentFrameIndex(currentFrameIndex)
7102 {
7103 }
~VmaDefragmentationAlgorithm()7104 virtual ~VmaDefragmentationAlgorithm()
7105 {
7106 }
7107
7108 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7109 virtual void AddAll() = 0;
7110
7111 virtual VkResult Defragment(
7112 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7113 VkDeviceSize maxBytesToMove,
7114 uint32_t maxAllocationsToMove,
7115 VmaDefragmentationFlags flags) = 0;
7116
7117 virtual VkDeviceSize GetBytesMoved() const = 0;
7118 virtual uint32_t GetAllocationsMoved() const = 0;
7119
7120 protected:
7121 VmaAllocator const m_hAllocator;
7122 VmaBlockVector* const m_pBlockVector;
7123 const uint32_t m_CurrentFrameIndex;
7124
7125 struct AllocationInfo
7126 {
7127 VmaAllocation m_hAllocation;
7128 VkBool32* m_pChanged;
7129
AllocationInfoAllocationInfo7130 AllocationInfo() :
7131 m_hAllocation(VK_NULL_HANDLE),
7132 m_pChanged(VMA_NULL)
7133 {
7134 }
AllocationInfoAllocationInfo7135 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7136 m_hAllocation(hAlloc),
7137 m_pChanged(pChanged)
7138 {
7139 }
7140 };
7141 };
7142
7143 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7144 {
7145 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7146 public:
7147 VmaDefragmentationAlgorithm_Generic(
7148 VmaAllocator hAllocator,
7149 VmaBlockVector* pBlockVector,
7150 uint32_t currentFrameIndex,
7151 bool overlappingMoveSupported);
7152 virtual ~VmaDefragmentationAlgorithm_Generic();
7153
7154 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7155 virtual void AddAll() { m_AllAllocations = true; }
7156
7157 virtual VkResult Defragment(
7158 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7159 VkDeviceSize maxBytesToMove,
7160 uint32_t maxAllocationsToMove,
7161 VmaDefragmentationFlags flags);
7162
GetBytesMoved()7163 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7164 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7165
7166 private:
7167 uint32_t m_AllocationCount;
7168 bool m_AllAllocations;
7169
7170 VkDeviceSize m_BytesMoved;
7171 uint32_t m_AllocationsMoved;
7172
7173 struct AllocationInfoSizeGreater
7174 {
operatorAllocationInfoSizeGreater7175 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7176 {
7177 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7178 }
7179 };
7180
7181 struct AllocationInfoOffsetGreater
7182 {
operatorAllocationInfoOffsetGreater7183 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7184 {
7185 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7186 }
7187 };
7188
7189 struct BlockInfo
7190 {
7191 size_t m_OriginalBlockIndex;
7192 VmaDeviceMemoryBlock* m_pBlock;
7193 bool m_HasNonMovableAllocations;
7194 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7195
BlockInfoBlockInfo7196 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7197 m_OriginalBlockIndex(SIZE_MAX),
7198 m_pBlock(VMA_NULL),
7199 m_HasNonMovableAllocations(true),
7200 m_Allocations(pAllocationCallbacks)
7201 {
7202 }
7203
CalcHasNonMovableAllocationsBlockInfo7204 void CalcHasNonMovableAllocations()
7205 {
7206 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7207 const size_t defragmentAllocCount = m_Allocations.size();
7208 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7209 }
7210
SortAllocationsBySizeDescendingBlockInfo7211 void SortAllocationsBySizeDescending()
7212 {
7213 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7214 }
7215
SortAllocationsByOffsetDescendingBlockInfo7216 void SortAllocationsByOffsetDescending()
7217 {
7218 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7219 }
7220 };
7221
7222 struct BlockPointerLess
7223 {
operatorBlockPointerLess7224 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7225 {
7226 return pLhsBlockInfo->m_pBlock < pRhsBlock;
7227 }
operatorBlockPointerLess7228 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7229 {
7230 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7231 }
7232 };
7233
7234 // 1. Blocks with some non-movable allocations go first.
7235 // 2. Blocks with smaller sumFreeSize go first.
7236 struct BlockInfoCompareMoveDestination
7237 {
operatorBlockInfoCompareMoveDestination7238 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7239 {
7240 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7241 {
7242 return true;
7243 }
7244 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7245 {
7246 return false;
7247 }
7248 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7249 {
7250 return true;
7251 }
7252 return false;
7253 }
7254 };
7255
7256 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7257 BlockInfoVector m_Blocks;
7258
7259 VkResult DefragmentRound(
7260 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7261 VkDeviceSize maxBytesToMove,
7262 uint32_t maxAllocationsToMove,
7263 bool freeOldAllocations);
7264
7265 size_t CalcBlocksWithNonMovableCount() const;
7266
7267 static bool MoveMakesSense(
7268 size_t dstBlockIndex, VkDeviceSize dstOffset,
7269 size_t srcBlockIndex, VkDeviceSize srcOffset);
7270 };
7271
7272 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7273 {
7274 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7275 public:
7276 VmaDefragmentationAlgorithm_Fast(
7277 VmaAllocator hAllocator,
7278 VmaBlockVector* pBlockVector,
7279 uint32_t currentFrameIndex,
7280 bool overlappingMoveSupported);
7281 virtual ~VmaDefragmentationAlgorithm_Fast();
7282
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7283 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()7284 virtual void AddAll() { m_AllAllocations = true; }
7285
7286 virtual VkResult Defragment(
7287 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7288 VkDeviceSize maxBytesToMove,
7289 uint32_t maxAllocationsToMove,
7290 VmaDefragmentationFlags flags);
7291
GetBytesMoved()7292 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7293 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7294
7295 private:
7296 struct BlockInfo
7297 {
7298 size_t origBlockIndex;
7299 };
7300
7301 class FreeSpaceDatabase
7302 {
7303 public:
FreeSpaceDatabase()7304 FreeSpaceDatabase()
7305 {
7306 FreeSpace s = {};
7307 s.blockInfoIndex = SIZE_MAX;
7308 for(size_t i = 0; i < MAX_COUNT; ++i)
7309 {
7310 m_FreeSpaces[i] = s;
7311 }
7312 }
7313
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)7314 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7315 {
7316 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7317 {
7318 return;
7319 }
7320
7321 // Find first invalid or the smallest structure.
7322 size_t bestIndex = SIZE_MAX;
7323 for(size_t i = 0; i < MAX_COUNT; ++i)
7324 {
7325 // Empty structure.
7326 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7327 {
7328 bestIndex = i;
7329 break;
7330 }
7331 if(m_FreeSpaces[i].size < size &&
7332 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7333 {
7334 bestIndex = i;
7335 }
7336 }
7337
7338 if(bestIndex != SIZE_MAX)
7339 {
7340 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7341 m_FreeSpaces[bestIndex].offset = offset;
7342 m_FreeSpaces[bestIndex].size = size;
7343 }
7344 }
7345
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)7346 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7347 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7348 {
7349 size_t bestIndex = SIZE_MAX;
7350 VkDeviceSize bestFreeSpaceAfter = 0;
7351 for(size_t i = 0; i < MAX_COUNT; ++i)
7352 {
7353 // Structure is valid.
7354 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7355 {
7356 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7357 // Allocation fits into this structure.
7358 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7359 {
7360 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7361 (dstOffset + size);
7362 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7363 {
7364 bestIndex = i;
7365 bestFreeSpaceAfter = freeSpaceAfter;
7366 }
7367 }
7368 }
7369 }
7370
7371 if(bestIndex != SIZE_MAX)
7372 {
7373 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7374 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7375
7376 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7377 {
7378 // Leave this structure for remaining empty space.
7379 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7380 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7381 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7382 }
7383 else
7384 {
7385 // This structure becomes invalid.
7386 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7387 }
7388
7389 return true;
7390 }
7391
7392 return false;
7393 }
7394
7395 private:
7396 static const size_t MAX_COUNT = 4;
7397
7398 struct FreeSpace
7399 {
7400 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7401 VkDeviceSize offset;
7402 VkDeviceSize size;
7403 } m_FreeSpaces[MAX_COUNT];
7404 };
7405
7406 const bool m_OverlappingMoveSupported;
7407
7408 uint32_t m_AllocationCount;
7409 bool m_AllAllocations;
7410
7411 VkDeviceSize m_BytesMoved;
7412 uint32_t m_AllocationsMoved;
7413
7414 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7415
7416 void PreprocessMetadata();
7417 void PostprocessMetadata();
7418 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7419 };
7420
7421 struct VmaBlockDefragmentationContext
7422 {
7423 enum BLOCK_FLAG
7424 {
7425 BLOCK_FLAG_USED = 0x00000001,
7426 };
7427 uint32_t flags;
7428 VkBuffer hBuffer;
7429 };
7430
7431 class VmaBlockVectorDefragmentationContext
7432 {
7433 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7434 public:
7435 VkResult res;
7436 bool mutexLocked;
7437 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7438 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7439 uint32_t defragmentationMovesProcessed;
7440 uint32_t defragmentationMovesCommitted;
7441 bool hasDefragmentationPlan;
7442
7443 VmaBlockVectorDefragmentationContext(
7444 VmaAllocator hAllocator,
7445 VmaPool hCustomPool, // Optional.
7446 VmaBlockVector* pBlockVector,
7447 uint32_t currFrameIndex);
7448 ~VmaBlockVectorDefragmentationContext();
7449
GetCustomPool()7450 VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()7451 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()7452 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7453
7454 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7455 void AddAll() { m_AllAllocations = true; }
7456
7457 void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7458
7459 private:
7460 const VmaAllocator m_hAllocator;
7461 // Null if not from custom pool.
7462 const VmaPool m_hCustomPool;
7463 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7464 VmaBlockVector* const m_pBlockVector;
7465 const uint32_t m_CurrFrameIndex;
7466 // Owner of this object.
7467 VmaDefragmentationAlgorithm* m_pAlgorithm;
7468
7469 struct AllocInfo
7470 {
7471 VmaAllocation hAlloc;
7472 VkBool32* pChanged;
7473 };
7474 // Used between constructor and Begin.
7475 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7476 bool m_AllAllocations;
7477 };
7478
7479 struct VmaDefragmentationContext_T
7480 {
7481 private:
7482 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7483 public:
7484 VmaDefragmentationContext_T(
7485 VmaAllocator hAllocator,
7486 uint32_t currFrameIndex,
7487 uint32_t flags,
7488 VmaDefragmentationStats* pStats);
7489 ~VmaDefragmentationContext_T();
7490
7491 void AddPools(uint32_t poolCount, const VmaPool* pPools);
7492 void AddAllocations(
7493 uint32_t allocationCount,
7494 const VmaAllocation* pAllocations,
7495 VkBool32* pAllocationsChanged);
7496
7497 /*
7498 Returns:
7499 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7500 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7501 - Negative value if error occured and object can be destroyed immediately.
7502 */
7503 VkResult Defragment(
7504 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7505 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7506 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7507
7508 VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7509 VkResult DefragmentPassEnd();
7510
7511 private:
7512 const VmaAllocator m_hAllocator;
7513 const uint32_t m_CurrFrameIndex;
7514 const uint32_t m_Flags;
7515 VmaDefragmentationStats* const m_pStats;
7516
7517 VkDeviceSize m_MaxCpuBytesToMove;
7518 uint32_t m_MaxCpuAllocationsToMove;
7519 VkDeviceSize m_MaxGpuBytesToMove;
7520 uint32_t m_MaxGpuAllocationsToMove;
7521
7522 // Owner of these objects.
7523 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7524 // Owner of these objects.
7525 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7526 };
7527
7528 #if VMA_RECORDING_ENABLED
7529
7530 class VmaRecorder
7531 {
7532 public:
7533 VmaRecorder();
7534 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7535 void WriteConfiguration(
7536 const VkPhysicalDeviceProperties& devProps,
7537 const VkPhysicalDeviceMemoryProperties& memProps,
7538 uint32_t vulkanApiVersion,
7539 bool dedicatedAllocationExtensionEnabled,
7540 bool bindMemory2ExtensionEnabled,
7541 bool memoryBudgetExtensionEnabled,
7542 bool deviceCoherentMemoryExtensionEnabled);
7543 ~VmaRecorder();
7544
7545 void RecordCreateAllocator(uint32_t frameIndex);
7546 void RecordDestroyAllocator(uint32_t frameIndex);
7547 void RecordCreatePool(uint32_t frameIndex,
7548 const VmaPoolCreateInfo& createInfo,
7549 VmaPool pool);
7550 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7551 void RecordAllocateMemory(uint32_t frameIndex,
7552 const VkMemoryRequirements& vkMemReq,
7553 const VmaAllocationCreateInfo& createInfo,
7554 VmaAllocation allocation);
7555 void RecordAllocateMemoryPages(uint32_t frameIndex,
7556 const VkMemoryRequirements& vkMemReq,
7557 const VmaAllocationCreateInfo& createInfo,
7558 uint64_t allocationCount,
7559 const VmaAllocation* pAllocations);
7560 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7561 const VkMemoryRequirements& vkMemReq,
7562 bool requiresDedicatedAllocation,
7563 bool prefersDedicatedAllocation,
7564 const VmaAllocationCreateInfo& createInfo,
7565 VmaAllocation allocation);
7566 void RecordAllocateMemoryForImage(uint32_t frameIndex,
7567 const VkMemoryRequirements& vkMemReq,
7568 bool requiresDedicatedAllocation,
7569 bool prefersDedicatedAllocation,
7570 const VmaAllocationCreateInfo& createInfo,
7571 VmaAllocation allocation);
7572 void RecordFreeMemory(uint32_t frameIndex,
7573 VmaAllocation allocation);
7574 void RecordFreeMemoryPages(uint32_t frameIndex,
7575 uint64_t allocationCount,
7576 const VmaAllocation* pAllocations);
7577 void RecordSetAllocationUserData(uint32_t frameIndex,
7578 VmaAllocation allocation,
7579 const void* pUserData);
7580 void RecordCreateLostAllocation(uint32_t frameIndex,
7581 VmaAllocation allocation);
7582 void RecordMapMemory(uint32_t frameIndex,
7583 VmaAllocation allocation);
7584 void RecordUnmapMemory(uint32_t frameIndex,
7585 VmaAllocation allocation);
7586 void RecordFlushAllocation(uint32_t frameIndex,
7587 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7588 void RecordInvalidateAllocation(uint32_t frameIndex,
7589 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7590 void RecordCreateBuffer(uint32_t frameIndex,
7591 const VkBufferCreateInfo& bufCreateInfo,
7592 const VmaAllocationCreateInfo& allocCreateInfo,
7593 VmaAllocation allocation);
7594 void RecordCreateImage(uint32_t frameIndex,
7595 const VkImageCreateInfo& imageCreateInfo,
7596 const VmaAllocationCreateInfo& allocCreateInfo,
7597 VmaAllocation allocation);
7598 void RecordDestroyBuffer(uint32_t frameIndex,
7599 VmaAllocation allocation);
7600 void RecordDestroyImage(uint32_t frameIndex,
7601 VmaAllocation allocation);
7602 void RecordTouchAllocation(uint32_t frameIndex,
7603 VmaAllocation allocation);
7604 void RecordGetAllocationInfo(uint32_t frameIndex,
7605 VmaAllocation allocation);
7606 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7607 VmaPool pool);
7608 void RecordDefragmentationBegin(uint32_t frameIndex,
7609 const VmaDefragmentationInfo2& info,
7610 VmaDefragmentationContext ctx);
7611 void RecordDefragmentationEnd(uint32_t frameIndex,
7612 VmaDefragmentationContext ctx);
7613 void RecordSetPoolName(uint32_t frameIndex,
7614 VmaPool pool,
7615 const char* name);
7616
7617 private:
7618 struct CallParams
7619 {
7620 uint32_t threadId;
7621 double time;
7622 };
7623
7624 class UserDataString
7625 {
7626 public:
7627 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()7628 const char* GetString() const { return m_Str; }
7629
7630 private:
7631 char m_PtrStr[17];
7632 const char* m_Str;
7633 };
7634
7635 bool m_UseMutex;
7636 VmaRecordFlags m_Flags;
7637 FILE* m_File;
7638 VMA_MUTEX m_FileMutex;
7639 std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7640
7641 void GetBasicParams(CallParams& outParams);
7642
7643 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7644 template<typename T>
PrintPointerList(uint64_t count,const T * pItems)7645 void PrintPointerList(uint64_t count, const T* pItems)
7646 {
7647 if(count)
7648 {
7649 fprintf(m_File, "%p", pItems[0]);
7650 for(uint64_t i = 1; i < count; ++i)
7651 {
7652 fprintf(m_File, " %p", pItems[i]);
7653 }
7654 }
7655 }
7656
7657 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7658 void Flush();
7659 };
7660
7661 #endif // #if VMA_RECORDING_ENABLED
7662
7663 /*
7664 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7665 */
7666 class VmaAllocationObjectAllocator
7667 {
7668 VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7669 public:
7670 VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7671
7672 template<typename... Types> VmaAllocation Allocate(Types... args);
7673 void Free(VmaAllocation hAlloc);
7674
7675 private:
7676 VMA_MUTEX m_Mutex;
7677 VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7678 };
7679
7680 struct VmaCurrentBudgetData
7681 {
7682 VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7683 VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7684
7685 #if VMA_MEMORY_BUDGET
7686 VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7687 VMA_RW_MUTEX m_BudgetMutex;
7688 uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7689 uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7690 uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7691 #endif // #if VMA_MEMORY_BUDGET
7692
VmaCurrentBudgetDataVmaCurrentBudgetData7693 VmaCurrentBudgetData()
7694 {
7695 for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7696 {
7697 m_BlockBytes[heapIndex] = 0;
7698 m_AllocationBytes[heapIndex] = 0;
7699 #if VMA_MEMORY_BUDGET
7700 m_VulkanUsage[heapIndex] = 0;
7701 m_VulkanBudget[heapIndex] = 0;
7702 m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7703 #endif
7704 }
7705
7706 #if VMA_MEMORY_BUDGET
7707 m_OperationsSinceBudgetFetch = 0;
7708 #endif
7709 }
7710
AddAllocationVmaCurrentBudgetData7711 void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7712 {
7713 m_AllocationBytes[heapIndex] += allocationSize;
7714 #if VMA_MEMORY_BUDGET
7715 ++m_OperationsSinceBudgetFetch;
7716 #endif
7717 }
7718
RemoveAllocationVmaCurrentBudgetData7719 void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7720 {
7721 VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7722 m_AllocationBytes[heapIndex] -= allocationSize;
7723 #if VMA_MEMORY_BUDGET
7724 ++m_OperationsSinceBudgetFetch;
7725 #endif
7726 }
7727 };
7728
7729 // Main allocator object.
7730 struct VmaAllocator_T
7731 {
7732 VMA_CLASS_NO_COPY(VmaAllocator_T)
7733 public:
7734 bool m_UseMutex;
7735 uint32_t m_VulkanApiVersion;
7736 bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7737 bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7738 bool m_UseExtMemoryBudget;
7739 bool m_UseAmdDeviceCoherentMemory;
7740 bool m_UseKhrBufferDeviceAddress;
7741 VkDevice m_hDevice;
7742 VkInstance m_hInstance;
7743 bool m_AllocationCallbacksSpecified;
7744 VkAllocationCallbacks m_AllocationCallbacks;
7745 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7746 VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7747
7748 // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7749 uint32_t m_HeapSizeLimitMask;
7750
7751 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7752 VkPhysicalDeviceMemoryProperties m_MemProps;
7753
7754 // Default pools.
7755 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7756 // Reserved pools.
7757 VmaBlockVector* m_pReservedBlockVectors[VK_MAX_MEMORY_TYPES];
7758
7759 // Each vector is sorted by memory (handle value).
7760 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7761 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7762 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7763
7764 VmaCurrentBudgetData m_Budget;
7765
7766 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7767 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7768 ~VmaAllocator_T();
7769
GetAllocationCallbacksVmaAllocator_T7770 const VkAllocationCallbacks* GetAllocationCallbacks() const
7771 {
7772 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7773 }
GetVulkanFunctionsVmaAllocator_T7774 const VmaVulkanFunctions& GetVulkanFunctions() const
7775 {
7776 return m_VulkanFunctions;
7777 }
7778
GetPhysicalDeviceVmaAllocator_T7779 VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7780
GetBufferImageGranularityVmaAllocator_T7781 VkDeviceSize GetBufferImageGranularity() const
7782 {
7783 return VMA_MAX(
7784 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7785 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7786 }
7787
GetMemoryHeapCountVmaAllocator_T7788 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T7789 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7790
MemoryTypeIndexToHeapIndexVmaAllocator_T7791 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7792 {
7793 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7794 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7795 }
7796 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T7797 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7798 {
7799 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7800 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7801 }
7802 // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T7803 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7804 {
7805 return IsMemoryTypeNonCoherent(memTypeIndex) ?
7806 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7807 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7808 }
7809
IsIntegratedGpuVmaAllocator_T7810 bool IsIntegratedGpu() const
7811 {
7812 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7813 }
7814
GetGlobalMemoryTypeBitsVmaAllocator_T7815 uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7816
7817 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T7818 VmaRecorder* GetRecorder() const { return m_pRecorder; }
7819 #endif
7820
7821 void GetBufferMemoryRequirements(
7822 VkBuffer hBuffer,
7823 VkMemoryRequirements& memReq,
7824 bool& requiresDedicatedAllocation,
7825 bool& prefersDedicatedAllocation) const;
7826 void GetImageMemoryRequirements(
7827 VkImage hImage,
7828 VkMemoryRequirements& memReq,
7829 bool& requiresDedicatedAllocation,
7830 bool& prefersDedicatedAllocation) const;
7831
7832 // Main allocation function.
7833 VkResult AllocateMemory(
7834 const VkMemoryRequirements& vkMemReq,
7835 bool requiresDedicatedAllocation,
7836 bool prefersDedicatedAllocation,
7837 VkBuffer dedicatedBuffer,
7838 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7839 VkImage dedicatedImage,
7840 const VmaAllocationCreateInfo& createInfo,
7841 VmaSuballocationType suballocType,
7842 size_t allocationCount,
7843 VmaAllocation* pAllocations);
7844
7845 // Main deallocation function.
7846 void FreeMemory(
7847 size_t allocationCount,
7848 const VmaAllocation* pAllocations);
7849
7850 // OH ISSUE: VMA preAlloc
7851 // pre-allocation
7852 VkResult AllocateReservedMemory(
7853 const VkMemoryRequirements& vkMemReq,
7854 bool requiresDedicatedAllocation,
7855 bool prefersDedicatedAllocation,
7856 VkBuffer dedicatedBuffer,
7857 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7858 VkImage dedicatedImage,
7859 const VmaAllocationCreateInfo& createInfo,
7860 VmaSuballocationType suballocType,
7861 size_t allocationCount,
7862 VmaAllocation* pAllocations);
7863
7864 // pre-deallocation function.
7865 void FreeReservedMemory(
7866 size_t allocationCount,
7867 const VmaAllocation* pAllocations);
7868
7869 VkResult SwapReservedBlock(
7870 VkImage image,
7871 const VmaAllocationCreateInfo* pCreateInfo,
7872 VmaAllocation* pAllocation,
7873 VmaAllocationInfo* pAllocationInfo);
7874
7875 uint32_t GetPreAllocBlockSize();
7876
7877 VkResult ResizeAllocation(
7878 const VmaAllocation alloc,
7879 VkDeviceSize newSize);
7880
7881 void CalculateStats(VmaStats* pStats);
7882
7883 void FreeEmptyBlock();
7884
7885 void GetBudget(
7886 VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7887
7888 #if VMA_STATS_STRING_ENABLED
7889 void PrintDetailedMap(class VmaJsonWriter& json);
7890 #endif
7891
7892 VkResult DefragmentationBegin(
7893 const VmaDefragmentationInfo2& info,
7894 VmaDefragmentationStats* pStats,
7895 VmaDefragmentationContext* pContext);
7896 VkResult DefragmentationEnd(
7897 VmaDefragmentationContext context);
7898
7899 VkResult DefragmentationPassBegin(
7900 VmaDefragmentationPassInfo* pInfo,
7901 VmaDefragmentationContext context);
7902 VkResult DefragmentationPassEnd(
7903 VmaDefragmentationContext context);
7904
7905 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7906 bool TouchAllocation(VmaAllocation hAllocation);
7907
7908 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7909 void DestroyPool(VmaPool pool);
7910 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7911
7912 void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T7913 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7914
7915 void MakePoolAllocationsLost(
7916 VmaPool hPool,
7917 size_t* pLostAllocationCount);
7918 VkResult CheckPoolCorruption(VmaPool hPool);
7919 VkResult CheckCorruption(uint32_t memoryTypeBits);
7920
7921 void CreateLostAllocation(VmaAllocation* pAllocation);
7922
7923 // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7924 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7925 // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7926 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7927 // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7928 VkResult BindVulkanBuffer(
7929 VkDeviceMemory memory,
7930 VkDeviceSize memoryOffset,
7931 VkBuffer buffer,
7932 const void* pNext);
7933 // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7934 VkResult BindVulkanImage(
7935 VkDeviceMemory memory,
7936 VkDeviceSize memoryOffset,
7937 VkImage image,
7938 const void* pNext);
7939
7940 VkResult Map(VmaAllocation hAllocation, void** ppData);
7941 void Unmap(VmaAllocation hAllocation);
7942
7943 VkResult BindBufferMemory(
7944 VmaAllocation hAllocation,
7945 VkDeviceSize allocationLocalOffset,
7946 VkBuffer hBuffer,
7947 const void* pNext);
7948 VkResult BindImageMemory(
7949 VmaAllocation hAllocation,
7950 VkDeviceSize allocationLocalOffset,
7951 VkImage hImage,
7952 const void* pNext);
7953
7954 VkResult FlushOrInvalidateAllocation(
7955 VmaAllocation hAllocation,
7956 VkDeviceSize offset, VkDeviceSize size,
7957 VMA_CACHE_OPERATION op);
7958 VkResult FlushOrInvalidateAllocations(
7959 uint32_t allocationCount,
7960 const VmaAllocation* allocations,
7961 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7962 VMA_CACHE_OPERATION op);
7963
7964 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7965
7966 /*
7967 Returns bit mask of memory types that can support defragmentation on GPU as
7968 they support creation of required buffer for copy operations.
7969 */
7970 uint32_t GetGpuDefragmentationMemoryTypeBits();
7971
7972 private:
7973 VkDeviceSize m_PreferredLargeHeapBlockSize;
7974
7975 VkPhysicalDevice m_PhysicalDevice;
7976 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7977 VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7978
7979 VMA_RW_MUTEX m_PoolsMutex;
7980 // Protected by m_PoolsMutex. Sorted by pointer value.
7981 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7982 uint32_t m_NextPoolId;
7983
7984 VmaVulkanFunctions m_VulkanFunctions;
7985
7986 // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7987 uint32_t m_GlobalMemoryTypeBits;
7988
7989 #if VMA_RECORDING_ENABLED
7990 VmaRecorder* m_pRecorder;
7991 #endif
7992
7993 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7994
7995 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7996 void ImportVulkanFunctions_Static();
7997 #endif
7998
7999 void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8000
8001 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8002 void ImportVulkanFunctions_Dynamic();
8003 #endif
8004
8005 void ValidateVulkanFunctions();
8006
8007 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8008
8009 VkResult AllocateMemoryOfType(
8010 VkDeviceSize size,
8011 VkDeviceSize alignment,
8012 bool dedicatedAllocation,
8013 VkBuffer dedicatedBuffer,
8014 VkBufferUsageFlags dedicatedBufferUsage,
8015 VkImage dedicatedImage,
8016 const VmaAllocationCreateInfo& createInfo,
8017 uint32_t memTypeIndex,
8018 VmaSuballocationType suballocType,
8019 size_t allocationCount,
8020 VmaAllocation* pAllocations);
8021
8022 // Helper function only to be used inside AllocateDedicatedMemory.
8023 VkResult AllocateDedicatedMemoryPage(
8024 VkDeviceSize size,
8025 VmaSuballocationType suballocType,
8026 uint32_t memTypeIndex,
8027 const VkMemoryAllocateInfo& allocInfo,
8028 bool map,
8029 bool isUserDataString,
8030 void* pUserData,
8031 VmaAllocation* pAllocation);
8032
8033 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8034 VkResult AllocateDedicatedMemory(
8035 VkDeviceSize size,
8036 VmaSuballocationType suballocType,
8037 uint32_t memTypeIndex,
8038 bool withinBudget,
8039 bool map,
8040 bool isUserDataString,
8041 void* pUserData,
8042 VkBuffer dedicatedBuffer,
8043 VkBufferUsageFlags dedicatedBufferUsage,
8044 VkImage dedicatedImage,
8045 size_t allocationCount,
8046 VmaAllocation* pAllocations);
8047
8048 void FreeDedicatedMemory(const VmaAllocation allocation);
8049
8050 /*
8051 Calculates and returns bit mask of memory types that can support defragmentation
8052 on GPU as they support creation of required buffer for copy operations.
8053 */
8054 uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8055
8056 uint32_t CalculateGlobalMemoryTypeBits() const;
8057
8058 bool GetFlushOrInvalidateRange(
8059 VmaAllocation allocation,
8060 VkDeviceSize offset, VkDeviceSize size,
8061 VkMappedMemoryRange& outRange) const;
8062
8063 #if VMA_MEMORY_BUDGET
8064 void UpdateVulkanBudget();
8065 #endif // #if VMA_MEMORY_BUDGET
8066 };
8067
8068 ////////////////////////////////////////////////////////////////////////////////
8069 // Memory allocation #2 after VmaAllocator_T definition
8070
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)8071 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8072 {
8073 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8074 }
8075
VmaFree(VmaAllocator hAllocator,void * ptr)8076 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8077 {
8078 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8079 }
8080
8081 template<typename T>
VmaAllocate(VmaAllocator hAllocator)8082 static T* VmaAllocate(VmaAllocator hAllocator)
8083 {
8084 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8085 }
8086
8087 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)8088 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8089 {
8090 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8091 }
8092
8093 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)8094 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8095 {
8096 if(ptr != VMA_NULL)
8097 {
8098 ptr->~T();
8099 VmaFree(hAllocator, ptr);
8100 }
8101 }
8102
8103 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)8104 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8105 {
8106 if(ptr != VMA_NULL)
8107 {
8108 for(size_t i = count; i--; )
8109 ptr[i].~T();
8110 VmaFree(hAllocator, ptr);
8111 }
8112 }
8113
8114 ////////////////////////////////////////////////////////////////////////////////
8115 // VmaStringBuilder
8116
8117 #if VMA_STATS_STRING_ENABLED
8118
8119 class VmaStringBuilder
8120 {
8121 public:
VmaStringBuilder(VmaAllocator alloc)8122 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()8123 size_t GetLength() const { return m_Data.size(); }
GetData()8124 const char* GetData() const { return m_Data.data(); }
8125
Add(char ch)8126 void Add(char ch) { m_Data.push_back(ch); }
8127 void Add(const char* pStr);
AddNewLine()8128 void AddNewLine() { Add('\n'); }
8129 void AddNumber(uint32_t num);
8130 void AddNumber(uint64_t num);
8131 void AddPointer(const void* ptr);
8132
8133 private:
8134 VmaVector< char, VmaStlAllocator<char> > m_Data;
8135 };
8136
Add(const char * pStr)8137 void VmaStringBuilder::Add(const char* pStr)
8138 {
8139 const size_t strLen = strlen(pStr);
8140 if(strLen > 0)
8141 {
8142 const size_t oldCount = m_Data.size();
8143 m_Data.resize(oldCount + strLen);
8144 memcpy(m_Data.data() + oldCount, pStr, strLen);
8145 }
8146 }
8147
AddNumber(uint32_t num)8148 void VmaStringBuilder::AddNumber(uint32_t num)
8149 {
8150 char buf[11];
8151 buf[10] = '\0';
8152 char *p = &buf[10];
8153 do
8154 {
8155 *--p = '0' + (num % 10);
8156 num /= 10;
8157 }
8158 while(num);
8159 Add(p);
8160 }
8161
AddNumber(uint64_t num)8162 void VmaStringBuilder::AddNumber(uint64_t num)
8163 {
8164 char buf[21];
8165 buf[20] = '\0';
8166 char *p = &buf[20];
8167 do
8168 {
8169 *--p = '0' + (num % 10);
8170 num /= 10;
8171 }
8172 while(num);
8173 Add(p);
8174 }
8175
AddPointer(const void * ptr)8176 void VmaStringBuilder::AddPointer(const void* ptr)
8177 {
8178 char buf[21];
8179 VmaPtrToStr(buf, sizeof(buf), ptr);
8180 Add(buf);
8181 }
8182
8183 #endif // #if VMA_STATS_STRING_ENABLED
8184
8185 ////////////////////////////////////////////////////////////////////////////////
8186 // VmaJsonWriter
8187
8188 #if VMA_STATS_STRING_ENABLED
8189
8190 class VmaJsonWriter
8191 {
8192 VMA_CLASS_NO_COPY(VmaJsonWriter)
8193 public:
8194 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8195 ~VmaJsonWriter();
8196
8197 void BeginObject(bool singleLine = false);
8198 void EndObject();
8199
8200 void BeginArray(bool singleLine = false);
8201 void EndArray();
8202
8203 void WriteString(const char* pStr);
8204 void BeginString(const char* pStr = VMA_NULL);
8205 void ContinueString(const char* pStr);
8206 void ContinueString(uint32_t n);
8207 void ContinueString(uint64_t n);
8208 void ContinueString_Pointer(const void* ptr);
8209 void EndString(const char* pStr = VMA_NULL);
8210
8211 void WriteNumber(uint32_t n);
8212 void WriteNumber(uint64_t n);
8213 void WriteBool(bool b);
8214 void WriteNull();
8215
8216 private:
8217 static const char* const INDENT;
8218
8219 enum COLLECTION_TYPE
8220 {
8221 COLLECTION_TYPE_OBJECT,
8222 COLLECTION_TYPE_ARRAY,
8223 };
8224 struct StackItem
8225 {
8226 COLLECTION_TYPE type;
8227 uint32_t valueCount;
8228 bool singleLineMode;
8229 };
8230
8231 VmaStringBuilder& m_SB;
8232 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8233 bool m_InsideString;
8234
8235 void BeginValue(bool isString);
8236 void WriteIndent(bool oneLess = false);
8237 };
8238
8239 const char* const VmaJsonWriter::INDENT = " ";
8240
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)8241 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8242 m_SB(sb),
8243 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8244 m_InsideString(false)
8245 {
8246 }
8247
~VmaJsonWriter()8248 VmaJsonWriter::~VmaJsonWriter()
8249 {
8250 VMA_ASSERT(!m_InsideString);
8251 VMA_ASSERT(m_Stack.empty());
8252 }
8253
BeginObject(bool singleLine)8254 void VmaJsonWriter::BeginObject(bool singleLine)
8255 {
8256 VMA_ASSERT(!m_InsideString);
8257
8258 BeginValue(false);
8259 m_SB.Add('{');
8260
8261 StackItem item;
8262 item.type = COLLECTION_TYPE_OBJECT;
8263 item.valueCount = 0;
8264 item.singleLineMode = singleLine;
8265 m_Stack.push_back(item);
8266 }
8267
EndObject()8268 void VmaJsonWriter::EndObject()
8269 {
8270 VMA_ASSERT(!m_InsideString);
8271
8272 WriteIndent(true);
8273 m_SB.Add('}');
8274
8275 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8276 m_Stack.pop_back();
8277 }
8278
BeginArray(bool singleLine)8279 void VmaJsonWriter::BeginArray(bool singleLine)
8280 {
8281 VMA_ASSERT(!m_InsideString);
8282
8283 BeginValue(false);
8284 m_SB.Add('[');
8285
8286 StackItem item;
8287 item.type = COLLECTION_TYPE_ARRAY;
8288 item.valueCount = 0;
8289 item.singleLineMode = singleLine;
8290 m_Stack.push_back(item);
8291 }
8292
EndArray()8293 void VmaJsonWriter::EndArray()
8294 {
8295 VMA_ASSERT(!m_InsideString);
8296
8297 WriteIndent(true);
8298 m_SB.Add(']');
8299
8300 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8301 m_Stack.pop_back();
8302 }
8303
WriteString(const char * pStr)8304 void VmaJsonWriter::WriteString(const char* pStr)
8305 {
8306 BeginString(pStr);
8307 EndString();
8308 }
8309
BeginString(const char * pStr)8310 void VmaJsonWriter::BeginString(const char* pStr)
8311 {
8312 VMA_ASSERT(!m_InsideString);
8313
8314 BeginValue(true);
8315 m_SB.Add('"');
8316 m_InsideString = true;
8317 if(pStr != VMA_NULL && pStr[0] != '\0')
8318 {
8319 ContinueString(pStr);
8320 }
8321 }
8322
ContinueString(const char * pStr)8323 void VmaJsonWriter::ContinueString(const char* pStr)
8324 {
8325 VMA_ASSERT(m_InsideString);
8326
8327 const size_t strLen = strlen(pStr);
8328 for(size_t i = 0; i < strLen; ++i)
8329 {
8330 char ch = pStr[i];
8331 if(ch == '\\')
8332 {
8333 m_SB.Add("\\\\");
8334 }
8335 else if(ch == '"')
8336 {
8337 m_SB.Add("\\\"");
8338 }
8339 else if(ch >= 32)
8340 {
8341 m_SB.Add(ch);
8342 }
8343 else switch(ch)
8344 {
8345 case '\b':
8346 m_SB.Add("\\b");
8347 break;
8348 case '\f':
8349 m_SB.Add("\\f");
8350 break;
8351 case '\n':
8352 m_SB.Add("\\n");
8353 break;
8354 case '\r':
8355 m_SB.Add("\\r");
8356 break;
8357 case '\t':
8358 m_SB.Add("\\t");
8359 break;
8360 default:
8361 VMA_ASSERT(0 && "Character not currently supported.");
8362 break;
8363 }
8364 }
8365 }
8366
ContinueString(uint32_t n)8367 void VmaJsonWriter::ContinueString(uint32_t n)
8368 {
8369 VMA_ASSERT(m_InsideString);
8370 m_SB.AddNumber(n);
8371 }
8372
ContinueString(uint64_t n)8373 void VmaJsonWriter::ContinueString(uint64_t n)
8374 {
8375 VMA_ASSERT(m_InsideString);
8376 m_SB.AddNumber(n);
8377 }
8378
ContinueString_Pointer(const void * ptr)8379 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8380 {
8381 VMA_ASSERT(m_InsideString);
8382 m_SB.AddPointer(ptr);
8383 }
8384
EndString(const char * pStr)8385 void VmaJsonWriter::EndString(const char* pStr)
8386 {
8387 VMA_ASSERT(m_InsideString);
8388 if(pStr != VMA_NULL && pStr[0] != '\0')
8389 {
8390 ContinueString(pStr);
8391 }
8392 m_SB.Add('"');
8393 m_InsideString = false;
8394 }
8395
WriteNumber(uint32_t n)8396 void VmaJsonWriter::WriteNumber(uint32_t n)
8397 {
8398 VMA_ASSERT(!m_InsideString);
8399 BeginValue(false);
8400 m_SB.AddNumber(n);
8401 }
8402
WriteNumber(uint64_t n)8403 void VmaJsonWriter::WriteNumber(uint64_t n)
8404 {
8405 VMA_ASSERT(!m_InsideString);
8406 BeginValue(false);
8407 m_SB.AddNumber(n);
8408 }
8409
WriteBool(bool b)8410 void VmaJsonWriter::WriteBool(bool b)
8411 {
8412 VMA_ASSERT(!m_InsideString);
8413 BeginValue(false);
8414 m_SB.Add(b ? "true" : "false");
8415 }
8416
WriteNull()8417 void VmaJsonWriter::WriteNull()
8418 {
8419 VMA_ASSERT(!m_InsideString);
8420 BeginValue(false);
8421 m_SB.Add("null");
8422 }
8423
BeginValue(bool isString)8424 void VmaJsonWriter::BeginValue(bool isString)
8425 {
8426 if(!m_Stack.empty())
8427 {
8428 StackItem& currItem = m_Stack.back();
8429 if(currItem.type == COLLECTION_TYPE_OBJECT &&
8430 currItem.valueCount % 2 == 0)
8431 {
8432 VMA_ASSERT(isString);
8433 }
8434
8435 if(currItem.type == COLLECTION_TYPE_OBJECT &&
8436 currItem.valueCount % 2 != 0)
8437 {
8438 m_SB.Add(": ");
8439 }
8440 else if(currItem.valueCount > 0)
8441 {
8442 m_SB.Add(", ");
8443 WriteIndent();
8444 }
8445 else
8446 {
8447 WriteIndent();
8448 }
8449 ++currItem.valueCount;
8450 }
8451 }
8452
WriteIndent(bool oneLess)8453 void VmaJsonWriter::WriteIndent(bool oneLess)
8454 {
8455 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8456 {
8457 m_SB.AddNewLine();
8458
8459 size_t count = m_Stack.size();
8460 if(count > 0 && oneLess)
8461 {
8462 --count;
8463 }
8464 for(size_t i = 0; i < count; ++i)
8465 {
8466 m_SB.Add(INDENT);
8467 }
8468 }
8469 }
8470
8471 #endif // #if VMA_STATS_STRING_ENABLED
8472
8473 ////////////////////////////////////////////////////////////////////////////////
8474
SetUserData(VmaAllocator hAllocator,void * pUserData)8475 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8476 {
8477 if(IsUserDataString())
8478 {
8479 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8480
8481 FreeUserDataString(hAllocator);
8482
8483 if(pUserData != VMA_NULL)
8484 {
8485 m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8486 }
8487 }
8488 else
8489 {
8490 m_pUserData = pUserData;
8491 }
8492 }
8493
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)8494 void VmaAllocation_T::ChangeBlockAllocation(
8495 VmaAllocator hAllocator,
8496 VmaDeviceMemoryBlock* block,
8497 VkDeviceSize offset)
8498 {
8499 VMA_ASSERT(block != VMA_NULL);
8500 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8501
8502 // Move mapping reference counter from old block to new block.
8503 if(block != m_BlockAllocation.m_Block)
8504 {
8505 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8506 if(IsPersistentMap())
8507 ++mapRefCount;
8508 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8509 block->Map(hAllocator, mapRefCount, VMA_NULL);
8510 }
8511
8512 m_BlockAllocation.m_Block = block;
8513 m_BlockAllocation.m_Offset = offset;
8514 }
8515
ChangeOffset(VkDeviceSize newOffset)8516 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8517 {
8518 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8519 m_BlockAllocation.m_Offset = newOffset;
8520 }
8521
GetOffset()8522 VkDeviceSize VmaAllocation_T::GetOffset() const
8523 {
8524 switch(m_Type)
8525 {
8526 case ALLOCATION_TYPE_BLOCK:
8527 return m_BlockAllocation.m_Offset;
8528 case ALLOCATION_TYPE_DEDICATED:
8529 return 0;
8530 default:
8531 VMA_ASSERT(0);
8532 return 0;
8533 }
8534 }
8535
GetMemory()8536 VkDeviceMemory VmaAllocation_T::GetMemory() const
8537 {
8538 switch(m_Type)
8539 {
8540 case ALLOCATION_TYPE_BLOCK:
8541 return m_BlockAllocation.m_Block->GetDeviceMemory();
8542 case ALLOCATION_TYPE_DEDICATED:
8543 return m_DedicatedAllocation.m_hMemory;
8544 default:
8545 VMA_ASSERT(0);
8546 return VK_NULL_HANDLE;
8547 }
8548 }
8549
GetMappedData()8550 void* VmaAllocation_T::GetMappedData() const
8551 {
8552 switch(m_Type)
8553 {
8554 case ALLOCATION_TYPE_BLOCK:
8555 if(m_MapCount != 0)
8556 {
8557 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8558 VMA_ASSERT(pBlockData != VMA_NULL);
8559 return (char*)pBlockData + m_BlockAllocation.m_Offset;
8560 }
8561 else
8562 {
8563 return VMA_NULL;
8564 }
8565 break;
8566 case ALLOCATION_TYPE_DEDICATED:
8567 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8568 return m_DedicatedAllocation.m_pMappedData;
8569 default:
8570 VMA_ASSERT(0);
8571 return VMA_NULL;
8572 }
8573 }
8574
CanBecomeLost()8575 bool VmaAllocation_T::CanBecomeLost() const
8576 {
8577 switch(m_Type)
8578 {
8579 case ALLOCATION_TYPE_BLOCK:
8580 return m_BlockAllocation.m_CanBecomeLost;
8581 case ALLOCATION_TYPE_DEDICATED:
8582 return false;
8583 default:
8584 VMA_ASSERT(0);
8585 return false;
8586 }
8587 }
8588
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8589 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8590 {
8591 VMA_ASSERT(CanBecomeLost());
8592
8593 /*
8594 Warning: This is a carefully designed algorithm.
8595 Do not modify unless you really know what you're doing :)
8596 */
8597 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8598 for(;;)
8599 {
8600 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8601 {
8602 VMA_ASSERT(0);
8603 return false;
8604 }
8605 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8606 {
8607 return false;
8608 }
8609 else // Last use time earlier than current time.
8610 {
8611 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8612 {
8613 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8614 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8615 return true;
8616 }
8617 }
8618 }
8619 }
8620
8621 #if VMA_STATS_STRING_ENABLED
8622
8623 // Correspond to values of enum VmaSuballocationType.
8624 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8625 "FREE",
8626 "UNKNOWN",
8627 "BUFFER",
8628 "IMAGE_UNKNOWN",
8629 "IMAGE_LINEAR",
8630 "IMAGE_OPTIMAL",
8631 };
8632
PrintParameters(class VmaJsonWriter & json)8633 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8634 {
8635 json.WriteString("Type");
8636 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8637
8638 json.WriteString("Size");
8639 json.WriteNumber(m_Size);
8640
8641 if(m_pUserData != VMA_NULL)
8642 {
8643 json.WriteString("UserData");
8644 if(IsUserDataString())
8645 {
8646 json.WriteString((const char*)m_pUserData);
8647 }
8648 else
8649 {
8650 json.BeginString();
8651 json.ContinueString_Pointer(m_pUserData);
8652 json.EndString();
8653 }
8654 }
8655
8656 json.WriteString("CreationFrameIndex");
8657 json.WriteNumber(m_CreationFrameIndex);
8658
8659 json.WriteString("LastUseFrameIndex");
8660 json.WriteNumber(GetLastUseFrameIndex());
8661
8662 if(m_BufferImageUsage != 0)
8663 {
8664 json.WriteString("Usage");
8665 json.WriteNumber(m_BufferImageUsage);
8666 }
8667 }
8668
8669 #endif
8670
FreeUserDataString(VmaAllocator hAllocator)8671 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8672 {
8673 VMA_ASSERT(IsUserDataString());
8674 VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8675 m_pUserData = VMA_NULL;
8676 }
8677
BlockAllocMap()8678 void VmaAllocation_T::BlockAllocMap()
8679 {
8680 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8681
8682 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8683 {
8684 ++m_MapCount;
8685 }
8686 else
8687 {
8688 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8689 }
8690 }
8691
BlockAllocUnmap()8692 void VmaAllocation_T::BlockAllocUnmap()
8693 {
8694 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8695
8696 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8697 {
8698 --m_MapCount;
8699 }
8700 else
8701 {
8702 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8703 }
8704 }
8705
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)8706 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8707 {
8708 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8709
8710 if(m_MapCount != 0)
8711 {
8712 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8713 {
8714 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8715 *ppData = m_DedicatedAllocation.m_pMappedData;
8716 ++m_MapCount;
8717 return VK_SUCCESS;
8718 }
8719 else
8720 {
8721 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8722 return VK_ERROR_MEMORY_MAP_FAILED;
8723 }
8724 }
8725 else
8726 {
8727 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8728 hAllocator->m_hDevice,
8729 m_DedicatedAllocation.m_hMemory,
8730 0, // offset
8731 VK_WHOLE_SIZE,
8732 0, // flags
8733 ppData);
8734 if(result == VK_SUCCESS)
8735 {
8736 m_DedicatedAllocation.m_pMappedData = *ppData;
8737 m_MapCount = 1;
8738 }
8739 return result;
8740 }
8741 }
8742
DedicatedAllocUnmap(VmaAllocator hAllocator)8743 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8744 {
8745 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8746
8747 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8748 {
8749 --m_MapCount;
8750 if(m_MapCount == 0)
8751 {
8752 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8753 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8754 hAllocator->m_hDevice,
8755 m_DedicatedAllocation.m_hMemory);
8756 }
8757 }
8758 else
8759 {
8760 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8761 }
8762 }
8763
8764 #if VMA_STATS_STRING_ENABLED
8765
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)8766 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8767 {
8768 json.BeginObject();
8769
8770 json.WriteString("Blocks");
8771 json.WriteNumber(stat.blockCount);
8772
8773 json.WriteString("Allocations");
8774 json.WriteNumber(stat.allocationCount);
8775
8776 json.WriteString("UnusedRanges");
8777 json.WriteNumber(stat.unusedRangeCount);
8778
8779 json.WriteString("UsedBytes");
8780 json.WriteNumber(stat.usedBytes);
8781
8782 json.WriteString("UnusedBytes");
8783 json.WriteNumber(stat.unusedBytes);
8784
8785 if(stat.allocationCount > 1)
8786 {
8787 json.WriteString("AllocationSize");
8788 json.BeginObject(true);
8789 json.WriteString("Min");
8790 json.WriteNumber(stat.allocationSizeMin);
8791 json.WriteString("Avg");
8792 json.WriteNumber(stat.allocationSizeAvg);
8793 json.WriteString("Max");
8794 json.WriteNumber(stat.allocationSizeMax);
8795 json.EndObject();
8796 }
8797
8798 if(stat.unusedRangeCount > 1)
8799 {
8800 json.WriteString("UnusedRangeSize");
8801 json.BeginObject(true);
8802 json.WriteString("Min");
8803 json.WriteNumber(stat.unusedRangeSizeMin);
8804 json.WriteString("Avg");
8805 json.WriteNumber(stat.unusedRangeSizeAvg);
8806 json.WriteString("Max");
8807 json.WriteNumber(stat.unusedRangeSizeMax);
8808 json.EndObject();
8809 }
8810
8811 json.EndObject();
8812 }
8813
8814 #endif // #if VMA_STATS_STRING_ENABLED
8815
8816 struct VmaSuballocationItemSizeLess
8817 {
operatorVmaSuballocationItemSizeLess8818 bool operator()(
8819 const VmaSuballocationList::iterator lhs,
8820 const VmaSuballocationList::iterator rhs) const
8821 {
8822 return lhs->size < rhs->size;
8823 }
operatorVmaSuballocationItemSizeLess8824 bool operator()(
8825 const VmaSuballocationList::iterator lhs,
8826 VkDeviceSize rhsSize) const
8827 {
8828 return lhs->size < rhsSize;
8829 }
8830 };
8831
8832
8833 ////////////////////////////////////////////////////////////////////////////////
8834 // class VmaBlockMetadata
8835
VmaBlockMetadata(VmaAllocator hAllocator)8836 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8837 m_Size(0),
8838 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8839 {
8840 }
8841
8842 #if VMA_STATS_STRING_ENABLED
8843
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)8844 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8845 VkDeviceSize unusedBytes,
8846 size_t allocationCount,
8847 size_t unusedRangeCount) const
8848 {
8849 json.BeginObject();
8850
8851 json.WriteString("TotalBytes");
8852 json.WriteNumber(GetSize());
8853
8854 json.WriteString("UnusedBytes");
8855 json.WriteNumber(unusedBytes);
8856
8857 json.WriteString("Allocations");
8858 json.WriteNumber((uint64_t)allocationCount);
8859
8860 json.WriteString("UnusedRanges");
8861 json.WriteNumber((uint64_t)unusedRangeCount);
8862
8863 json.WriteString("Suballocations");
8864 json.BeginArray();
8865 }
8866
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)8867 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8868 VkDeviceSize offset,
8869 VmaAllocation hAllocation) const
8870 {
8871 json.BeginObject(true);
8872
8873 json.WriteString("Offset");
8874 json.WriteNumber(offset);
8875
8876 hAllocation->PrintParameters(json);
8877
8878 json.EndObject();
8879 }
8880
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)8881 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8882 VkDeviceSize offset,
8883 VkDeviceSize size) const
8884 {
8885 json.BeginObject(true);
8886
8887 json.WriteString("Offset");
8888 json.WriteNumber(offset);
8889
8890 json.WriteString("Type");
8891 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8892
8893 json.WriteString("Size");
8894 json.WriteNumber(size);
8895
8896 json.EndObject();
8897 }
8898
PrintDetailedMap_End(class VmaJsonWriter & json)8899 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8900 {
8901 json.EndArray();
8902 json.EndObject();
8903 }
8904
8905 #endif // #if VMA_STATS_STRING_ENABLED
8906
8907 ////////////////////////////////////////////////////////////////////////////////
8908 // class VmaBlockMetadata_Generic
8909
VmaBlockMetadata_Generic(VmaAllocator hAllocator)8910 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8911 VmaBlockMetadata(hAllocator),
8912 m_FreeCount(0),
8913 m_SumFreeSize(0),
8914 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8915 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8916 {
8917 }
8918
~VmaBlockMetadata_Generic()8919 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8920 {
8921 }
8922
Init(VkDeviceSize size)8923 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8924 {
8925 VmaBlockMetadata::Init(size);
8926
8927 m_FreeCount = 1;
8928 m_SumFreeSize = size;
8929
8930 VmaSuballocation suballoc = {};
8931 suballoc.offset = 0;
8932 suballoc.size = size;
8933 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8934 suballoc.hAllocation = VK_NULL_HANDLE;
8935
8936 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8937 m_Suballocations.push_back(suballoc);
8938 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8939 --suballocItem;
8940 m_FreeSuballocationsBySize.push_back(suballocItem);
8941 }
8942
Validate()8943 bool VmaBlockMetadata_Generic::Validate() const
8944 {
8945 VMA_VALIDATE(!m_Suballocations.empty());
8946
8947 // Expected offset of new suballocation as calculated from previous ones.
8948 VkDeviceSize calculatedOffset = 0;
8949 // Expected number of free suballocations as calculated from traversing their list.
8950 uint32_t calculatedFreeCount = 0;
8951 // Expected sum size of free suballocations as calculated from traversing their list.
8952 VkDeviceSize calculatedSumFreeSize = 0;
8953 // Expected number of free suballocations that should be registered in
8954 // m_FreeSuballocationsBySize calculated from traversing their list.
8955 size_t freeSuballocationsToRegister = 0;
8956 // True if previous visited suballocation was free.
8957 bool prevFree = false;
8958
8959 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8960 suballocItem != m_Suballocations.cend();
8961 ++suballocItem)
8962 {
8963 const VmaSuballocation& subAlloc = *suballocItem;
8964
8965 // Actual offset of this suballocation doesn't match expected one.
8966 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8967
8968 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8969 // Two adjacent free suballocations are invalid. They should be merged.
8970 VMA_VALIDATE(!prevFree || !currFree);
8971
8972 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8973
8974 if(currFree)
8975 {
8976 calculatedSumFreeSize += subAlloc.size;
8977 ++calculatedFreeCount;
8978 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8979 {
8980 ++freeSuballocationsToRegister;
8981 }
8982
8983 // Margin required between allocations - every free space must be at least that large.
8984 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8985 }
8986 else
8987 {
8988 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8989 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8990
8991 // Margin required between allocations - previous allocation must be free.
8992 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8993 }
8994
8995 calculatedOffset += subAlloc.size;
8996 prevFree = currFree;
8997 }
8998
8999 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9000 // match expected one.
9001 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9002
9003 VkDeviceSize lastSize = 0;
9004 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9005 {
9006 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9007
9008 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9009 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9010 // They must be sorted by size ascending.
9011 VMA_VALIDATE(suballocItem->size >= lastSize);
9012
9013 lastSize = suballocItem->size;
9014 }
9015
9016 // Check if totals match calculacted values.
9017 VMA_VALIDATE(ValidateFreeSuballocationList());
9018 VMA_VALIDATE(calculatedOffset == GetSize());
9019 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9020 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9021
9022 return true;
9023 }
9024
GetUnusedRangeSizeMax()9025 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9026 {
9027 if(!m_FreeSuballocationsBySize.empty())
9028 {
9029 return m_FreeSuballocationsBySize.back()->size;
9030 }
9031 else
9032 {
9033 return 0;
9034 }
9035 }
9036
IsEmpty()9037 bool VmaBlockMetadata_Generic::IsEmpty() const
9038 {
9039 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9040 }
9041
CalcAllocationStatInfo(VmaStatInfo & outInfo)9042 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9043 {
9044 outInfo.blockCount = 1;
9045
9046 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9047 outInfo.allocationCount = rangeCount - m_FreeCount;
9048 outInfo.unusedRangeCount = m_FreeCount;
9049
9050 outInfo.unusedBytes = m_SumFreeSize;
9051 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9052
9053 outInfo.allocationSizeMin = UINT64_MAX;
9054 outInfo.allocationSizeMax = 0;
9055 outInfo.unusedRangeSizeMin = UINT64_MAX;
9056 outInfo.unusedRangeSizeMax = 0;
9057
9058 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9059 suballocItem != m_Suballocations.cend();
9060 ++suballocItem)
9061 {
9062 const VmaSuballocation& suballoc = *suballocItem;
9063 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9064 {
9065 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9066 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9067 }
9068 else
9069 {
9070 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9071 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9072 }
9073 }
9074 }
9075
AddPoolStats(VmaPoolStats & inoutStats)9076 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9077 {
9078 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9079
9080 inoutStats.size += GetSize();
9081 inoutStats.unusedSize += m_SumFreeSize;
9082 inoutStats.allocationCount += rangeCount - m_FreeCount;
9083 inoutStats.unusedRangeCount += m_FreeCount;
9084 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9085 }
9086
9087 #if VMA_STATS_STRING_ENABLED
9088
PrintDetailedMap(class VmaJsonWriter & json)9089 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9090 {
9091 PrintDetailedMap_Begin(json,
9092 m_SumFreeSize, // unusedBytes
9093 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9094 m_FreeCount); // unusedRangeCount
9095
9096 size_t i = 0;
9097 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9098 suballocItem != m_Suballocations.cend();
9099 ++suballocItem, ++i)
9100 {
9101 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9102 {
9103 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9104 }
9105 else
9106 {
9107 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9108 }
9109 }
9110
9111 PrintDetailedMap_End(json);
9112 }
9113
9114 #endif // #if VMA_STATS_STRING_ENABLED
9115
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9116 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9117 uint32_t currentFrameIndex,
9118 uint32_t frameInUseCount,
9119 VkDeviceSize bufferImageGranularity,
9120 VkDeviceSize allocSize,
9121 VkDeviceSize allocAlignment,
9122 bool upperAddress,
9123 VmaSuballocationType allocType,
9124 bool canMakeOtherLost,
9125 uint32_t strategy,
9126 VmaAllocationRequest* pAllocationRequest)
9127 {
9128 VMA_ASSERT(allocSize > 0);
9129 VMA_ASSERT(!upperAddress);
9130 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9131 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9132 VMA_HEAVY_ASSERT(Validate());
9133
9134 pAllocationRequest->type = VmaAllocationRequestType::Normal;
9135
9136 // There is not enough total free space in this block to fullfill the request: Early return.
9137 if(canMakeOtherLost == false &&
9138 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9139 {
9140 return false;
9141 }
9142
9143 // New algorithm, efficiently searching freeSuballocationsBySize.
9144 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9145 if(freeSuballocCount > 0)
9146 {
9147 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9148 {
9149 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9150 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9151 m_FreeSuballocationsBySize.data(),
9152 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9153 allocSize + 2 * VMA_DEBUG_MARGIN,
9154 VmaSuballocationItemSizeLess());
9155 size_t index = it - m_FreeSuballocationsBySize.data();
9156 for(; index < freeSuballocCount; ++index)
9157 {
9158 if(CheckAllocation(
9159 currentFrameIndex,
9160 frameInUseCount,
9161 bufferImageGranularity,
9162 allocSize,
9163 allocAlignment,
9164 allocType,
9165 m_FreeSuballocationsBySize[index],
9166 false, // canMakeOtherLost
9167 &pAllocationRequest->offset,
9168 &pAllocationRequest->itemsToMakeLostCount,
9169 &pAllocationRequest->sumFreeSize,
9170 &pAllocationRequest->sumItemSize))
9171 {
9172 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9173 return true;
9174 }
9175 }
9176 }
9177 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9178 {
9179 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9180 it != m_Suballocations.end();
9181 ++it)
9182 {
9183 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9184 currentFrameIndex,
9185 frameInUseCount,
9186 bufferImageGranularity,
9187 allocSize,
9188 allocAlignment,
9189 allocType,
9190 it,
9191 false, // canMakeOtherLost
9192 &pAllocationRequest->offset,
9193 &pAllocationRequest->itemsToMakeLostCount,
9194 &pAllocationRequest->sumFreeSize,
9195 &pAllocationRequest->sumItemSize))
9196 {
9197 pAllocationRequest->item = it;
9198 return true;
9199 }
9200 }
9201 }
9202 else // WORST_FIT, FIRST_FIT
9203 {
9204 // Search staring from biggest suballocations.
9205 for(size_t index = freeSuballocCount; index--; )
9206 {
9207 if(CheckAllocation(
9208 currentFrameIndex,
9209 frameInUseCount,
9210 bufferImageGranularity,
9211 allocSize,
9212 allocAlignment,
9213 allocType,
9214 m_FreeSuballocationsBySize[index],
9215 false, // canMakeOtherLost
9216 &pAllocationRequest->offset,
9217 &pAllocationRequest->itemsToMakeLostCount,
9218 &pAllocationRequest->sumFreeSize,
9219 &pAllocationRequest->sumItemSize))
9220 {
9221 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9222 return true;
9223 }
9224 }
9225 }
9226 }
9227
9228 if(canMakeOtherLost)
9229 {
9230 // Brute-force algorithm. TODO: Come up with something better.
9231
9232 bool found = false;
9233 VmaAllocationRequest tmpAllocRequest = {};
9234 tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9235 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9236 suballocIt != m_Suballocations.end();
9237 ++suballocIt)
9238 {
9239 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9240 suballocIt->hAllocation->CanBecomeLost())
9241 {
9242 if(CheckAllocation(
9243 currentFrameIndex,
9244 frameInUseCount,
9245 bufferImageGranularity,
9246 allocSize,
9247 allocAlignment,
9248 allocType,
9249 suballocIt,
9250 canMakeOtherLost,
9251 &tmpAllocRequest.offset,
9252 &tmpAllocRequest.itemsToMakeLostCount,
9253 &tmpAllocRequest.sumFreeSize,
9254 &tmpAllocRequest.sumItemSize))
9255 {
9256 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9257 {
9258 *pAllocationRequest = tmpAllocRequest;
9259 pAllocationRequest->item = suballocIt;
9260 break;
9261 }
9262 if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9263 {
9264 *pAllocationRequest = tmpAllocRequest;
9265 pAllocationRequest->item = suballocIt;
9266 found = true;
9267 }
9268 }
9269 }
9270 }
9271
9272 return found;
9273 }
9274
9275 return false;
9276 }
9277
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)9278 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9279 uint32_t currentFrameIndex,
9280 uint32_t frameInUseCount,
9281 VmaAllocationRequest* pAllocationRequest)
9282 {
9283 VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9284
9285 while(pAllocationRequest->itemsToMakeLostCount > 0)
9286 {
9287 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9288 {
9289 ++pAllocationRequest->item;
9290 }
9291 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9292 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9293 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9294 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9295 {
9296 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9297 --pAllocationRequest->itemsToMakeLostCount;
9298 }
9299 else
9300 {
9301 return false;
9302 }
9303 }
9304
9305 VMA_HEAVY_ASSERT(Validate());
9306 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9307 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9308
9309 return true;
9310 }
9311
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)9312 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9313 {
9314 uint32_t lostAllocationCount = 0;
9315 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9316 it != m_Suballocations.end();
9317 ++it)
9318 {
9319 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9320 it->hAllocation->CanBecomeLost() &&
9321 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9322 {
9323 it = FreeSuballocation(it);
9324 ++lostAllocationCount;
9325 }
9326 }
9327 return lostAllocationCount;
9328 }
9329
CheckCorruption(const void * pBlockData)9330 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9331 {
9332 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9333 it != m_Suballocations.end();
9334 ++it)
9335 {
9336 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9337 {
9338 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9339 {
9340 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9341 return VK_ERROR_VALIDATION_FAILED_EXT;
9342 }
9343 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9344 {
9345 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9346 return VK_ERROR_VALIDATION_FAILED_EXT;
9347 }
9348 }
9349 }
9350
9351 return VK_SUCCESS;
9352 }
9353
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)9354 void VmaBlockMetadata_Generic::Alloc(
9355 const VmaAllocationRequest& request,
9356 VmaSuballocationType type,
9357 VkDeviceSize allocSize,
9358 VmaAllocation hAllocation)
9359 {
9360 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9361 VMA_ASSERT(request.item != m_Suballocations.end());
9362 VmaSuballocation& suballoc = *request.item;
9363 // Given suballocation is a free block.
9364 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9365 // Given offset is inside this suballocation.
9366 VMA_ASSERT(request.offset >= suballoc.offset);
9367 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9368 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9369 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9370
9371 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9372 // it to become used.
9373 UnregisterFreeSuballocation(request.item);
9374
9375 suballoc.offset = request.offset;
9376 suballoc.size = allocSize;
9377 suballoc.type = type;
9378 suballoc.hAllocation = hAllocation;
9379
9380 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9381 if(paddingEnd)
9382 {
9383 VmaSuballocation paddingSuballoc = {};
9384 paddingSuballoc.offset = request.offset + allocSize;
9385 paddingSuballoc.size = paddingEnd;
9386 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9387 VmaSuballocationList::iterator next = request.item;
9388 ++next;
9389 const VmaSuballocationList::iterator paddingEndItem =
9390 m_Suballocations.insert(next, paddingSuballoc);
9391 RegisterFreeSuballocation(paddingEndItem);
9392 }
9393
9394 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9395 if(paddingBegin)
9396 {
9397 VmaSuballocation paddingSuballoc = {};
9398 paddingSuballoc.offset = request.offset - paddingBegin;
9399 paddingSuballoc.size = paddingBegin;
9400 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9401 const VmaSuballocationList::iterator paddingBeginItem =
9402 m_Suballocations.insert(request.item, paddingSuballoc);
9403 RegisterFreeSuballocation(paddingBeginItem);
9404 }
9405
9406 // Update totals.
9407 m_FreeCount = m_FreeCount - 1;
9408 if(paddingBegin > 0)
9409 {
9410 ++m_FreeCount;
9411 }
9412 if(paddingEnd > 0)
9413 {
9414 ++m_FreeCount;
9415 }
9416 m_SumFreeSize -= allocSize;
9417 }
9418
Free(const VmaAllocation allocation)9419 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9420 {
9421 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9422 suballocItem != m_Suballocations.end();
9423 ++suballocItem)
9424 {
9425 VmaSuballocation& suballoc = *suballocItem;
9426 if(suballoc.hAllocation == allocation)
9427 {
9428 FreeSuballocation(suballocItem);
9429 VMA_HEAVY_ASSERT(Validate());
9430 return;
9431 }
9432 }
9433 VMA_ASSERT(0 && "Not found!");
9434 }
9435
FreeAtOffset(VkDeviceSize offset)9436 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9437 {
9438 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9439 suballocItem != m_Suballocations.end();
9440 ++suballocItem)
9441 {
9442 VmaSuballocation& suballoc = *suballocItem;
9443 if(suballoc.offset == offset)
9444 {
9445 FreeSuballocation(suballocItem);
9446 return;
9447 }
9448 }
9449 VMA_ASSERT(0 && "Not found!");
9450 }
9451
ValidateFreeSuballocationList()9452 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9453 {
9454 VkDeviceSize lastSize = 0;
9455 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9456 {
9457 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9458
9459 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9460 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9461 VMA_VALIDATE(it->size >= lastSize);
9462 lastSize = it->size;
9463 }
9464 return true;
9465 }
9466
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)9467 bool VmaBlockMetadata_Generic::CheckAllocation(
9468 uint32_t currentFrameIndex,
9469 uint32_t frameInUseCount,
9470 VkDeviceSize bufferImageGranularity,
9471 VkDeviceSize allocSize,
9472 VkDeviceSize allocAlignment,
9473 VmaSuballocationType allocType,
9474 VmaSuballocationList::const_iterator suballocItem,
9475 bool canMakeOtherLost,
9476 VkDeviceSize* pOffset,
9477 size_t* itemsToMakeLostCount,
9478 VkDeviceSize* pSumFreeSize,
9479 VkDeviceSize* pSumItemSize) const
9480 {
9481 VMA_ASSERT(allocSize > 0);
9482 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9483 VMA_ASSERT(suballocItem != m_Suballocations.cend());
9484 VMA_ASSERT(pOffset != VMA_NULL);
9485
9486 *itemsToMakeLostCount = 0;
9487 *pSumFreeSize = 0;
9488 *pSumItemSize = 0;
9489
9490 if(canMakeOtherLost)
9491 {
9492 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9493 {
9494 *pSumFreeSize = suballocItem->size;
9495 }
9496 else
9497 {
9498 if(suballocItem->hAllocation->CanBecomeLost() &&
9499 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9500 {
9501 ++*itemsToMakeLostCount;
9502 *pSumItemSize = suballocItem->size;
9503 }
9504 else
9505 {
9506 return false;
9507 }
9508 }
9509
9510 // Remaining size is too small for this request: Early return.
9511 if(GetSize() - suballocItem->offset < allocSize)
9512 {
9513 return false;
9514 }
9515
9516 // Start from offset equal to beginning of this suballocation.
9517 *pOffset = suballocItem->offset;
9518
9519 // Apply VMA_DEBUG_MARGIN at the beginning.
9520 if(VMA_DEBUG_MARGIN > 0)
9521 {
9522 *pOffset += VMA_DEBUG_MARGIN;
9523 }
9524
9525 // Apply alignment.
9526 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9527
9528 // Check previous suballocations for BufferImageGranularity conflicts.
9529 // Make bigger alignment if necessary.
9530 if(bufferImageGranularity > 1)
9531 {
9532 bool bufferImageGranularityConflict = false;
9533 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9534 while(prevSuballocItem != m_Suballocations.cbegin())
9535 {
9536 --prevSuballocItem;
9537 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9538 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9539 {
9540 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9541 {
9542 bufferImageGranularityConflict = true;
9543 break;
9544 }
9545 }
9546 else
9547 // Already on previous page.
9548 break;
9549 }
9550 if(bufferImageGranularityConflict)
9551 {
9552 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9553 }
9554 }
9555
9556 // Now that we have final *pOffset, check if we are past suballocItem.
9557 // If yes, return false - this function should be called for another suballocItem as starting point.
9558 if(*pOffset >= suballocItem->offset + suballocItem->size)
9559 {
9560 return false;
9561 }
9562
9563 // Calculate padding at the beginning based on current offset.
9564 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9565
9566 // Calculate required margin at the end.
9567 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9568
9569 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9570 // Another early return check.
9571 if(suballocItem->offset + totalSize > GetSize())
9572 {
9573 return false;
9574 }
9575
9576 // Advance lastSuballocItem until desired size is reached.
9577 // Update itemsToMakeLostCount.
9578 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9579 if(totalSize > suballocItem->size)
9580 {
9581 VkDeviceSize remainingSize = totalSize - suballocItem->size;
9582 while(remainingSize > 0)
9583 {
9584 ++lastSuballocItem;
9585 if(lastSuballocItem == m_Suballocations.cend())
9586 {
9587 return false;
9588 }
9589 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9590 {
9591 *pSumFreeSize += lastSuballocItem->size;
9592 }
9593 else
9594 {
9595 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9596 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9597 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9598 {
9599 ++*itemsToMakeLostCount;
9600 *pSumItemSize += lastSuballocItem->size;
9601 }
9602 else
9603 {
9604 return false;
9605 }
9606 }
9607 remainingSize = (lastSuballocItem->size < remainingSize) ?
9608 remainingSize - lastSuballocItem->size : 0;
9609 }
9610 }
9611
9612 // Check next suballocations for BufferImageGranularity conflicts.
9613 // If conflict exists, we must mark more allocations lost or fail.
9614 if(bufferImageGranularity > 1)
9615 {
9616 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9617 ++nextSuballocItem;
9618 while(nextSuballocItem != m_Suballocations.cend())
9619 {
9620 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9621 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9622 {
9623 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9624 {
9625 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9626 if(nextSuballoc.hAllocation->CanBecomeLost() &&
9627 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9628 {
9629 ++*itemsToMakeLostCount;
9630 }
9631 else
9632 {
9633 return false;
9634 }
9635 }
9636 }
9637 else
9638 {
9639 // Already on next page.
9640 break;
9641 }
9642 ++nextSuballocItem;
9643 }
9644 }
9645 }
9646 else
9647 {
9648 const VmaSuballocation& suballoc = *suballocItem;
9649 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9650
9651 *pSumFreeSize = suballoc.size;
9652
9653 // Size of this suballocation is too small for this request: Early return.
9654 if(suballoc.size < allocSize)
9655 {
9656 return false;
9657 }
9658
9659 // Start from offset equal to beginning of this suballocation.
9660 *pOffset = suballoc.offset;
9661
9662 // Apply VMA_DEBUG_MARGIN at the beginning.
9663 if(VMA_DEBUG_MARGIN > 0)
9664 {
9665 *pOffset += VMA_DEBUG_MARGIN;
9666 }
9667
9668 // Apply alignment.
9669 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9670
9671 // Check previous suballocations for BufferImageGranularity conflicts.
9672 // Make bigger alignment if necessary.
9673 if(bufferImageGranularity > 1)
9674 {
9675 bool bufferImageGranularityConflict = false;
9676 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9677 while(prevSuballocItem != m_Suballocations.cbegin())
9678 {
9679 --prevSuballocItem;
9680 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9681 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9682 {
9683 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9684 {
9685 bufferImageGranularityConflict = true;
9686 break;
9687 }
9688 }
9689 else
9690 // Already on previous page.
9691 break;
9692 }
9693 if(bufferImageGranularityConflict)
9694 {
9695 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9696 }
9697 }
9698
9699 // Calculate padding at the beginning based on current offset.
9700 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9701
9702 // Calculate required margin at the end.
9703 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9704
9705 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9706 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9707 {
9708 return false;
9709 }
9710
9711 // Check next suballocations for BufferImageGranularity conflicts.
9712 // If conflict exists, allocation cannot be made here.
9713 if(bufferImageGranularity > 1)
9714 {
9715 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9716 ++nextSuballocItem;
9717 while(nextSuballocItem != m_Suballocations.cend())
9718 {
9719 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9720 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9721 {
9722 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9723 {
9724 return false;
9725 }
9726 }
9727 else
9728 {
9729 // Already on next page.
9730 break;
9731 }
9732 ++nextSuballocItem;
9733 }
9734 }
9735 }
9736
9737 // All tests passed: Success. pOffset is already filled.
9738 return true;
9739 }
9740
MergeFreeWithNext(VmaSuballocationList::iterator item)9741 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9742 {
9743 VMA_ASSERT(item != m_Suballocations.end());
9744 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9745
9746 VmaSuballocationList::iterator nextItem = item;
9747 ++nextItem;
9748 VMA_ASSERT(nextItem != m_Suballocations.end());
9749 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9750
9751 item->size += nextItem->size;
9752 --m_FreeCount;
9753 m_Suballocations.erase(nextItem);
9754 }
9755
FreeSuballocation(VmaSuballocationList::iterator suballocItem)9756 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9757 {
9758 // Change this suballocation to be marked as free.
9759 VmaSuballocation& suballoc = *suballocItem;
9760 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9761 suballoc.hAllocation = VK_NULL_HANDLE;
9762
9763 // Update totals.
9764 ++m_FreeCount;
9765 m_SumFreeSize += suballoc.size;
9766
9767 // Merge with previous and/or next suballocation if it's also free.
9768 bool mergeWithNext = false;
9769 bool mergeWithPrev = false;
9770
9771 VmaSuballocationList::iterator nextItem = suballocItem;
9772 ++nextItem;
9773 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9774 {
9775 mergeWithNext = true;
9776 }
9777
9778 VmaSuballocationList::iterator prevItem = suballocItem;
9779 if(suballocItem != m_Suballocations.begin())
9780 {
9781 --prevItem;
9782 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9783 {
9784 mergeWithPrev = true;
9785 }
9786 }
9787
9788 if(mergeWithNext)
9789 {
9790 UnregisterFreeSuballocation(nextItem);
9791 MergeFreeWithNext(suballocItem);
9792 }
9793
9794 if(mergeWithPrev)
9795 {
9796 UnregisterFreeSuballocation(prevItem);
9797 MergeFreeWithNext(prevItem);
9798 RegisterFreeSuballocation(prevItem);
9799 return prevItem;
9800 }
9801 else
9802 {
9803 RegisterFreeSuballocation(suballocItem);
9804 return suballocItem;
9805 }
9806 }
9807
RegisterFreeSuballocation(VmaSuballocationList::iterator item)9808 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9809 {
9810 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9811 VMA_ASSERT(item->size > 0);
9812
9813 // You may want to enable this validation at the beginning or at the end of
9814 // this function, depending on what do you want to check.
9815 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9816
9817 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9818 {
9819 if(m_FreeSuballocationsBySize.empty())
9820 {
9821 m_FreeSuballocationsBySize.push_back(item);
9822 }
9823 else
9824 {
9825 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9826 }
9827 }
9828
9829 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9830 }
9831
9832
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)9833 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9834 {
9835 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9836 VMA_ASSERT(item->size > 0);
9837
9838 // You may want to enable this validation at the beginning or at the end of
9839 // this function, depending on what do you want to check.
9840 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9841
9842 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9843 {
9844 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9845 m_FreeSuballocationsBySize.data(),
9846 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9847 item,
9848 VmaSuballocationItemSizeLess());
9849 for(size_t index = it - m_FreeSuballocationsBySize.data();
9850 index < m_FreeSuballocationsBySize.size();
9851 ++index)
9852 {
9853 if(m_FreeSuballocationsBySize[index] == item)
9854 {
9855 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9856 return;
9857 }
9858 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9859 }
9860 VMA_ASSERT(0 && "Not found.");
9861 }
9862
9863 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9864 }
9865
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)9866 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9867 VkDeviceSize bufferImageGranularity,
9868 VmaSuballocationType& inOutPrevSuballocType) const
9869 {
9870 if(bufferImageGranularity == 1 || IsEmpty())
9871 {
9872 return false;
9873 }
9874
9875 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9876 bool typeConflictFound = false;
9877 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9878 it != m_Suballocations.cend();
9879 ++it)
9880 {
9881 const VmaSuballocationType suballocType = it->type;
9882 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9883 {
9884 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9885 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9886 {
9887 typeConflictFound = true;
9888 }
9889 inOutPrevSuballocType = suballocType;
9890 }
9891 }
9892
9893 return typeConflictFound || minAlignment >= bufferImageGranularity;
9894 }
9895
9896 ////////////////////////////////////////////////////////////////////////////////
9897 // class VmaBlockMetadata_Linear
9898
VmaBlockMetadata_Linear(VmaAllocator hAllocator)9899 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9900 VmaBlockMetadata(hAllocator),
9901 m_SumFreeSize(0),
9902 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9903 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9904 m_1stVectorIndex(0),
9905 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9906 m_1stNullItemsBeginCount(0),
9907 m_1stNullItemsMiddleCount(0),
9908 m_2ndNullItemsCount(0)
9909 {
9910 }
9911
~VmaBlockMetadata_Linear()9912 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9913 {
9914 }
9915
Init(VkDeviceSize size)9916 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9917 {
9918 VmaBlockMetadata::Init(size);
9919 m_SumFreeSize = size;
9920 }
9921
Validate()9922 bool VmaBlockMetadata_Linear::Validate() const
9923 {
9924 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9925 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9926
9927 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9928 VMA_VALIDATE(!suballocations1st.empty() ||
9929 suballocations2nd.empty() ||
9930 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9931
9932 if(!suballocations1st.empty())
9933 {
9934 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9935 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9936 // Null item at the end should be just pop_back().
9937 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9938 }
9939 if(!suballocations2nd.empty())
9940 {
9941 // Null item at the end should be just pop_back().
9942 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9943 }
9944
9945 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9946 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9947
9948 VkDeviceSize sumUsedSize = 0;
9949 const size_t suballoc1stCount = suballocations1st.size();
9950 VkDeviceSize offset = VMA_DEBUG_MARGIN;
9951
9952 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9953 {
9954 const size_t suballoc2ndCount = suballocations2nd.size();
9955 size_t nullItem2ndCount = 0;
9956 for(size_t i = 0; i < suballoc2ndCount; ++i)
9957 {
9958 const VmaSuballocation& suballoc = suballocations2nd[i];
9959 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9960
9961 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9962 VMA_VALIDATE(suballoc.offset >= offset);
9963
9964 if(!currFree)
9965 {
9966 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9967 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9968 sumUsedSize += suballoc.size;
9969 }
9970 else
9971 {
9972 ++nullItem2ndCount;
9973 }
9974
9975 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9976 }
9977
9978 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9979 }
9980
9981 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9982 {
9983 const VmaSuballocation& suballoc = suballocations1st[i];
9984 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9985 suballoc.hAllocation == VK_NULL_HANDLE);
9986 }
9987
9988 size_t nullItem1stCount = m_1stNullItemsBeginCount;
9989
9990 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9991 {
9992 const VmaSuballocation& suballoc = suballocations1st[i];
9993 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9994
9995 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9996 VMA_VALIDATE(suballoc.offset >= offset);
9997 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
9998
9999 if(!currFree)
10000 {
10001 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10002 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10003 sumUsedSize += suballoc.size;
10004 }
10005 else
10006 {
10007 ++nullItem1stCount;
10008 }
10009
10010 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10011 }
10012 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10013
10014 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10015 {
10016 const size_t suballoc2ndCount = suballocations2nd.size();
10017 size_t nullItem2ndCount = 0;
10018 for(size_t i = suballoc2ndCount; i--; )
10019 {
10020 const VmaSuballocation& suballoc = suballocations2nd[i];
10021 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10022
10023 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10024 VMA_VALIDATE(suballoc.offset >= offset);
10025
10026 if(!currFree)
10027 {
10028 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10029 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10030 sumUsedSize += suballoc.size;
10031 }
10032 else
10033 {
10034 ++nullItem2ndCount;
10035 }
10036
10037 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10038 }
10039
10040 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10041 }
10042
10043 VMA_VALIDATE(offset <= GetSize());
10044 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10045
10046 return true;
10047 }
10048
GetAllocationCount()10049 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10050 {
10051 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10052 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10053 }
10054
GetUnusedRangeSizeMax()10055 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10056 {
10057 const VkDeviceSize size = GetSize();
10058
10059 /*
10060 We don't consider gaps inside allocation vectors with freed allocations because
10061 they are not suitable for reuse in linear allocator. We consider only space that
10062 is available for new allocations.
10063 */
10064 if(IsEmpty())
10065 {
10066 return size;
10067 }
10068
10069 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10070
10071 switch(m_2ndVectorMode)
10072 {
10073 case SECOND_VECTOR_EMPTY:
10074 /*
10075 Available space is after end of 1st, as well as before beginning of 1st (which
10076 whould make it a ring buffer).
10077 */
10078 {
10079 const size_t suballocations1stCount = suballocations1st.size();
10080 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10081 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10082 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
10083 return VMA_MAX(
10084 firstSuballoc.offset,
10085 size - (lastSuballoc.offset + lastSuballoc.size));
10086 }
10087 break;
10088
10089 case SECOND_VECTOR_RING_BUFFER:
10090 /*
10091 Available space is only between end of 2nd and beginning of 1st.
10092 */
10093 {
10094 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10095 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10096 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10097 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10098 }
10099 break;
10100
10101 case SECOND_VECTOR_DOUBLE_STACK:
10102 /*
10103 Available space is only between end of 1st and top of 2nd.
10104 */
10105 {
10106 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10107 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10108 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10109 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10110 }
10111 break;
10112
10113 default:
10114 VMA_ASSERT(0);
10115 return 0;
10116 }
10117 }
10118
CalcAllocationStatInfo(VmaStatInfo & outInfo)10119 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10120 {
10121 const VkDeviceSize size = GetSize();
10122 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10123 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10124 const size_t suballoc1stCount = suballocations1st.size();
10125 const size_t suballoc2ndCount = suballocations2nd.size();
10126
10127 outInfo.blockCount = 1;
10128 outInfo.allocationCount = (uint32_t)GetAllocationCount();
10129 outInfo.unusedRangeCount = 0;
10130 outInfo.usedBytes = 0;
10131 outInfo.allocationSizeMin = UINT64_MAX;
10132 outInfo.allocationSizeMax = 0;
10133 outInfo.unusedRangeSizeMin = UINT64_MAX;
10134 outInfo.unusedRangeSizeMax = 0;
10135
10136 VkDeviceSize lastOffset = 0;
10137
10138 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10139 {
10140 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10141 size_t nextAlloc2ndIndex = 0;
10142 while(lastOffset < freeSpace2ndTo1stEnd)
10143 {
10144 // Find next non-null allocation or move nextAllocIndex to the end.
10145 while(nextAlloc2ndIndex < suballoc2ndCount &&
10146 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10147 {
10148 ++nextAlloc2ndIndex;
10149 }
10150
10151 // Found non-null allocation.
10152 if(nextAlloc2ndIndex < suballoc2ndCount)
10153 {
10154 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10155
10156 // 1. Process free space before this allocation.
10157 if(lastOffset < suballoc.offset)
10158 {
10159 // There is free space from lastOffset to suballoc.offset.
10160 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10161 ++outInfo.unusedRangeCount;
10162 outInfo.unusedBytes += unusedRangeSize;
10163 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10164 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10165 }
10166
10167 // 2. Process this allocation.
10168 // There is allocation with suballoc.offset, suballoc.size.
10169 outInfo.usedBytes += suballoc.size;
10170 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10171 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10172
10173 // 3. Prepare for next iteration.
10174 lastOffset = suballoc.offset + suballoc.size;
10175 ++nextAlloc2ndIndex;
10176 }
10177 // We are at the end.
10178 else
10179 {
10180 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10181 if(lastOffset < freeSpace2ndTo1stEnd)
10182 {
10183 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10184 ++outInfo.unusedRangeCount;
10185 outInfo.unusedBytes += unusedRangeSize;
10186 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10187 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10188 }
10189
10190 // End of loop.
10191 lastOffset = freeSpace2ndTo1stEnd;
10192 }
10193 }
10194 }
10195
10196 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10197 const VkDeviceSize freeSpace1stTo2ndEnd =
10198 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10199 while(lastOffset < freeSpace1stTo2ndEnd)
10200 {
10201 // Find next non-null allocation or move nextAllocIndex to the end.
10202 while(nextAlloc1stIndex < suballoc1stCount &&
10203 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10204 {
10205 ++nextAlloc1stIndex;
10206 }
10207
10208 // Found non-null allocation.
10209 if(nextAlloc1stIndex < suballoc1stCount)
10210 {
10211 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10212
10213 // 1. Process free space before this allocation.
10214 if(lastOffset < suballoc.offset)
10215 {
10216 // There is free space from lastOffset to suballoc.offset.
10217 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10218 ++outInfo.unusedRangeCount;
10219 outInfo.unusedBytes += unusedRangeSize;
10220 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10221 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10222 }
10223
10224 // 2. Process this allocation.
10225 // There is allocation with suballoc.offset, suballoc.size.
10226 outInfo.usedBytes += suballoc.size;
10227 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10228 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10229
10230 // 3. Prepare for next iteration.
10231 lastOffset = suballoc.offset + suballoc.size;
10232 ++nextAlloc1stIndex;
10233 }
10234 // We are at the end.
10235 else
10236 {
10237 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10238 if(lastOffset < freeSpace1stTo2ndEnd)
10239 {
10240 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10241 ++outInfo.unusedRangeCount;
10242 outInfo.unusedBytes += unusedRangeSize;
10243 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10244 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10245 }
10246
10247 // End of loop.
10248 lastOffset = freeSpace1stTo2ndEnd;
10249 }
10250 }
10251
10252 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10253 {
10254 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10255 while(lastOffset < size)
10256 {
10257 // Find next non-null allocation or move nextAllocIndex to the end.
10258 while(nextAlloc2ndIndex != SIZE_MAX &&
10259 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10260 {
10261 --nextAlloc2ndIndex;
10262 }
10263
10264 // Found non-null allocation.
10265 if(nextAlloc2ndIndex != SIZE_MAX)
10266 {
10267 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10268
10269 // 1. Process free space before this allocation.
10270 if(lastOffset < suballoc.offset)
10271 {
10272 // There is free space from lastOffset to suballoc.offset.
10273 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10274 ++outInfo.unusedRangeCount;
10275 outInfo.unusedBytes += unusedRangeSize;
10276 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10277 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10278 }
10279
10280 // 2. Process this allocation.
10281 // There is allocation with suballoc.offset, suballoc.size.
10282 outInfo.usedBytes += suballoc.size;
10283 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10284 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10285
10286 // 3. Prepare for next iteration.
10287 lastOffset = suballoc.offset + suballoc.size;
10288 --nextAlloc2ndIndex;
10289 }
10290 // We are at the end.
10291 else
10292 {
10293 // There is free space from lastOffset to size.
10294 if(lastOffset < size)
10295 {
10296 const VkDeviceSize unusedRangeSize = size - lastOffset;
10297 ++outInfo.unusedRangeCount;
10298 outInfo.unusedBytes += unusedRangeSize;
10299 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10300 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10301 }
10302
10303 // End of loop.
10304 lastOffset = size;
10305 }
10306 }
10307 }
10308
10309 outInfo.unusedBytes = size - outInfo.usedBytes;
10310 }
10311
AddPoolStats(VmaPoolStats & inoutStats)10312 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10313 {
10314 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10315 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10316 const VkDeviceSize size = GetSize();
10317 const size_t suballoc1stCount = suballocations1st.size();
10318 const size_t suballoc2ndCount = suballocations2nd.size();
10319
10320 inoutStats.size += size;
10321
10322 VkDeviceSize lastOffset = 0;
10323
10324 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10325 {
10326 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10327 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10328 while(lastOffset < freeSpace2ndTo1stEnd)
10329 {
10330 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10331 while(nextAlloc2ndIndex < suballoc2ndCount &&
10332 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10333 {
10334 ++nextAlloc2ndIndex;
10335 }
10336
10337 // Found non-null allocation.
10338 if(nextAlloc2ndIndex < suballoc2ndCount)
10339 {
10340 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10341
10342 // 1. Process free space before this allocation.
10343 if(lastOffset < suballoc.offset)
10344 {
10345 // There is free space from lastOffset to suballoc.offset.
10346 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10347 inoutStats.unusedSize += unusedRangeSize;
10348 ++inoutStats.unusedRangeCount;
10349 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10350 }
10351
10352 // 2. Process this allocation.
10353 // There is allocation with suballoc.offset, suballoc.size.
10354 ++inoutStats.allocationCount;
10355
10356 // 3. Prepare for next iteration.
10357 lastOffset = suballoc.offset + suballoc.size;
10358 ++nextAlloc2ndIndex;
10359 }
10360 // We are at the end.
10361 else
10362 {
10363 if(lastOffset < freeSpace2ndTo1stEnd)
10364 {
10365 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10366 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10367 inoutStats.unusedSize += unusedRangeSize;
10368 ++inoutStats.unusedRangeCount;
10369 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10370 }
10371
10372 // End of loop.
10373 lastOffset = freeSpace2ndTo1stEnd;
10374 }
10375 }
10376 }
10377
10378 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10379 const VkDeviceSize freeSpace1stTo2ndEnd =
10380 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10381 while(lastOffset < freeSpace1stTo2ndEnd)
10382 {
10383 // Find next non-null allocation or move nextAllocIndex to the end.
10384 while(nextAlloc1stIndex < suballoc1stCount &&
10385 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10386 {
10387 ++nextAlloc1stIndex;
10388 }
10389
10390 // Found non-null allocation.
10391 if(nextAlloc1stIndex < suballoc1stCount)
10392 {
10393 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10394
10395 // 1. Process free space before this allocation.
10396 if(lastOffset < suballoc.offset)
10397 {
10398 // There is free space from lastOffset to suballoc.offset.
10399 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10400 inoutStats.unusedSize += unusedRangeSize;
10401 ++inoutStats.unusedRangeCount;
10402 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10403 }
10404
10405 // 2. Process this allocation.
10406 // There is allocation with suballoc.offset, suballoc.size.
10407 ++inoutStats.allocationCount;
10408
10409 // 3. Prepare for next iteration.
10410 lastOffset = suballoc.offset + suballoc.size;
10411 ++nextAlloc1stIndex;
10412 }
10413 // We are at the end.
10414 else
10415 {
10416 if(lastOffset < freeSpace1stTo2ndEnd)
10417 {
10418 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10419 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10420 inoutStats.unusedSize += unusedRangeSize;
10421 ++inoutStats.unusedRangeCount;
10422 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10423 }
10424
10425 // End of loop.
10426 lastOffset = freeSpace1stTo2ndEnd;
10427 }
10428 }
10429
10430 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10431 {
10432 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10433 while(lastOffset < size)
10434 {
10435 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10436 while(nextAlloc2ndIndex != SIZE_MAX &&
10437 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10438 {
10439 --nextAlloc2ndIndex;
10440 }
10441
10442 // Found non-null allocation.
10443 if(nextAlloc2ndIndex != SIZE_MAX)
10444 {
10445 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10446
10447 // 1. Process free space before this allocation.
10448 if(lastOffset < suballoc.offset)
10449 {
10450 // There is free space from lastOffset to suballoc.offset.
10451 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10452 inoutStats.unusedSize += unusedRangeSize;
10453 ++inoutStats.unusedRangeCount;
10454 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10455 }
10456
10457 // 2. Process this allocation.
10458 // There is allocation with suballoc.offset, suballoc.size.
10459 ++inoutStats.allocationCount;
10460
10461 // 3. Prepare for next iteration.
10462 lastOffset = suballoc.offset + suballoc.size;
10463 --nextAlloc2ndIndex;
10464 }
10465 // We are at the end.
10466 else
10467 {
10468 if(lastOffset < size)
10469 {
10470 // There is free space from lastOffset to size.
10471 const VkDeviceSize unusedRangeSize = size - lastOffset;
10472 inoutStats.unusedSize += unusedRangeSize;
10473 ++inoutStats.unusedRangeCount;
10474 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10475 }
10476
10477 // End of loop.
10478 lastOffset = size;
10479 }
10480 }
10481 }
10482 }
10483
10484 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10485 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10486 {
10487 const VkDeviceSize size = GetSize();
10488 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10489 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10490 const size_t suballoc1stCount = suballocations1st.size();
10491 const size_t suballoc2ndCount = suballocations2nd.size();
10492
10493 // FIRST PASS
10494
10495 size_t unusedRangeCount = 0;
10496 VkDeviceSize usedBytes = 0;
10497
10498 VkDeviceSize lastOffset = 0;
10499
10500 size_t alloc2ndCount = 0;
10501 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10502 {
10503 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10504 size_t nextAlloc2ndIndex = 0;
10505 while(lastOffset < freeSpace2ndTo1stEnd)
10506 {
10507 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10508 while(nextAlloc2ndIndex < suballoc2ndCount &&
10509 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10510 {
10511 ++nextAlloc2ndIndex;
10512 }
10513
10514 // Found non-null allocation.
10515 if(nextAlloc2ndIndex < suballoc2ndCount)
10516 {
10517 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10518
10519 // 1. Process free space before this allocation.
10520 if(lastOffset < suballoc.offset)
10521 {
10522 // There is free space from lastOffset to suballoc.offset.
10523 ++unusedRangeCount;
10524 }
10525
10526 // 2. Process this allocation.
10527 // There is allocation with suballoc.offset, suballoc.size.
10528 ++alloc2ndCount;
10529 usedBytes += suballoc.size;
10530
10531 // 3. Prepare for next iteration.
10532 lastOffset = suballoc.offset + suballoc.size;
10533 ++nextAlloc2ndIndex;
10534 }
10535 // We are at the end.
10536 else
10537 {
10538 if(lastOffset < freeSpace2ndTo1stEnd)
10539 {
10540 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10541 ++unusedRangeCount;
10542 }
10543
10544 // End of loop.
10545 lastOffset = freeSpace2ndTo1stEnd;
10546 }
10547 }
10548 }
10549
10550 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10551 size_t alloc1stCount = 0;
10552 const VkDeviceSize freeSpace1stTo2ndEnd =
10553 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10554 while(lastOffset < freeSpace1stTo2ndEnd)
10555 {
10556 // Find next non-null allocation or move nextAllocIndex to the end.
10557 while(nextAlloc1stIndex < suballoc1stCount &&
10558 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10559 {
10560 ++nextAlloc1stIndex;
10561 }
10562
10563 // Found non-null allocation.
10564 if(nextAlloc1stIndex < suballoc1stCount)
10565 {
10566 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10567
10568 // 1. Process free space before this allocation.
10569 if(lastOffset < suballoc.offset)
10570 {
10571 // There is free space from lastOffset to suballoc.offset.
10572 ++unusedRangeCount;
10573 }
10574
10575 // 2. Process this allocation.
10576 // There is allocation with suballoc.offset, suballoc.size.
10577 ++alloc1stCount;
10578 usedBytes += suballoc.size;
10579
10580 // 3. Prepare for next iteration.
10581 lastOffset = suballoc.offset + suballoc.size;
10582 ++nextAlloc1stIndex;
10583 }
10584 // We are at the end.
10585 else
10586 {
10587 if(lastOffset < size)
10588 {
10589 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10590 ++unusedRangeCount;
10591 }
10592
10593 // End of loop.
10594 lastOffset = freeSpace1stTo2ndEnd;
10595 }
10596 }
10597
10598 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10599 {
10600 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10601 while(lastOffset < size)
10602 {
10603 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10604 while(nextAlloc2ndIndex != SIZE_MAX &&
10605 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10606 {
10607 --nextAlloc2ndIndex;
10608 }
10609
10610 // Found non-null allocation.
10611 if(nextAlloc2ndIndex != SIZE_MAX)
10612 {
10613 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10614
10615 // 1. Process free space before this allocation.
10616 if(lastOffset < suballoc.offset)
10617 {
10618 // There is free space from lastOffset to suballoc.offset.
10619 ++unusedRangeCount;
10620 }
10621
10622 // 2. Process this allocation.
10623 // There is allocation with suballoc.offset, suballoc.size.
10624 ++alloc2ndCount;
10625 usedBytes += suballoc.size;
10626
10627 // 3. Prepare for next iteration.
10628 lastOffset = suballoc.offset + suballoc.size;
10629 --nextAlloc2ndIndex;
10630 }
10631 // We are at the end.
10632 else
10633 {
10634 if(lastOffset < size)
10635 {
10636 // There is free space from lastOffset to size.
10637 ++unusedRangeCount;
10638 }
10639
10640 // End of loop.
10641 lastOffset = size;
10642 }
10643 }
10644 }
10645
10646 const VkDeviceSize unusedBytes = size - usedBytes;
10647 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10648
10649 // SECOND PASS
10650 lastOffset = 0;
10651
10652 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10653 {
10654 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10655 size_t nextAlloc2ndIndex = 0;
10656 while(lastOffset < freeSpace2ndTo1stEnd)
10657 {
10658 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10659 while(nextAlloc2ndIndex < suballoc2ndCount &&
10660 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10661 {
10662 ++nextAlloc2ndIndex;
10663 }
10664
10665 // Found non-null allocation.
10666 if(nextAlloc2ndIndex < suballoc2ndCount)
10667 {
10668 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10669
10670 // 1. Process free space before this allocation.
10671 if(lastOffset < suballoc.offset)
10672 {
10673 // There is free space from lastOffset to suballoc.offset.
10674 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10675 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10676 }
10677
10678 // 2. Process this allocation.
10679 // There is allocation with suballoc.offset, suballoc.size.
10680 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10681
10682 // 3. Prepare for next iteration.
10683 lastOffset = suballoc.offset + suballoc.size;
10684 ++nextAlloc2ndIndex;
10685 }
10686 // We are at the end.
10687 else
10688 {
10689 if(lastOffset < freeSpace2ndTo1stEnd)
10690 {
10691 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10692 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10693 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10694 }
10695
10696 // End of loop.
10697 lastOffset = freeSpace2ndTo1stEnd;
10698 }
10699 }
10700 }
10701
10702 nextAlloc1stIndex = m_1stNullItemsBeginCount;
10703 while(lastOffset < freeSpace1stTo2ndEnd)
10704 {
10705 // Find next non-null allocation or move nextAllocIndex to the end.
10706 while(nextAlloc1stIndex < suballoc1stCount &&
10707 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10708 {
10709 ++nextAlloc1stIndex;
10710 }
10711
10712 // Found non-null allocation.
10713 if(nextAlloc1stIndex < suballoc1stCount)
10714 {
10715 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10716
10717 // 1. Process free space before this allocation.
10718 if(lastOffset < suballoc.offset)
10719 {
10720 // There is free space from lastOffset to suballoc.offset.
10721 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10722 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10723 }
10724
10725 // 2. Process this allocation.
10726 // There is allocation with suballoc.offset, suballoc.size.
10727 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10728
10729 // 3. Prepare for next iteration.
10730 lastOffset = suballoc.offset + suballoc.size;
10731 ++nextAlloc1stIndex;
10732 }
10733 // We are at the end.
10734 else
10735 {
10736 if(lastOffset < freeSpace1stTo2ndEnd)
10737 {
10738 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10739 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10740 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10741 }
10742
10743 // End of loop.
10744 lastOffset = freeSpace1stTo2ndEnd;
10745 }
10746 }
10747
10748 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10749 {
10750 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10751 while(lastOffset < size)
10752 {
10753 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10754 while(nextAlloc2ndIndex != SIZE_MAX &&
10755 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10756 {
10757 --nextAlloc2ndIndex;
10758 }
10759
10760 // Found non-null allocation.
10761 if(nextAlloc2ndIndex != SIZE_MAX)
10762 {
10763 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10764
10765 // 1. Process free space before this allocation.
10766 if(lastOffset < suballoc.offset)
10767 {
10768 // There is free space from lastOffset to suballoc.offset.
10769 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10770 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10771 }
10772
10773 // 2. Process this allocation.
10774 // There is allocation with suballoc.offset, suballoc.size.
10775 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10776
10777 // 3. Prepare for next iteration.
10778 lastOffset = suballoc.offset + suballoc.size;
10779 --nextAlloc2ndIndex;
10780 }
10781 // We are at the end.
10782 else
10783 {
10784 if(lastOffset < size)
10785 {
10786 // There is free space from lastOffset to size.
10787 const VkDeviceSize unusedRangeSize = size - lastOffset;
10788 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10789 }
10790
10791 // End of loop.
10792 lastOffset = size;
10793 }
10794 }
10795 }
10796
10797 PrintDetailedMap_End(json);
10798 }
10799 #endif // #if VMA_STATS_STRING_ENABLED
10800
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10801 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10802 uint32_t currentFrameIndex,
10803 uint32_t frameInUseCount,
10804 VkDeviceSize bufferImageGranularity,
10805 VkDeviceSize allocSize,
10806 VkDeviceSize allocAlignment,
10807 bool upperAddress,
10808 VmaSuballocationType allocType,
10809 bool canMakeOtherLost,
10810 uint32_t strategy,
10811 VmaAllocationRequest* pAllocationRequest)
10812 {
10813 VMA_ASSERT(allocSize > 0);
10814 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10815 VMA_ASSERT(pAllocationRequest != VMA_NULL);
10816 VMA_HEAVY_ASSERT(Validate());
10817 return upperAddress ?
10818 CreateAllocationRequest_UpperAddress(
10819 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10820 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10821 CreateAllocationRequest_LowerAddress(
10822 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10823 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10824 }
10825
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10826 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10827 uint32_t currentFrameIndex,
10828 uint32_t frameInUseCount,
10829 VkDeviceSize bufferImageGranularity,
10830 VkDeviceSize allocSize,
10831 VkDeviceSize allocAlignment,
10832 VmaSuballocationType allocType,
10833 bool canMakeOtherLost,
10834 uint32_t strategy,
10835 VmaAllocationRequest* pAllocationRequest)
10836 {
10837 const VkDeviceSize size = GetSize();
10838 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10839 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10840
10841 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10842 {
10843 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10844 return false;
10845 }
10846
10847 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10848 if(allocSize > size)
10849 {
10850 return false;
10851 }
10852 VkDeviceSize resultBaseOffset = size - allocSize;
10853 if(!suballocations2nd.empty())
10854 {
10855 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10856 resultBaseOffset = lastSuballoc.offset - allocSize;
10857 if(allocSize > lastSuballoc.offset)
10858 {
10859 return false;
10860 }
10861 }
10862
10863 // Start from offset equal to end of free space.
10864 VkDeviceSize resultOffset = resultBaseOffset;
10865
10866 // Apply VMA_DEBUG_MARGIN at the end.
10867 if(VMA_DEBUG_MARGIN > 0)
10868 {
10869 if(resultOffset < VMA_DEBUG_MARGIN)
10870 {
10871 return false;
10872 }
10873 resultOffset -= VMA_DEBUG_MARGIN;
10874 }
10875
10876 // Apply alignment.
10877 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10878
10879 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10880 // Make bigger alignment if necessary.
10881 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10882 {
10883 bool bufferImageGranularityConflict = false;
10884 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10885 {
10886 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10887 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10888 {
10889 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10890 {
10891 bufferImageGranularityConflict = true;
10892 break;
10893 }
10894 }
10895 else
10896 // Already on previous page.
10897 break;
10898 }
10899 if(bufferImageGranularityConflict)
10900 {
10901 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10902 }
10903 }
10904
10905 // There is enough free space.
10906 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10907 suballocations1st.back().offset + suballocations1st.back().size :
10908 0;
10909 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10910 {
10911 // Check previous suballocations for BufferImageGranularity conflicts.
10912 // If conflict exists, allocation cannot be made here.
10913 if(bufferImageGranularity > 1)
10914 {
10915 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10916 {
10917 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10918 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10919 {
10920 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10921 {
10922 return false;
10923 }
10924 }
10925 else
10926 {
10927 // Already on next page.
10928 break;
10929 }
10930 }
10931 }
10932
10933 // All tests passed: Success.
10934 pAllocationRequest->offset = resultOffset;
10935 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10936 pAllocationRequest->sumItemSize = 0;
10937 // pAllocationRequest->item unused.
10938 pAllocationRequest->itemsToMakeLostCount = 0;
10939 pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10940 return true;
10941 }
10942
10943 return false;
10944 }
10945
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10946 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10947 uint32_t currentFrameIndex,
10948 uint32_t frameInUseCount,
10949 VkDeviceSize bufferImageGranularity,
10950 VkDeviceSize allocSize,
10951 VkDeviceSize allocAlignment,
10952 VmaSuballocationType allocType,
10953 bool canMakeOtherLost,
10954 uint32_t strategy,
10955 VmaAllocationRequest* pAllocationRequest)
10956 {
10957 const VkDeviceSize size = GetSize();
10958 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10959 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10960
10961 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10962 {
10963 // Try to allocate at the end of 1st vector.
10964
10965 VkDeviceSize resultBaseOffset = 0;
10966 if(!suballocations1st.empty())
10967 {
10968 const VmaSuballocation& lastSuballoc = suballocations1st.back();
10969 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10970 }
10971
10972 // Start from offset equal to beginning of free space.
10973 VkDeviceSize resultOffset = resultBaseOffset;
10974
10975 // Apply VMA_DEBUG_MARGIN at the beginning.
10976 if(VMA_DEBUG_MARGIN > 0)
10977 {
10978 resultOffset += VMA_DEBUG_MARGIN;
10979 }
10980
10981 // Apply alignment.
10982 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10983
10984 // Check previous suballocations for BufferImageGranularity conflicts.
10985 // Make bigger alignment if necessary.
10986 if(bufferImageGranularity > 1 && !suballocations1st.empty())
10987 {
10988 bool bufferImageGranularityConflict = false;
10989 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10990 {
10991 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10992 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10993 {
10994 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
10995 {
10996 bufferImageGranularityConflict = true;
10997 break;
10998 }
10999 }
11000 else
11001 // Already on previous page.
11002 break;
11003 }
11004 if(bufferImageGranularityConflict)
11005 {
11006 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11007 }
11008 }
11009
11010 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11011 suballocations2nd.back().offset : size;
11012
11013 // There is enough free space at the end after alignment.
11014 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11015 {
11016 // Check next suballocations for BufferImageGranularity conflicts.
11017 // If conflict exists, allocation cannot be made here.
11018 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11019 {
11020 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11021 {
11022 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11023 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11024 {
11025 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11026 {
11027 return false;
11028 }
11029 }
11030 else
11031 {
11032 // Already on previous page.
11033 break;
11034 }
11035 }
11036 }
11037
11038 // All tests passed: Success.
11039 pAllocationRequest->offset = resultOffset;
11040 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11041 pAllocationRequest->sumItemSize = 0;
11042 // pAllocationRequest->item, customData unused.
11043 pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11044 pAllocationRequest->itemsToMakeLostCount = 0;
11045 return true;
11046 }
11047 }
11048
11049 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11050 // beginning of 1st vector as the end of free space.
11051 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11052 {
11053 VMA_ASSERT(!suballocations1st.empty());
11054
11055 VkDeviceSize resultBaseOffset = 0;
11056 if(!suballocations2nd.empty())
11057 {
11058 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11059 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11060 }
11061
11062 // Start from offset equal to beginning of free space.
11063 VkDeviceSize resultOffset = resultBaseOffset;
11064
11065 // Apply VMA_DEBUG_MARGIN at the beginning.
11066 if(VMA_DEBUG_MARGIN > 0)
11067 {
11068 resultOffset += VMA_DEBUG_MARGIN;
11069 }
11070
11071 // Apply alignment.
11072 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11073
11074 // Check previous suballocations for BufferImageGranularity conflicts.
11075 // Make bigger alignment if necessary.
11076 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
11077 {
11078 bool bufferImageGranularityConflict = false;
11079 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11080 {
11081 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11082 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11083 {
11084 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11085 {
11086 bufferImageGranularityConflict = true;
11087 break;
11088 }
11089 }
11090 else
11091 // Already on previous page.
11092 break;
11093 }
11094 if(bufferImageGranularityConflict)
11095 {
11096 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11097 }
11098 }
11099
11100 pAllocationRequest->itemsToMakeLostCount = 0;
11101 pAllocationRequest->sumItemSize = 0;
11102 size_t index1st = m_1stNullItemsBeginCount;
11103
11104 if(canMakeOtherLost)
11105 {
11106 while(index1st < suballocations1st.size() &&
11107 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11108 {
11109 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11110 const VmaSuballocation& suballoc = suballocations1st[index1st];
11111 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11112 {
11113 // No problem.
11114 }
11115 else
11116 {
11117 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11118 if(suballoc.hAllocation->CanBecomeLost() &&
11119 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11120 {
11121 ++pAllocationRequest->itemsToMakeLostCount;
11122 pAllocationRequest->sumItemSize += suballoc.size;
11123 }
11124 else
11125 {
11126 return false;
11127 }
11128 }
11129 ++index1st;
11130 }
11131
11132 // Check next suballocations for BufferImageGranularity conflicts.
11133 // If conflict exists, we must mark more allocations lost or fail.
11134 if(bufferImageGranularity > 1)
11135 {
11136 while(index1st < suballocations1st.size())
11137 {
11138 const VmaSuballocation& suballoc = suballocations1st[index1st];
11139 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11140 {
11141 if(suballoc.hAllocation != VK_NULL_HANDLE)
11142 {
11143 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11144 if(suballoc.hAllocation->CanBecomeLost() &&
11145 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11146 {
11147 ++pAllocationRequest->itemsToMakeLostCount;
11148 pAllocationRequest->sumItemSize += suballoc.size;
11149 }
11150 else
11151 {
11152 return false;
11153 }
11154 }
11155 }
11156 else
11157 {
11158 // Already on next page.
11159 break;
11160 }
11161 ++index1st;
11162 }
11163 }
11164
11165 // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11166 if(index1st == suballocations1st.size() &&
11167 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11168 {
11169 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11170 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11171 }
11172 }
11173
11174 // There is enough free space at the end after alignment.
11175 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11176 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11177 {
11178 // Check next suballocations for BufferImageGranularity conflicts.
11179 // If conflict exists, allocation cannot be made here.
11180 if(bufferImageGranularity > 1)
11181 {
11182 for(size_t nextSuballocIndex = index1st;
11183 nextSuballocIndex < suballocations1st.size();
11184 nextSuballocIndex++)
11185 {
11186 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11187 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11188 {
11189 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11190 {
11191 return false;
11192 }
11193 }
11194 else
11195 {
11196 // Already on next page.
11197 break;
11198 }
11199 }
11200 }
11201
11202 // All tests passed: Success.
11203 pAllocationRequest->offset = resultOffset;
11204 pAllocationRequest->sumFreeSize =
11205 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11206 - resultBaseOffset
11207 - pAllocationRequest->sumItemSize;
11208 pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11209 // pAllocationRequest->item, customData unused.
11210 return true;
11211 }
11212 }
11213
11214 return false;
11215 }
11216
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11217 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11218 uint32_t currentFrameIndex,
11219 uint32_t frameInUseCount,
11220 VmaAllocationRequest* pAllocationRequest)
11221 {
11222 if(pAllocationRequest->itemsToMakeLostCount == 0)
11223 {
11224 return true;
11225 }
11226
11227 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11228
11229 // We always start from 1st.
11230 SuballocationVectorType* suballocations = &AccessSuballocations1st();
11231 size_t index = m_1stNullItemsBeginCount;
11232 size_t madeLostCount = 0;
11233 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11234 {
11235 if(index == suballocations->size())
11236 {
11237 index = 0;
11238 // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11239 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11240 {
11241 suballocations = &AccessSuballocations2nd();
11242 }
11243 // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11244 // suballocations continues pointing at AccessSuballocations1st().
11245 VMA_ASSERT(!suballocations->empty());
11246 }
11247 VmaSuballocation& suballoc = (*suballocations)[index];
11248 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11249 {
11250 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11251 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11252 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11253 {
11254 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11255 suballoc.hAllocation = VK_NULL_HANDLE;
11256 m_SumFreeSize += suballoc.size;
11257 if(suballocations == &AccessSuballocations1st())
11258 {
11259 ++m_1stNullItemsMiddleCount;
11260 }
11261 else
11262 {
11263 ++m_2ndNullItemsCount;
11264 }
11265 ++madeLostCount;
11266 }
11267 else
11268 {
11269 return false;
11270 }
11271 }
11272 ++index;
11273 }
11274
11275 CleanupAfterFree();
11276 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11277
11278 return true;
11279 }
11280
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11281 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11282 {
11283 uint32_t lostAllocationCount = 0;
11284
11285 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11286 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11287 {
11288 VmaSuballocation& suballoc = suballocations1st[i];
11289 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11290 suballoc.hAllocation->CanBecomeLost() &&
11291 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11292 {
11293 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11294 suballoc.hAllocation = VK_NULL_HANDLE;
11295 ++m_1stNullItemsMiddleCount;
11296 m_SumFreeSize += suballoc.size;
11297 ++lostAllocationCount;
11298 }
11299 }
11300
11301 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11302 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11303 {
11304 VmaSuballocation& suballoc = suballocations2nd[i];
11305 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11306 suballoc.hAllocation->CanBecomeLost() &&
11307 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11308 {
11309 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11310 suballoc.hAllocation = VK_NULL_HANDLE;
11311 ++m_2ndNullItemsCount;
11312 m_SumFreeSize += suballoc.size;
11313 ++lostAllocationCount;
11314 }
11315 }
11316
11317 if(lostAllocationCount)
11318 {
11319 CleanupAfterFree();
11320 }
11321
11322 return lostAllocationCount;
11323 }
11324
CheckCorruption(const void * pBlockData)11325 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11326 {
11327 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11328 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11329 {
11330 const VmaSuballocation& suballoc = suballocations1st[i];
11331 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11332 {
11333 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11334 {
11335 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11336 return VK_ERROR_VALIDATION_FAILED_EXT;
11337 }
11338 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11339 {
11340 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11341 return VK_ERROR_VALIDATION_FAILED_EXT;
11342 }
11343 }
11344 }
11345
11346 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11347 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11348 {
11349 const VmaSuballocation& suballoc = suballocations2nd[i];
11350 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11351 {
11352 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11353 {
11354 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11355 return VK_ERROR_VALIDATION_FAILED_EXT;
11356 }
11357 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11358 {
11359 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11360 return VK_ERROR_VALIDATION_FAILED_EXT;
11361 }
11362 }
11363 }
11364
11365 return VK_SUCCESS;
11366 }
11367
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11368 void VmaBlockMetadata_Linear::Alloc(
11369 const VmaAllocationRequest& request,
11370 VmaSuballocationType type,
11371 VkDeviceSize allocSize,
11372 VmaAllocation hAllocation)
11373 {
11374 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11375
11376 switch(request.type)
11377 {
11378 case VmaAllocationRequestType::UpperAddress:
11379 {
11380 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11381 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11382 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11383 suballocations2nd.push_back(newSuballoc);
11384 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11385 }
11386 break;
11387 case VmaAllocationRequestType::EndOf1st:
11388 {
11389 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11390
11391 VMA_ASSERT(suballocations1st.empty() ||
11392 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11393 // Check if it fits before the end of the block.
11394 VMA_ASSERT(request.offset + allocSize <= GetSize());
11395
11396 suballocations1st.push_back(newSuballoc);
11397 }
11398 break;
11399 case VmaAllocationRequestType::EndOf2nd:
11400 {
11401 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11402 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11403 VMA_ASSERT(!suballocations1st.empty() &&
11404 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11405 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11406
11407 switch(m_2ndVectorMode)
11408 {
11409 case SECOND_VECTOR_EMPTY:
11410 // First allocation from second part ring buffer.
11411 VMA_ASSERT(suballocations2nd.empty());
11412 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11413 break;
11414 case SECOND_VECTOR_RING_BUFFER:
11415 // 2-part ring buffer is already started.
11416 VMA_ASSERT(!suballocations2nd.empty());
11417 break;
11418 case SECOND_VECTOR_DOUBLE_STACK:
11419 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11420 break;
11421 default:
11422 VMA_ASSERT(0);
11423 }
11424
11425 suballocations2nd.push_back(newSuballoc);
11426 }
11427 break;
11428 default:
11429 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11430 }
11431
11432 m_SumFreeSize -= newSuballoc.size;
11433 }
11434
Free(const VmaAllocation allocation)11435 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11436 {
11437 FreeAtOffset(allocation->GetOffset());
11438 }
11439
FreeAtOffset(VkDeviceSize offset)11440 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11441 {
11442 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11443 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11444
11445 if(!suballocations1st.empty())
11446 {
11447 // First allocation: Mark it as next empty at the beginning.
11448 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11449 if(firstSuballoc.offset == offset)
11450 {
11451 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11452 firstSuballoc.hAllocation = VK_NULL_HANDLE;
11453 m_SumFreeSize += firstSuballoc.size;
11454 ++m_1stNullItemsBeginCount;
11455 CleanupAfterFree();
11456 return;
11457 }
11458 }
11459
11460 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11461 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11462 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11463 {
11464 VmaSuballocation& lastSuballoc = suballocations2nd.back();
11465 if(lastSuballoc.offset == offset)
11466 {
11467 m_SumFreeSize += lastSuballoc.size;
11468 suballocations2nd.pop_back();
11469 CleanupAfterFree();
11470 return;
11471 }
11472 }
11473 // Last allocation in 1st vector.
11474 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11475 {
11476 VmaSuballocation& lastSuballoc = suballocations1st.back();
11477 if(lastSuballoc.offset == offset)
11478 {
11479 m_SumFreeSize += lastSuballoc.size;
11480 suballocations1st.pop_back();
11481 CleanupAfterFree();
11482 return;
11483 }
11484 }
11485
11486 // Item from the middle of 1st vector.
11487 {
11488 VmaSuballocation refSuballoc;
11489 refSuballoc.offset = offset;
11490 // Rest of members stays uninitialized intentionally for better performance.
11491 SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11492 suballocations1st.begin() + m_1stNullItemsBeginCount,
11493 suballocations1st.end(),
11494 refSuballoc,
11495 VmaSuballocationOffsetLess());
11496 if(it != suballocations1st.end())
11497 {
11498 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11499 it->hAllocation = VK_NULL_HANDLE;
11500 ++m_1stNullItemsMiddleCount;
11501 m_SumFreeSize += it->size;
11502 CleanupAfterFree();
11503 return;
11504 }
11505 }
11506
11507 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11508 {
11509 // Item from the middle of 2nd vector.
11510 VmaSuballocation refSuballoc;
11511 refSuballoc.offset = offset;
11512 // Rest of members stays uninitialized intentionally for better performance.
11513 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11514 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11515 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11516 if(it != suballocations2nd.end())
11517 {
11518 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11519 it->hAllocation = VK_NULL_HANDLE;
11520 ++m_2ndNullItemsCount;
11521 m_SumFreeSize += it->size;
11522 CleanupAfterFree();
11523 return;
11524 }
11525 }
11526
11527 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11528 }
11529
ShouldCompact1st()11530 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11531 {
11532 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11533 const size_t suballocCount = AccessSuballocations1st().size();
11534 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11535 }
11536
CleanupAfterFree()11537 void VmaBlockMetadata_Linear::CleanupAfterFree()
11538 {
11539 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11540 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11541
11542 if(IsEmpty())
11543 {
11544 suballocations1st.clear();
11545 suballocations2nd.clear();
11546 m_1stNullItemsBeginCount = 0;
11547 m_1stNullItemsMiddleCount = 0;
11548 m_2ndNullItemsCount = 0;
11549 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11550 }
11551 else
11552 {
11553 const size_t suballoc1stCount = suballocations1st.size();
11554 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11555 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11556
11557 // Find more null items at the beginning of 1st vector.
11558 while(m_1stNullItemsBeginCount < suballoc1stCount &&
11559 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11560 {
11561 ++m_1stNullItemsBeginCount;
11562 --m_1stNullItemsMiddleCount;
11563 }
11564
11565 // Find more null items at the end of 1st vector.
11566 while(m_1stNullItemsMiddleCount > 0 &&
11567 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11568 {
11569 --m_1stNullItemsMiddleCount;
11570 suballocations1st.pop_back();
11571 }
11572
11573 // Find more null items at the end of 2nd vector.
11574 while(m_2ndNullItemsCount > 0 &&
11575 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11576 {
11577 --m_2ndNullItemsCount;
11578 suballocations2nd.pop_back();
11579 }
11580
11581 // Find more null items at the beginning of 2nd vector.
11582 while(m_2ndNullItemsCount > 0 &&
11583 suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11584 {
11585 --m_2ndNullItemsCount;
11586 VmaVectorRemove(suballocations2nd, 0);
11587 }
11588
11589 if(ShouldCompact1st())
11590 {
11591 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11592 size_t srcIndex = m_1stNullItemsBeginCount;
11593 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11594 {
11595 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11596 {
11597 ++srcIndex;
11598 }
11599 if(dstIndex != srcIndex)
11600 {
11601 suballocations1st[dstIndex] = suballocations1st[srcIndex];
11602 }
11603 ++srcIndex;
11604 }
11605 suballocations1st.resize(nonNullItemCount);
11606 m_1stNullItemsBeginCount = 0;
11607 m_1stNullItemsMiddleCount = 0;
11608 }
11609
11610 // 2nd vector became empty.
11611 if(suballocations2nd.empty())
11612 {
11613 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11614 }
11615
11616 // 1st vector became empty.
11617 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11618 {
11619 suballocations1st.clear();
11620 m_1stNullItemsBeginCount = 0;
11621
11622 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11623 {
11624 // Swap 1st with 2nd. Now 2nd is empty.
11625 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11626 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11627 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11628 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11629 {
11630 ++m_1stNullItemsBeginCount;
11631 --m_1stNullItemsMiddleCount;
11632 }
11633 m_2ndNullItemsCount = 0;
11634 m_1stVectorIndex ^= 1;
11635 }
11636 }
11637 }
11638
11639 VMA_HEAVY_ASSERT(Validate());
11640 }
11641
11642
11643 ////////////////////////////////////////////////////////////////////////////////
11644 // class VmaBlockMetadata_Buddy
11645
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)11646 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11647 VmaBlockMetadata(hAllocator),
11648 m_Root(VMA_NULL),
11649 m_AllocationCount(0),
11650 m_FreeCount(1),
11651 m_SumFreeSize(0)
11652 {
11653 memset(m_FreeList, 0, sizeof(m_FreeList));
11654 }
11655
~VmaBlockMetadata_Buddy()11656 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11657 {
11658 DeleteNode(m_Root);
11659 }
11660
Init(VkDeviceSize size)11661 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11662 {
11663 VmaBlockMetadata::Init(size);
11664
11665 m_UsableSize = VmaPrevPow2(size);
11666 m_SumFreeSize = m_UsableSize;
11667
11668 // Calculate m_LevelCount.
11669 m_LevelCount = 1;
11670 while(m_LevelCount < MAX_LEVELS &&
11671 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11672 {
11673 ++m_LevelCount;
11674 }
11675
11676 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11677 rootNode->offset = 0;
11678 rootNode->type = Node::TYPE_FREE;
11679 rootNode->parent = VMA_NULL;
11680 rootNode->buddy = VMA_NULL;
11681
11682 m_Root = rootNode;
11683 AddToFreeListFront(0, rootNode);
11684 }
11685
Validate()11686 bool VmaBlockMetadata_Buddy::Validate() const
11687 {
11688 // Validate tree.
11689 ValidationContext ctx;
11690 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11691 {
11692 VMA_VALIDATE(false && "ValidateNode failed.");
11693 }
11694 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11695 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11696
11697 // Validate free node lists.
11698 for(uint32_t level = 0; level < m_LevelCount; ++level)
11699 {
11700 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11701 m_FreeList[level].front->free.prev == VMA_NULL);
11702
11703 for(Node* node = m_FreeList[level].front;
11704 node != VMA_NULL;
11705 node = node->free.next)
11706 {
11707 VMA_VALIDATE(node->type == Node::TYPE_FREE);
11708
11709 if(node->free.next == VMA_NULL)
11710 {
11711 VMA_VALIDATE(m_FreeList[level].back == node);
11712 }
11713 else
11714 {
11715 VMA_VALIDATE(node->free.next->free.prev == node);
11716 }
11717 }
11718 }
11719
11720 // Validate that free lists ar higher levels are empty.
11721 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11722 {
11723 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11724 }
11725
11726 return true;
11727 }
11728
GetUnusedRangeSizeMax()11729 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11730 {
11731 for(uint32_t level = 0; level < m_LevelCount; ++level)
11732 {
11733 if(m_FreeList[level].front != VMA_NULL)
11734 {
11735 return LevelToNodeSize(level);
11736 }
11737 }
11738 return 0;
11739 }
11740
CalcAllocationStatInfo(VmaStatInfo & outInfo)11741 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11742 {
11743 const VkDeviceSize unusableSize = GetUnusableSize();
11744
11745 outInfo.blockCount = 1;
11746
11747 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11748 outInfo.usedBytes = outInfo.unusedBytes = 0;
11749
11750 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11751 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11752 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11753
11754 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11755
11756 if(unusableSize > 0)
11757 {
11758 ++outInfo.unusedRangeCount;
11759 outInfo.unusedBytes += unusableSize;
11760 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11761 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11762 }
11763 }
11764
AddPoolStats(VmaPoolStats & inoutStats)11765 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11766 {
11767 const VkDeviceSize unusableSize = GetUnusableSize();
11768
11769 inoutStats.size += GetSize();
11770 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11771 inoutStats.allocationCount += m_AllocationCount;
11772 inoutStats.unusedRangeCount += m_FreeCount;
11773 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11774
11775 if(unusableSize > 0)
11776 {
11777 ++inoutStats.unusedRangeCount;
11778 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11779 }
11780 }
11781
11782 #if VMA_STATS_STRING_ENABLED
11783
PrintDetailedMap(class VmaJsonWriter & json)11784 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11785 {
11786 // TODO optimize
11787 VmaStatInfo stat;
11788 CalcAllocationStatInfo(stat);
11789
11790 PrintDetailedMap_Begin(
11791 json,
11792 stat.unusedBytes,
11793 stat.allocationCount,
11794 stat.unusedRangeCount);
11795
11796 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11797
11798 const VkDeviceSize unusableSize = GetUnusableSize();
11799 if(unusableSize > 0)
11800 {
11801 PrintDetailedMap_UnusedRange(json,
11802 m_UsableSize, // offset
11803 unusableSize); // size
11804 }
11805
11806 PrintDetailedMap_End(json);
11807 }
11808
11809 #endif // #if VMA_STATS_STRING_ENABLED
11810
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)11811 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11812 uint32_t currentFrameIndex,
11813 uint32_t frameInUseCount,
11814 VkDeviceSize bufferImageGranularity,
11815 VkDeviceSize allocSize,
11816 VkDeviceSize allocAlignment,
11817 bool upperAddress,
11818 VmaSuballocationType allocType,
11819 bool canMakeOtherLost,
11820 uint32_t strategy,
11821 VmaAllocationRequest* pAllocationRequest)
11822 {
11823 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11824
11825 // Simple way to respect bufferImageGranularity. May be optimized some day.
11826 // Whenever it might be an OPTIMAL image...
11827 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11828 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11829 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11830 {
11831 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11832 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11833 }
11834
11835 if(allocSize > m_UsableSize)
11836 {
11837 return false;
11838 }
11839
11840 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11841 for(uint32_t level = targetLevel + 1; level--; )
11842 {
11843 for(Node* freeNode = m_FreeList[level].front;
11844 freeNode != VMA_NULL;
11845 freeNode = freeNode->free.next)
11846 {
11847 if(freeNode->offset % allocAlignment == 0)
11848 {
11849 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11850 pAllocationRequest->offset = freeNode->offset;
11851 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11852 pAllocationRequest->sumItemSize = 0;
11853 pAllocationRequest->itemsToMakeLostCount = 0;
11854 pAllocationRequest->customData = (void*)(uintptr_t)level;
11855 return true;
11856 }
11857 }
11858 }
11859
11860 return false;
11861 }
11862
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11863 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11864 uint32_t currentFrameIndex,
11865 uint32_t frameInUseCount,
11866 VmaAllocationRequest* pAllocationRequest)
11867 {
11868 /*
11869 Lost allocations are not supported in buddy allocator at the moment.
11870 Support might be added in the future.
11871 */
11872 return pAllocationRequest->itemsToMakeLostCount == 0;
11873 }
11874
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11875 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11876 {
11877 /*
11878 Lost allocations are not supported in buddy allocator at the moment.
11879 Support might be added in the future.
11880 */
11881 return 0;
11882 }
11883
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11884 void VmaBlockMetadata_Buddy::Alloc(
11885 const VmaAllocationRequest& request,
11886 VmaSuballocationType type,
11887 VkDeviceSize allocSize,
11888 VmaAllocation hAllocation)
11889 {
11890 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11891
11892 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11893 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11894
11895 Node* currNode = m_FreeList[currLevel].front;
11896 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11897 while(currNode->offset != request.offset)
11898 {
11899 currNode = currNode->free.next;
11900 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11901 }
11902
11903 // Go down, splitting free nodes.
11904 while(currLevel < targetLevel)
11905 {
11906 // currNode is already first free node at currLevel.
11907 // Remove it from list of free nodes at this currLevel.
11908 RemoveFromFreeList(currLevel, currNode);
11909
11910 const uint32_t childrenLevel = currLevel + 1;
11911
11912 // Create two free sub-nodes.
11913 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11914 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11915
11916 leftChild->offset = currNode->offset;
11917 leftChild->type = Node::TYPE_FREE;
11918 leftChild->parent = currNode;
11919 leftChild->buddy = rightChild;
11920
11921 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11922 rightChild->type = Node::TYPE_FREE;
11923 rightChild->parent = currNode;
11924 rightChild->buddy = leftChild;
11925
11926 // Convert current currNode to split type.
11927 currNode->type = Node::TYPE_SPLIT;
11928 currNode->split.leftChild = leftChild;
11929
11930 // Add child nodes to free list. Order is important!
11931 AddToFreeListFront(childrenLevel, rightChild);
11932 AddToFreeListFront(childrenLevel, leftChild);
11933
11934 ++m_FreeCount;
11935 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11936 ++currLevel;
11937 currNode = m_FreeList[currLevel].front;
11938
11939 /*
11940 We can be sure that currNode, as left child of node previously split,
11941 also fullfills the alignment requirement.
11942 */
11943 }
11944
11945 // Remove from free list.
11946 VMA_ASSERT(currLevel == targetLevel &&
11947 currNode != VMA_NULL &&
11948 currNode->type == Node::TYPE_FREE);
11949 RemoveFromFreeList(currLevel, currNode);
11950
11951 // Convert to allocation node.
11952 currNode->type = Node::TYPE_ALLOCATION;
11953 currNode->allocation.alloc = hAllocation;
11954
11955 ++m_AllocationCount;
11956 --m_FreeCount;
11957 m_SumFreeSize -= allocSize;
11958 }
11959
DeleteNode(Node * node)11960 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11961 {
11962 if(node->type == Node::TYPE_SPLIT)
11963 {
11964 DeleteNode(node->split.leftChild->buddy);
11965 DeleteNode(node->split.leftChild);
11966 }
11967
11968 vma_delete(GetAllocationCallbacks(), node);
11969 }
11970
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11971 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11972 {
11973 VMA_VALIDATE(level < m_LevelCount);
11974 VMA_VALIDATE(curr->parent == parent);
11975 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11976 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11977 switch(curr->type)
11978 {
11979 case Node::TYPE_FREE:
11980 // curr->free.prev, next are validated separately.
11981 ctx.calculatedSumFreeSize += levelNodeSize;
11982 ++ctx.calculatedFreeCount;
11983 break;
11984 case Node::TYPE_ALLOCATION:
11985 ++ctx.calculatedAllocationCount;
11986 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11987 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11988 break;
11989 case Node::TYPE_SPLIT:
11990 {
11991 const uint32_t childrenLevel = level + 1;
11992 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11993 const Node* const leftChild = curr->split.leftChild;
11994 VMA_VALIDATE(leftChild != VMA_NULL);
11995 VMA_VALIDATE(leftChild->offset == curr->offset);
11996 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
11997 {
11998 VMA_VALIDATE(false && "ValidateNode for left child failed.");
11999 }
12000 const Node* const rightChild = leftChild->buddy;
12001 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12002 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12003 {
12004 VMA_VALIDATE(false && "ValidateNode for right child failed.");
12005 }
12006 }
12007 break;
12008 default:
12009 return false;
12010 }
12011
12012 return true;
12013 }
12014
AllocSizeToLevel(VkDeviceSize allocSize)12015 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12016 {
12017 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12018 uint32_t level = 0;
12019 VkDeviceSize currLevelNodeSize = m_UsableSize;
12020 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12021 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12022 {
12023 ++level;
12024 currLevelNodeSize = nextLevelNodeSize;
12025 nextLevelNodeSize = currLevelNodeSize >> 1;
12026 }
12027 return level;
12028 }
12029
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)12030 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12031 {
12032 // Find node and level.
12033 Node* node = m_Root;
12034 VkDeviceSize nodeOffset = 0;
12035 uint32_t level = 0;
12036 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12037 while(node->type == Node::TYPE_SPLIT)
12038 {
12039 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12040 if(offset < nodeOffset + nextLevelSize)
12041 {
12042 node = node->split.leftChild;
12043 }
12044 else
12045 {
12046 node = node->split.leftChild->buddy;
12047 nodeOffset += nextLevelSize;
12048 }
12049 ++level;
12050 levelNodeSize = nextLevelSize;
12051 }
12052
12053 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12054 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12055
12056 ++m_FreeCount;
12057 --m_AllocationCount;
12058 m_SumFreeSize += alloc->GetSize();
12059
12060 node->type = Node::TYPE_FREE;
12061
12062 // Join free nodes if possible.
12063 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12064 {
12065 RemoveFromFreeList(level, node->buddy);
12066 Node* const parent = node->parent;
12067
12068 vma_delete(GetAllocationCallbacks(), node->buddy);
12069 vma_delete(GetAllocationCallbacks(), node);
12070 parent->type = Node::TYPE_FREE;
12071
12072 node = parent;
12073 --level;
12074 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12075 --m_FreeCount;
12076 }
12077
12078 AddToFreeListFront(level, node);
12079 }
12080
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)12081 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12082 {
12083 switch(node->type)
12084 {
12085 case Node::TYPE_FREE:
12086 ++outInfo.unusedRangeCount;
12087 outInfo.unusedBytes += levelNodeSize;
12088 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12089 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12090 break;
12091 case Node::TYPE_ALLOCATION:
12092 {
12093 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12094 ++outInfo.allocationCount;
12095 outInfo.usedBytes += allocSize;
12096 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12097 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12098
12099 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12100 if(unusedRangeSize > 0)
12101 {
12102 ++outInfo.unusedRangeCount;
12103 outInfo.unusedBytes += unusedRangeSize;
12104 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12105 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12106 }
12107 }
12108 break;
12109 case Node::TYPE_SPLIT:
12110 {
12111 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12112 const Node* const leftChild = node->split.leftChild;
12113 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12114 const Node* const rightChild = leftChild->buddy;
12115 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12116 }
12117 break;
12118 default:
12119 VMA_ASSERT(0);
12120 }
12121 }
12122
AddToFreeListFront(uint32_t level,Node * node)12123 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12124 {
12125 VMA_ASSERT(node->type == Node::TYPE_FREE);
12126
12127 // List is empty.
12128 Node* const frontNode = m_FreeList[level].front;
12129 if(frontNode == VMA_NULL)
12130 {
12131 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12132 node->free.prev = node->free.next = VMA_NULL;
12133 m_FreeList[level].front = m_FreeList[level].back = node;
12134 }
12135 else
12136 {
12137 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12138 node->free.prev = VMA_NULL;
12139 node->free.next = frontNode;
12140 frontNode->free.prev = node;
12141 m_FreeList[level].front = node;
12142 }
12143 }
12144
RemoveFromFreeList(uint32_t level,Node * node)12145 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12146 {
12147 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12148
12149 // It is at the front.
12150 if(node->free.prev == VMA_NULL)
12151 {
12152 VMA_ASSERT(m_FreeList[level].front == node);
12153 m_FreeList[level].front = node->free.next;
12154 }
12155 else
12156 {
12157 Node* const prevFreeNode = node->free.prev;
12158 VMA_ASSERT(prevFreeNode->free.next == node);
12159 prevFreeNode->free.next = node->free.next;
12160 }
12161
12162 // It is at the back.
12163 if(node->free.next == VMA_NULL)
12164 {
12165 VMA_ASSERT(m_FreeList[level].back == node);
12166 m_FreeList[level].back = node->free.prev;
12167 }
12168 else
12169 {
12170 Node* const nextFreeNode = node->free.next;
12171 VMA_ASSERT(nextFreeNode->free.prev == node);
12172 nextFreeNode->free.prev = node->free.prev;
12173 }
12174 }
12175
12176 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)12177 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12178 {
12179 switch(node->type)
12180 {
12181 case Node::TYPE_FREE:
12182 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12183 break;
12184 case Node::TYPE_ALLOCATION:
12185 {
12186 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12187 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12188 if(allocSize < levelNodeSize)
12189 {
12190 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12191 }
12192 }
12193 break;
12194 case Node::TYPE_SPLIT:
12195 {
12196 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12197 const Node* const leftChild = node->split.leftChild;
12198 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12199 const Node* const rightChild = leftChild->buddy;
12200 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12201 }
12202 break;
12203 default:
12204 VMA_ASSERT(0);
12205 }
12206 }
12207 #endif // #if VMA_STATS_STRING_ENABLED
12208
12209
12210 ////////////////////////////////////////////////////////////////////////////////
12211 // class VmaDeviceMemoryBlock
12212
VmaDeviceMemoryBlock(VmaAllocator hAllocator)12213 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12214 m_pMetadata(VMA_NULL),
12215 m_MemoryTypeIndex(UINT32_MAX),
12216 m_Id(0),
12217 m_hMemory(VK_NULL_HANDLE),
12218 m_MapCount(0),
12219 m_pMappedData(VMA_NULL)
12220 {
12221 }
12222
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)12223 void VmaDeviceMemoryBlock::Init(
12224 VmaAllocator hAllocator,
12225 VmaPool hParentPool,
12226 uint32_t newMemoryTypeIndex,
12227 VkDeviceMemory newMemory,
12228 VkDeviceSize newSize,
12229 uint32_t id,
12230 uint32_t algorithm)
12231 {
12232 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12233
12234 m_hParentPool = hParentPool;
12235 m_MemoryTypeIndex = newMemoryTypeIndex;
12236 m_Id = id;
12237 m_hMemory = newMemory;
12238
12239 switch(algorithm)
12240 {
12241 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12242 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12243 break;
12244 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12245 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12246 break;
12247 default:
12248 VMA_ASSERT(0);
12249 // Fall-through.
12250 case 0:
12251 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12252 }
12253 m_pMetadata->Init(newSize);
12254 }
12255
Destroy(VmaAllocator allocator)12256 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12257 {
12258 // This is the most important assert in the entire library.
12259 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12260 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12261
12262 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12263 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12264 m_hMemory = VK_NULL_HANDLE;
12265
12266 vma_delete(allocator, m_pMetadata);
12267 m_pMetadata = VMA_NULL;
12268 }
12269
Validate()12270 bool VmaDeviceMemoryBlock::Validate() const
12271 {
12272 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12273 (m_pMetadata->GetSize() != 0));
12274
12275 return m_pMetadata->Validate();
12276 }
12277
CheckCorruption(VmaAllocator hAllocator)12278 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12279 {
12280 void* pData = nullptr;
12281 VkResult res = Map(hAllocator, 1, &pData);
12282 if(res != VK_SUCCESS)
12283 {
12284 return res;
12285 }
12286
12287 res = m_pMetadata->CheckCorruption(pData);
12288
12289 Unmap(hAllocator, 1);
12290
12291 return res;
12292 }
12293
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)12294 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12295 {
12296 if(count == 0)
12297 {
12298 return VK_SUCCESS;
12299 }
12300
12301 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12302 if(m_MapCount != 0)
12303 {
12304 m_MapCount += count;
12305 VMA_ASSERT(m_pMappedData != VMA_NULL);
12306 if(ppData != VMA_NULL)
12307 {
12308 *ppData = m_pMappedData;
12309 }
12310 return VK_SUCCESS;
12311 }
12312 else
12313 {
12314 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12315 hAllocator->m_hDevice,
12316 m_hMemory,
12317 0, // offset
12318 VK_WHOLE_SIZE,
12319 0, // flags
12320 &m_pMappedData);
12321 if(result == VK_SUCCESS)
12322 {
12323 if(ppData != VMA_NULL)
12324 {
12325 *ppData = m_pMappedData;
12326 }
12327 m_MapCount = count;
12328 }
12329 return result;
12330 }
12331 }
12332
Unmap(VmaAllocator hAllocator,uint32_t count)12333 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12334 {
12335 if(count == 0)
12336 {
12337 return;
12338 }
12339
12340 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12341 if(m_MapCount >= count)
12342 {
12343 m_MapCount -= count;
12344 if(m_MapCount == 0)
12345 {
12346 m_pMappedData = VMA_NULL;
12347 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12348 }
12349 }
12350 else
12351 {
12352 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12353 }
12354 }
12355
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12356 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12357 {
12358 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12359 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12360
12361 void* pData;
12362 VkResult res = Map(hAllocator, 1, &pData);
12363 if(res != VK_SUCCESS)
12364 {
12365 return res;
12366 }
12367
12368 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12369 VmaWriteMagicValue(pData, allocOffset + allocSize);
12370
12371 Unmap(hAllocator, 1);
12372
12373 return VK_SUCCESS;
12374 }
12375
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12376 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12377 {
12378 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12379 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12380
12381 void* pData;
12382 VkResult res = Map(hAllocator, 1, &pData);
12383 if(res != VK_SUCCESS)
12384 {
12385 return res;
12386 }
12387
12388 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12389 {
12390 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12391 }
12392 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12393 {
12394 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12395 }
12396
12397 Unmap(hAllocator, 1);
12398
12399 return VK_SUCCESS;
12400 }
12401
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)12402 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12403 const VmaAllocator hAllocator,
12404 const VmaAllocation hAllocation,
12405 VkDeviceSize allocationLocalOffset,
12406 VkBuffer hBuffer,
12407 const void* pNext)
12408 {
12409 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12410 hAllocation->GetBlock() == this);
12411 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12412 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12413 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12414 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12415 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12416 return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12417 }
12418
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)12419 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12420 const VmaAllocator hAllocator,
12421 const VmaAllocation hAllocation,
12422 VkDeviceSize allocationLocalOffset,
12423 VkImage hImage,
12424 const void* pNext)
12425 {
12426 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12427 hAllocation->GetBlock() == this);
12428 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12429 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12430 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12431 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12432 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12433 return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12434 }
12435
InitStatInfo(VmaStatInfo & outInfo)12436 static void InitStatInfo(VmaStatInfo& outInfo)
12437 {
12438 memset(&outInfo, 0, sizeof(outInfo));
12439 outInfo.allocationSizeMin = UINT64_MAX;
12440 outInfo.unusedRangeSizeMin = UINT64_MAX;
12441 }
12442
12443 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)12444 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12445 {
12446 inoutInfo.blockCount += srcInfo.blockCount;
12447 inoutInfo.allocationCount += srcInfo.allocationCount;
12448 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12449 inoutInfo.usedBytes += srcInfo.usedBytes;
12450 inoutInfo.unusedBytes += srcInfo.unusedBytes;
12451 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12452 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12453 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12454 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12455 }
12456
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)12457 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12458 {
12459 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12460 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12461 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12462 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12463 }
12464
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)12465 VmaPool_T::VmaPool_T(
12466 VmaAllocator hAllocator,
12467 const VmaPoolCreateInfo& createInfo,
12468 VkDeviceSize preferredBlockSize) :
12469 m_BlockVector(
12470 hAllocator,
12471 this, // hParentPool
12472 createInfo.memoryTypeIndex,
12473 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12474 createInfo.minBlockCount,
12475 createInfo.maxBlockCount,
12476 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12477 createInfo.frameInUseCount,
12478 createInfo.blockSize != 0, // explicitBlockSize
12479 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12480 m_Id(0),
12481 m_Name(VMA_NULL)
12482 {
12483 }
12484
~VmaPool_T()12485 VmaPool_T::~VmaPool_T()
12486 {
12487 }
12488
SetName(const char * pName)12489 void VmaPool_T::SetName(const char* pName)
12490 {
12491 const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12492 VmaFreeString(allocs, m_Name);
12493
12494 if(pName != VMA_NULL)
12495 {
12496 m_Name = VmaCreateStringCopy(allocs, pName);
12497 }
12498 else
12499 {
12500 m_Name = VMA_NULL;
12501 }
12502 }
12503
12504 #if VMA_STATS_STRING_ENABLED
12505
12506 #endif // #if VMA_STATS_STRING_ENABLED
12507
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)12508 VmaBlockVector::VmaBlockVector(
12509 VmaAllocator hAllocator,
12510 VmaPool hParentPool,
12511 uint32_t memoryTypeIndex,
12512 VkDeviceSize preferredBlockSize,
12513 size_t minBlockCount,
12514 size_t maxBlockCount,
12515 VkDeviceSize bufferImageGranularity,
12516 uint32_t frameInUseCount,
12517 bool explicitBlockSize,
12518 uint32_t algorithm) :
12519 m_hAllocator(hAllocator),
12520 m_hParentPool(hParentPool),
12521 m_MemoryTypeIndex(memoryTypeIndex),
12522 m_PreferredBlockSize(preferredBlockSize),
12523 m_MinBlockCount(minBlockCount),
12524 m_MaxBlockCount(maxBlockCount),
12525 m_BufferImageGranularity(bufferImageGranularity),
12526 m_FrameInUseCount(frameInUseCount),
12527 m_ExplicitBlockSize(explicitBlockSize),
12528 m_Algorithm(algorithm),
12529 m_HasEmptyBlock(false),
12530 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12531 m_NextBlockId(0)
12532 {
12533 }
12534
~VmaBlockVector()12535 VmaBlockVector::~VmaBlockVector()
12536 {
12537 for(size_t i = m_Blocks.size(); i--; )
12538 {
12539 m_Blocks[i]->Destroy(m_hAllocator);
12540 vma_delete(m_hAllocator, m_Blocks[i]);
12541 }
12542 }
12543
CreateMinBlocks()12544 VkResult VmaBlockVector::CreateMinBlocks()
12545 {
12546 for(size_t i = 0; i < m_MinBlockCount; ++i)
12547 {
12548 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12549 if(res != VK_SUCCESS)
12550 {
12551 return res;
12552 }
12553 }
12554 return VK_SUCCESS;
12555 }
12556
GetPoolStats(VmaPoolStats * pStats)12557 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12558 {
12559 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12560
12561 const size_t blockCount = m_Blocks.size();
12562
12563 pStats->size = 0;
12564 pStats->unusedSize = 0;
12565 pStats->allocationCount = 0;
12566 pStats->unusedRangeCount = 0;
12567 pStats->unusedRangeSizeMax = 0;
12568 pStats->blockCount = blockCount;
12569
12570 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12571 {
12572 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12573 VMA_ASSERT(pBlock);
12574 VMA_HEAVY_ASSERT(pBlock->Validate());
12575 pBlock->m_pMetadata->AddPoolStats(*pStats);
12576 }
12577 }
12578
IsEmpty()12579 bool VmaBlockVector::IsEmpty()
12580 {
12581 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12582 return m_Blocks.empty();
12583 }
12584
IsCorruptionDetectionEnabled()12585 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12586 {
12587 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12588 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12589 (VMA_DEBUG_MARGIN > 0) &&
12590 (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12591 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12592 }
12593
12594 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12595
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12596 VkResult VmaBlockVector::Allocate(
12597 uint32_t currentFrameIndex,
12598 VkDeviceSize size,
12599 VkDeviceSize alignment,
12600 const VmaAllocationCreateInfo& createInfo,
12601 VmaSuballocationType suballocType,
12602 size_t allocationCount,
12603 VmaAllocation* pAllocations)
12604 {
12605 size_t allocIndex;
12606 VkResult res = VK_SUCCESS;
12607
12608 if(IsCorruptionDetectionEnabled())
12609 {
12610 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12611 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12612 }
12613
12614 {
12615 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12616 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12617 {
12618 res = AllocatePage(
12619 currentFrameIndex,
12620 size,
12621 alignment,
12622 createInfo,
12623 suballocType,
12624 pAllocations + allocIndex);
12625 if(res != VK_SUCCESS)
12626 {
12627 break;
12628 }
12629 }
12630 }
12631
12632 if(res != VK_SUCCESS)
12633 {
12634 // Free all already created allocations.
12635 while(allocIndex--)
12636 {
12637 Free(pAllocations[allocIndex]);
12638 }
12639 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12640 }
12641
12642 return res;
12643 }
12644
12645 // OH ISSUE: VMA preAlloc
AllocateReserved(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12646 VkResult VmaBlockVector::AllocateReserved(
12647 uint32_t currentFrameIndex,
12648 VkDeviceSize size,
12649 VkDeviceSize alignment,
12650 const VmaAllocationCreateInfo& createInfo,
12651 VmaSuballocationType suballocType,
12652 VmaAllocation* pAllocation)
12653 {
12654 size_t allocIndex;
12655 VkResult res = VK_SUCCESS;
12656
12657 if(IsCorruptionDetectionEnabled())
12658 {
12659 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12660 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12661 }
12662 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12663
12664 // Validate strategy.
12665 switch(strategy)
12666 {
12667 case 0:
12668 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12669 break;
12670 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12671 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12672 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12673 break;
12674 default:
12675 return VK_ERROR_FEATURE_NOT_PRESENT;
12676 }
12677
12678 {
12679 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12680 size_t newBlockIndex = 0;
12681 res = CreateBlock(m_PreferredBlockSize, &newBlockIndex);
12682 if(res != VK_SUCCESS)
12683 {
12684 return res;
12685 }
12686 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12687 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12688
12689 res = AllocateFromBlock(
12690 pBlock,
12691 currentFrameIndex,
12692 size,
12693 alignment,
12694 createInfo.flags,
12695 createInfo.pUserData,
12696 suballocType,
12697 strategy,
12698 pAllocation);
12699 }
12700 return res;
12701 }
12702
SwapLastBlock(VmaBlockVector * blockVector1,VmaBlockVector * blockVector2)12703 void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2)
12704 {
12705 VmaDeviceMemoryBlock* lastBlock1 = blockVector1->m_Blocks.back();
12706 blockVector1->m_Blocks.pop_back();
12707 VmaDeviceMemoryBlock* lastBlock2 = blockVector2->m_Blocks.back();
12708 blockVector2->m_Blocks.pop_back();
12709
12710 blockVector1->m_Blocks.push_back(lastBlock2);
12711 blockVector2->m_Blocks.push_back(lastBlock1);
12712 }
12713
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12714 VkResult VmaBlockVector::AllocatePage(
12715 uint32_t currentFrameIndex,
12716 VkDeviceSize size,
12717 VkDeviceSize alignment,
12718 const VmaAllocationCreateInfo& createInfo,
12719 VmaSuballocationType suballocType,
12720 VmaAllocation* pAllocation)
12721 {
12722 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12723 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12724 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12725 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12726
12727 VkDeviceSize freeMemory;
12728 {
12729 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12730 VmaBudget heapBudget = {};
12731 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12732 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12733 }
12734
12735 const bool canFallbackToDedicated = !IsCustomPool();
12736 const bool canCreateNewBlock =
12737 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12738 (m_Blocks.size() < m_MaxBlockCount) &&
12739 (freeMemory >= size || !canFallbackToDedicated);
12740 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12741
12742 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12743 // Which in turn is available only when maxBlockCount = 1.
12744 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12745 {
12746 canMakeOtherLost = false;
12747 }
12748
12749 // Upper address can only be used with linear allocator and within single memory block.
12750 if(isUpperAddress &&
12751 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12752 {
12753 return VK_ERROR_FEATURE_NOT_PRESENT;
12754 }
12755
12756 // Validate strategy.
12757 switch(strategy)
12758 {
12759 case 0:
12760 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12761 break;
12762 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12763 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12764 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12765 break;
12766 default:
12767 return VK_ERROR_FEATURE_NOT_PRESENT;
12768 }
12769
12770 // Early reject: requested allocation size is larger that maximum block size for this block vector.
12771 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12772 {
12773 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12774 }
12775
12776 /*
12777 Under certain condition, this whole section can be skipped for optimization, so
12778 we move on directly to trying to allocate with canMakeOtherLost. That's the case
12779 e.g. for custom pools with linear algorithm.
12780 */
12781 if(!canMakeOtherLost || canCreateNewBlock)
12782 {
12783 // 1. Search existing allocations. Try to allocate without making other allocations lost.
12784 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12785 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12786
12787 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12788 {
12789 // Use only last block.
12790 if(!m_Blocks.empty())
12791 {
12792 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12793 VMA_ASSERT(pCurrBlock);
12794 VkResult res = AllocateFromBlock(
12795 pCurrBlock,
12796 currentFrameIndex,
12797 size,
12798 alignment,
12799 allocFlagsCopy,
12800 createInfo.pUserData,
12801 suballocType,
12802 strategy,
12803 pAllocation);
12804 if(res == VK_SUCCESS)
12805 {
12806 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12807 return VK_SUCCESS;
12808 }
12809 }
12810 }
12811 else
12812 {
12813 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12814 {
12815 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12816 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12817 {
12818 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12819 VMA_ASSERT(pCurrBlock);
12820 VkResult res = AllocateFromBlock(
12821 pCurrBlock,
12822 currentFrameIndex,
12823 size,
12824 alignment,
12825 allocFlagsCopy,
12826 createInfo.pUserData,
12827 suballocType,
12828 strategy,
12829 pAllocation);
12830 if(res == VK_SUCCESS)
12831 {
12832 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12833 return VK_SUCCESS;
12834 }
12835 }
12836 }
12837 else // WORST_FIT, FIRST_FIT
12838 {
12839 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12840 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12841 {
12842 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12843 VMA_ASSERT(pCurrBlock);
12844 VkResult res = AllocateFromBlock(
12845 pCurrBlock,
12846 currentFrameIndex,
12847 size,
12848 alignment,
12849 allocFlagsCopy,
12850 createInfo.pUserData,
12851 suballocType,
12852 strategy,
12853 pAllocation);
12854 if(res == VK_SUCCESS)
12855 {
12856 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12857 return VK_SUCCESS;
12858 }
12859 }
12860 }
12861 }
12862
12863 // 2. Try to create new block.
12864 if(canCreateNewBlock)
12865 {
12866 // Calculate optimal size for new block.
12867 VkDeviceSize newBlockSize = m_PreferredBlockSize;
12868 uint32_t newBlockSizeShift = 0;
12869 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12870
12871 if(!m_ExplicitBlockSize)
12872 {
12873 // Allocate 1/8, 1/4, 1/2 as first blocks.
12874 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12875 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12876 {
12877 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12878 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12879 {
12880 newBlockSize = smallerNewBlockSize;
12881 ++newBlockSizeShift;
12882 }
12883 else
12884 {
12885 break;
12886 }
12887 }
12888 }
12889
12890 size_t newBlockIndex = 0;
12891 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12892 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12893 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12894 if(!m_ExplicitBlockSize)
12895 {
12896 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12897 {
12898 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12899 if(smallerNewBlockSize >= size)
12900 {
12901 newBlockSize = smallerNewBlockSize;
12902 ++newBlockSizeShift;
12903 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12904 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12905 }
12906 else
12907 {
12908 break;
12909 }
12910 }
12911 }
12912
12913 if(res == VK_SUCCESS)
12914 {
12915 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12916 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12917
12918 res = AllocateFromBlock(
12919 pBlock,
12920 currentFrameIndex,
12921 size,
12922 alignment,
12923 allocFlagsCopy,
12924 createInfo.pUserData,
12925 suballocType,
12926 strategy,
12927 pAllocation);
12928 if(res == VK_SUCCESS)
12929 {
12930 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12931 return VK_SUCCESS;
12932 }
12933 else
12934 {
12935 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12936 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12937 }
12938 }
12939 }
12940 }
12941
12942 // 3. Try to allocate from existing blocks with making other allocations lost.
12943 if(canMakeOtherLost)
12944 {
12945 uint32_t tryIndex = 0;
12946 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12947 {
12948 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12949 VmaAllocationRequest bestRequest = {};
12950 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12951
12952 // 1. Search existing allocations.
12953 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12954 {
12955 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12956 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12957 {
12958 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12959 VMA_ASSERT(pCurrBlock);
12960 VmaAllocationRequest currRequest = {};
12961 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12962 currentFrameIndex,
12963 m_FrameInUseCount,
12964 m_BufferImageGranularity,
12965 size,
12966 alignment,
12967 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12968 suballocType,
12969 canMakeOtherLost,
12970 strategy,
12971 &currRequest))
12972 {
12973 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12974 if(pBestRequestBlock == VMA_NULL ||
12975 currRequestCost < bestRequestCost)
12976 {
12977 pBestRequestBlock = pCurrBlock;
12978 bestRequest = currRequest;
12979 bestRequestCost = currRequestCost;
12980
12981 if(bestRequestCost == 0)
12982 {
12983 break;
12984 }
12985 }
12986 }
12987 }
12988 }
12989 else // WORST_FIT, FIRST_FIT
12990 {
12991 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12992 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12993 {
12994 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12995 VMA_ASSERT(pCurrBlock);
12996 VmaAllocationRequest currRequest = {};
12997 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12998 currentFrameIndex,
12999 m_FrameInUseCount,
13000 m_BufferImageGranularity,
13001 size,
13002 alignment,
13003 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13004 suballocType,
13005 canMakeOtherLost,
13006 strategy,
13007 &currRequest))
13008 {
13009 const VkDeviceSize currRequestCost = currRequest.CalcCost();
13010 if(pBestRequestBlock == VMA_NULL ||
13011 currRequestCost < bestRequestCost ||
13012 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13013 {
13014 pBestRequestBlock = pCurrBlock;
13015 bestRequest = currRequest;
13016 bestRequestCost = currRequestCost;
13017
13018 if(bestRequestCost == 0 ||
13019 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13020 {
13021 break;
13022 }
13023 }
13024 }
13025 }
13026 }
13027
13028 if(pBestRequestBlock != VMA_NULL)
13029 {
13030 if(mapped)
13031 {
13032 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13033 if(res != VK_SUCCESS)
13034 {
13035 return res;
13036 }
13037 }
13038
13039 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13040 currentFrameIndex,
13041 m_FrameInUseCount,
13042 &bestRequest))
13043 {
13044 // Allocate from this pBlock.
13045 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13046 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13047 UpdateHasEmptyBlock();
13048 (*pAllocation)->InitBlockAllocation(
13049 pBestRequestBlock,
13050 bestRequest.offset,
13051 alignment,
13052 size,
13053 m_MemoryTypeIndex,
13054 suballocType,
13055 mapped,
13056 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13057 IsNewBlockFlag());
13058 ClearNewBlockFlag();
13059 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13060 VMA_DEBUG_LOG(" Returned from existing block");
13061 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13062 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13063 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13064 {
13065 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13066 }
13067 if(IsCorruptionDetectionEnabled())
13068 {
13069 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13070 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13071 }
13072 return VK_SUCCESS;
13073 }
13074 // else: Some allocations must have been touched while we are here. Next try.
13075 }
13076 else
13077 {
13078 // Could not find place in any of the blocks - break outer loop.
13079 break;
13080 }
13081 }
13082 /* Maximum number of tries exceeded - a very unlike event when many other
13083 threads are simultaneously touching allocations making it impossible to make
13084 lost at the same time as we try to allocate. */
13085 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13086 {
13087 return VK_ERROR_TOO_MANY_OBJECTS;
13088 }
13089 }
13090
13091 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13092 }
13093
Free(const VmaAllocation hAllocation)13094 void VmaBlockVector::Free(
13095 const VmaAllocation hAllocation)
13096 {
13097 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13098
13099 bool budgetExceeded = false;
13100 {
13101 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13102 VmaBudget heapBudget = {};
13103 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13104 budgetExceeded = heapBudget.usage >= heapBudget.budget;
13105 }
13106
13107 // Scope for lock.
13108 {
13109 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13110
13111 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13112
13113 if(IsCorruptionDetectionEnabled())
13114 {
13115 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13116 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13117 }
13118
13119 if(hAllocation->IsPersistentMap())
13120 {
13121 pBlock->Unmap(m_hAllocator, 1);
13122 }
13123
13124 pBlock->m_pMetadata->Free(hAllocation);
13125 VMA_HEAVY_ASSERT(pBlock->Validate());
13126
13127 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13128
13129 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13130 // pBlock became empty after this deallocation.
13131 if(pBlock->m_pMetadata->IsEmpty())
13132 {
13133 // Already has empty block. We don't want to have two, so delete this one.
13134 if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13135 {
13136 pBlockToDelete = pBlock;
13137 Remove(pBlock);
13138 }
13139 // else: We now have an empty block - leave it.
13140 }
13141 // pBlock didn't become empty, but we have another empty block - find and free that one.
13142 // (This is optional, heuristics.)
13143 else if(m_HasEmptyBlock && canDeleteBlock)
13144 {
13145 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13146 if(pLastBlock->m_pMetadata->IsEmpty())
13147 {
13148 pBlockToDelete = pLastBlock;
13149 m_Blocks.pop_back();
13150 }
13151 }
13152
13153 UpdateHasEmptyBlock();
13154 IncrementallySortBlocks();
13155 }
13156
13157 // Destruction of a free block. Deferred until this point, outside of mutex
13158 // lock, for performance reason.
13159 if(pBlockToDelete != VMA_NULL)
13160 {
13161 VMA_DEBUG_LOG(" Deleted empty block");
13162 pBlockToDelete->Destroy(m_hAllocator);
13163 vma_delete(m_hAllocator, pBlockToDelete);
13164 }
13165 }
13166
13167 // OH ISSUE: VMA preAlloc
FreeReserved(const VmaAllocation hAllocation)13168 void VmaBlockVector::FreeReserved(
13169 const VmaAllocation hAllocation)
13170 {
13171 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13172
13173 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13174
13175 if(IsCorruptionDetectionEnabled())
13176 {
13177 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13178 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13179 }
13180
13181 if(hAllocation->IsPersistentMap())
13182 {
13183 pBlock->Unmap(m_hAllocator, 1);
13184 }
13185
13186 pBlock->m_pMetadata->Free(hAllocation);
13187 VMA_HEAVY_ASSERT(pBlock->Validate());
13188
13189 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13190 IncrementallySortBlocks();
13191 }
13192
CalcMaxBlockSize()13193 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13194 {
13195 VkDeviceSize result = 0;
13196 for(size_t i = m_Blocks.size(); i--; )
13197 {
13198 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13199 if(result >= m_PreferredBlockSize)
13200 {
13201 break;
13202 }
13203 }
13204 return result;
13205 }
13206
Remove(VmaDeviceMemoryBlock * pBlock)13207 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13208 {
13209 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13210 {
13211 if(m_Blocks[blockIndex] == pBlock)
13212 {
13213 VmaVectorRemove(m_Blocks, blockIndex);
13214 return;
13215 }
13216 }
13217 VMA_ASSERT(0);
13218 }
13219
IncrementallySortBlocks()13220 void VmaBlockVector::IncrementallySortBlocks()
13221 {
13222 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13223 {
13224 // Bubble sort only until first swap.
13225 for(size_t i = 1; i < m_Blocks.size(); ++i)
13226 {
13227 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13228 {
13229 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13230 return;
13231 }
13232 }
13233 }
13234 }
13235
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13236 VkResult VmaBlockVector::AllocateFromBlock(
13237 VmaDeviceMemoryBlock* pBlock,
13238 uint32_t currentFrameIndex,
13239 VkDeviceSize size,
13240 VkDeviceSize alignment,
13241 VmaAllocationCreateFlags allocFlags,
13242 void* pUserData,
13243 VmaSuballocationType suballocType,
13244 uint32_t strategy,
13245 VmaAllocation* pAllocation)
13246 {
13247 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13248 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13249 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13250 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13251
13252 VmaAllocationRequest currRequest = {};
13253 if(pBlock->m_pMetadata->CreateAllocationRequest(
13254 currentFrameIndex,
13255 m_FrameInUseCount,
13256 m_BufferImageGranularity,
13257 size,
13258 alignment,
13259 isUpperAddress,
13260 suballocType,
13261 false, // canMakeOtherLost
13262 strategy,
13263 &currRequest))
13264 {
13265 // Allocate from pCurrBlock.
13266 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13267
13268 if(mapped)
13269 {
13270 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13271 if(res != VK_SUCCESS)
13272 {
13273 return res;
13274 }
13275 }
13276
13277 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13278 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13279 UpdateHasEmptyBlock();
13280 (*pAllocation)->InitBlockAllocation(
13281 pBlock,
13282 currRequest.offset,
13283 alignment,
13284 size,
13285 m_MemoryTypeIndex,
13286 suballocType,
13287 mapped,
13288 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13289 IsNewBlockFlag());
13290 ClearNewBlockFlag();
13291 VMA_HEAVY_ASSERT(pBlock->Validate());
13292 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13293 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13294 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13295 {
13296 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13297 }
13298 if(IsCorruptionDetectionEnabled())
13299 {
13300 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13301 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13302 }
13303 return VK_SUCCESS;
13304 }
13305 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13306 }
13307
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13308 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13309 {
13310 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13311 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13312 allocInfo.allocationSize = blockSize;
13313
13314 #if VMA_BUFFER_DEVICE_ADDRESS
13315 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13316 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13317 if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13318 {
13319 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13320 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13321 }
13322 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13323
13324 VkDeviceMemory mem = VK_NULL_HANDLE;
13325 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13326 if(res < 0)
13327 {
13328 return res;
13329 }
13330
13331 // New VkDeviceMemory successfully created.
13332
13333 // Create new Allocation for it.
13334 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13335 pBlock->Init(
13336 m_hAllocator,
13337 m_hParentPool,
13338 m_MemoryTypeIndex,
13339 mem,
13340 allocInfo.allocationSize,
13341 m_NextBlockId++,
13342 m_Algorithm);
13343 m_NewBlockFlag = true;
13344
13345 m_Blocks.push_back(pBlock);
13346 if(pNewBlockIndex != VMA_NULL)
13347 {
13348 *pNewBlockIndex = m_Blocks.size() - 1;
13349 }
13350
13351 return VK_SUCCESS;
13352 }
13353
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13354 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13355 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13356 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13357 {
13358 const size_t blockCount = m_Blocks.size();
13359 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13360
13361 enum BLOCK_FLAG
13362 {
13363 BLOCK_FLAG_USED = 0x00000001,
13364 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13365 };
13366
13367 struct BlockInfo
13368 {
13369 uint32_t flags;
13370 void* pMappedData;
13371 };
13372 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13373 blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13374 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13375
13376 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13377 const size_t moveCount = moves.size();
13378 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13379 {
13380 const VmaDefragmentationMove& move = moves[moveIndex];
13381 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13382 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13383 }
13384
13385 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13386
13387 // Go over all blocks. Get mapped pointer or map if necessary.
13388 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13389 {
13390 BlockInfo& currBlockInfo = blockInfo[blockIndex];
13391 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13392 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13393 {
13394 currBlockInfo.pMappedData = pBlock->GetMappedData();
13395 // It is not originally mapped - map it.
13396 if(currBlockInfo.pMappedData == VMA_NULL)
13397 {
13398 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13399 if(pDefragCtx->res == VK_SUCCESS)
13400 {
13401 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13402 }
13403 }
13404 }
13405 }
13406
13407 // Go over all moves. Do actual data transfer.
13408 if(pDefragCtx->res == VK_SUCCESS)
13409 {
13410 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13411 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13412
13413 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13414 {
13415 const VmaDefragmentationMove& move = moves[moveIndex];
13416
13417 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13418 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13419
13420 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13421
13422 // Invalidate source.
13423 if(isNonCoherent)
13424 {
13425 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13426 memRange.memory = pSrcBlock->GetDeviceMemory();
13427 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13428 memRange.size = VMA_MIN(
13429 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13430 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13431 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13432 }
13433
13434 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13435 memmove(
13436 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13437 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13438 static_cast<size_t>(move.size));
13439
13440 if(IsCorruptionDetectionEnabled())
13441 {
13442 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13443 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13444 }
13445
13446 // Flush destination.
13447 if(isNonCoherent)
13448 {
13449 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13450 memRange.memory = pDstBlock->GetDeviceMemory();
13451 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13452 memRange.size = VMA_MIN(
13453 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13454 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13455 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13456 }
13457 }
13458 }
13459
13460 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13461 // Regardless of pCtx->res == VK_SUCCESS.
13462 for(size_t blockIndex = blockCount; blockIndex--; )
13463 {
13464 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13465 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13466 {
13467 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13468 pBlock->Unmap(m_hAllocator, 1);
13469 }
13470 }
13471 }
13472
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13473 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13474 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13475 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13476 VkCommandBuffer commandBuffer)
13477 {
13478 const size_t blockCount = m_Blocks.size();
13479
13480 pDefragCtx->blockContexts.resize(blockCount);
13481 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13482
13483 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13484 const size_t moveCount = moves.size();
13485 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13486 {
13487 const VmaDefragmentationMove& move = moves[moveIndex];
13488
13489 //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13490 {
13491 // Old school move still require us to map the whole block
13492 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13493 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13494 }
13495 }
13496
13497 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13498
13499 // Go over all blocks. Create and bind buffer for whole block if necessary.
13500 {
13501 VkBufferCreateInfo bufCreateInfo;
13502 VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13503
13504 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13505 {
13506 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13507 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13508 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13509 {
13510 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13511 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13512 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13513 if(pDefragCtx->res == VK_SUCCESS)
13514 {
13515 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13516 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13517 }
13518 }
13519 }
13520 }
13521
13522 // Go over all moves. Post data transfer commands to command buffer.
13523 if(pDefragCtx->res == VK_SUCCESS)
13524 {
13525 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13526 {
13527 const VmaDefragmentationMove& move = moves[moveIndex];
13528
13529 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13530 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13531
13532 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13533
13534 VkBufferCopy region = {
13535 move.srcOffset,
13536 move.dstOffset,
13537 move.size };
13538 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13539 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion);
13540 }
13541 }
13542
13543 // Save buffers to defrag context for later destruction.
13544 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13545 {
13546 pDefragCtx->res = VK_NOT_READY;
13547 }
13548 }
13549
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13550 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13551 {
13552 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13553 {
13554 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13555 if(pBlock->m_pMetadata->IsEmpty())
13556 {
13557 if(m_Blocks.size() > m_MinBlockCount)
13558 {
13559 if(pDefragmentationStats != VMA_NULL)
13560 {
13561 ++pDefragmentationStats->deviceMemoryBlocksFreed;
13562 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13563 }
13564
13565 VmaVectorRemove(m_Blocks, blockIndex);
13566 pBlock->Destroy(m_hAllocator);
13567 vma_delete(m_hAllocator, pBlock);
13568 }
13569 else
13570 {
13571 break;
13572 }
13573 }
13574 }
13575 UpdateHasEmptyBlock();
13576 }
13577
UpdateHasEmptyBlock()13578 void VmaBlockVector::UpdateHasEmptyBlock()
13579 {
13580 m_HasEmptyBlock = false;
13581 for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13582 {
13583 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13584 if(pBlock->m_pMetadata->IsEmpty())
13585 {
13586 m_HasEmptyBlock = true;
13587 break;
13588 }
13589 }
13590 }
13591
13592 #if VMA_STATS_STRING_ENABLED
13593
PrintDetailedMap(class VmaJsonWriter & json)13594 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13595 {
13596 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13597
13598 json.BeginObject();
13599
13600 if(IsCustomPool())
13601 {
13602 const char* poolName = m_hParentPool->GetName();
13603 if(poolName != VMA_NULL && poolName[0] != '\0')
13604 {
13605 json.WriteString("Name");
13606 json.WriteString(poolName);
13607 }
13608
13609 json.WriteString("MemoryTypeIndex");
13610 json.WriteNumber(m_MemoryTypeIndex);
13611
13612 json.WriteString("BlockSize");
13613 json.WriteNumber(m_PreferredBlockSize);
13614
13615 json.WriteString("BlockCount");
13616 json.BeginObject(true);
13617 if(m_MinBlockCount > 0)
13618 {
13619 json.WriteString("Min");
13620 json.WriteNumber((uint64_t)m_MinBlockCount);
13621 }
13622 if(m_MaxBlockCount < SIZE_MAX)
13623 {
13624 json.WriteString("Max");
13625 json.WriteNumber((uint64_t)m_MaxBlockCount);
13626 }
13627 json.WriteString("Cur");
13628 json.WriteNumber((uint64_t)m_Blocks.size());
13629 json.EndObject();
13630
13631 if(m_FrameInUseCount > 0)
13632 {
13633 json.WriteString("FrameInUseCount");
13634 json.WriteNumber(m_FrameInUseCount);
13635 }
13636
13637 if(m_Algorithm != 0)
13638 {
13639 json.WriteString("Algorithm");
13640 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13641 }
13642 }
13643 else
13644 {
13645 json.WriteString("PreferredBlockSize");
13646 json.WriteNumber(m_PreferredBlockSize);
13647 }
13648
13649 json.WriteString("Blocks");
13650 json.BeginObject();
13651 for(size_t i = 0; i < m_Blocks.size(); ++i)
13652 {
13653 json.BeginString();
13654 json.ContinueString(m_Blocks[i]->GetId());
13655 json.EndString();
13656
13657 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13658 }
13659 json.EndObject();
13660
13661 json.EndObject();
13662 }
13663
13664 #endif // #if VMA_STATS_STRING_ENABLED
13665
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13666 void VmaBlockVector::Defragment(
13667 class VmaBlockVectorDefragmentationContext* pCtx,
13668 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13669 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13670 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13671 VkCommandBuffer commandBuffer)
13672 {
13673 pCtx->res = VK_SUCCESS;
13674
13675 const VkMemoryPropertyFlags memPropFlags =
13676 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13677 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13678
13679 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13680 isHostVisible;
13681 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13682 !IsCorruptionDetectionEnabled() &&
13683 ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13684
13685 // There are options to defragment this memory type.
13686 if(canDefragmentOnCpu || canDefragmentOnGpu)
13687 {
13688 bool defragmentOnGpu;
13689 // There is only one option to defragment this memory type.
13690 if(canDefragmentOnGpu != canDefragmentOnCpu)
13691 {
13692 defragmentOnGpu = canDefragmentOnGpu;
13693 }
13694 // Both options are available: Heuristics to choose the best one.
13695 else
13696 {
13697 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13698 m_hAllocator->IsIntegratedGpu();
13699 }
13700
13701 bool overlappingMoveSupported = !defragmentOnGpu;
13702
13703 if(m_hAllocator->m_UseMutex)
13704 {
13705 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13706 {
13707 if(!m_Mutex.TryLockWrite())
13708 {
13709 pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13710 return;
13711 }
13712 }
13713 else
13714 {
13715 m_Mutex.LockWrite();
13716 pCtx->mutexLocked = true;
13717 }
13718 }
13719
13720 pCtx->Begin(overlappingMoveSupported, flags);
13721
13722 // Defragment.
13723
13724 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13725 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13726 pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13727
13728 // Accumulate statistics.
13729 if(pStats != VMA_NULL)
13730 {
13731 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13732 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13733 pStats->bytesMoved += bytesMoved;
13734 pStats->allocationsMoved += allocationsMoved;
13735 VMA_ASSERT(bytesMoved <= maxBytesToMove);
13736 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13737 if(defragmentOnGpu)
13738 {
13739 maxGpuBytesToMove -= bytesMoved;
13740 maxGpuAllocationsToMove -= allocationsMoved;
13741 }
13742 else
13743 {
13744 maxCpuBytesToMove -= bytesMoved;
13745 maxCpuAllocationsToMove -= allocationsMoved;
13746 }
13747 }
13748
13749 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13750 {
13751 if(m_hAllocator->m_UseMutex)
13752 m_Mutex.UnlockWrite();
13753
13754 if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13755 pCtx->res = VK_NOT_READY;
13756
13757 return;
13758 }
13759
13760 if(pCtx->res >= VK_SUCCESS)
13761 {
13762 if(defragmentOnGpu)
13763 {
13764 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13765 }
13766 else
13767 {
13768 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13769 }
13770 }
13771 }
13772 }
13773
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13774 void VmaBlockVector::DefragmentationEnd(
13775 class VmaBlockVectorDefragmentationContext* pCtx,
13776 uint32_t flags,
13777 VmaDefragmentationStats* pStats)
13778 {
13779 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13780 {
13781 VMA_ASSERT(pCtx->mutexLocked == false);
13782
13783 // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13784 // lock protecting us. Since we mutate state here, we have to take the lock out now
13785 m_Mutex.LockWrite();
13786 pCtx->mutexLocked = true;
13787 }
13788
13789 // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13790 if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13791 {
13792 // Destroy buffers.
13793 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13794 {
13795 VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13796 if(blockCtx.hBuffer)
13797 {
13798 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13799 }
13800 }
13801
13802 if(pCtx->res >= VK_SUCCESS)
13803 {
13804 FreeEmptyBlocks(pStats);
13805 }
13806 }
13807
13808 if(pCtx->mutexLocked)
13809 {
13810 VMA_ASSERT(m_hAllocator->m_UseMutex);
13811 m_Mutex.UnlockWrite();
13812 }
13813 }
13814
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13815 uint32_t VmaBlockVector::ProcessDefragmentations(
13816 class VmaBlockVectorDefragmentationContext *pCtx,
13817 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13818 {
13819 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13820
13821 const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13822
13823 for(uint32_t i = 0; i < moveCount; ++ i)
13824 {
13825 VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13826
13827 pMove->allocation = move.hAllocation;
13828 pMove->memory = move.pDstBlock->GetDeviceMemory();
13829 pMove->offset = move.dstOffset;
13830
13831 ++ pMove;
13832 }
13833
13834 pCtx->defragmentationMovesProcessed += moveCount;
13835
13836 return moveCount;
13837 }
13838
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13839 void VmaBlockVector::CommitDefragmentations(
13840 class VmaBlockVectorDefragmentationContext *pCtx,
13841 VmaDefragmentationStats* pStats)
13842 {
13843 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13844
13845 for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13846 {
13847 const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13848
13849 move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13850 move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13851 }
13852
13853 pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13854 FreeEmptyBlocks(pStats);
13855 }
13856
CalcAllocationCount()13857 size_t VmaBlockVector::CalcAllocationCount() const
13858 {
13859 size_t result = 0;
13860 for(size_t i = 0; i < m_Blocks.size(); ++i)
13861 {
13862 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13863 }
13864 return result;
13865 }
13866
IsBufferImageGranularityConflictPossible()13867 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13868 {
13869 if(m_BufferImageGranularity == 1)
13870 {
13871 return false;
13872 }
13873 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13874 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13875 {
13876 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13877 VMA_ASSERT(m_Algorithm == 0);
13878 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13879 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13880 {
13881 return true;
13882 }
13883 }
13884 return false;
13885 }
13886
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13887 void VmaBlockVector::MakePoolAllocationsLost(
13888 uint32_t currentFrameIndex,
13889 size_t* pLostAllocationCount)
13890 {
13891 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13892 size_t lostAllocationCount = 0;
13893 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13894 {
13895 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13896 VMA_ASSERT(pBlock);
13897 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13898 }
13899 if(pLostAllocationCount != VMA_NULL)
13900 {
13901 *pLostAllocationCount = lostAllocationCount;
13902 }
13903 }
13904
CheckCorruption()13905 VkResult VmaBlockVector::CheckCorruption()
13906 {
13907 if(!IsCorruptionDetectionEnabled())
13908 {
13909 return VK_ERROR_FEATURE_NOT_PRESENT;
13910 }
13911
13912 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13913 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13914 {
13915 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13916 VMA_ASSERT(pBlock);
13917 VkResult res = pBlock->CheckCorruption(m_hAllocator);
13918 if(res != VK_SUCCESS)
13919 {
13920 return res;
13921 }
13922 }
13923 return VK_SUCCESS;
13924 }
13925
AddStats(VmaStats * pStats)13926 void VmaBlockVector::AddStats(VmaStats* pStats)
13927 {
13928 const uint32_t memTypeIndex = m_MemoryTypeIndex;
13929 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13930
13931 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13932
13933 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13934 {
13935 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13936 VMA_ASSERT(pBlock);
13937 VMA_HEAVY_ASSERT(pBlock->Validate());
13938 VmaStatInfo allocationStatInfo;
13939 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13940 VmaAddStatInfo(pStats->total, allocationStatInfo);
13941 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13942 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13943 }
13944 }
13945
FreeEmptyBlock()13946 void VmaBlockVector::FreeEmptyBlock()
13947 {
13948 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13949 FreeEmptyBlocks(VMA_NULL);
13950 }
13951
13952 ////////////////////////////////////////////////////////////////////////////////
13953 // VmaDefragmentationAlgorithm_Generic members definition
13954
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13955 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13956 VmaAllocator hAllocator,
13957 VmaBlockVector* pBlockVector,
13958 uint32_t currentFrameIndex,
13959 bool overlappingMoveSupported) :
13960 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13961 m_AllocationCount(0),
13962 m_AllAllocations(false),
13963 m_BytesMoved(0),
13964 m_AllocationsMoved(0),
13965 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13966 {
13967 // Create block info for each block.
13968 const size_t blockCount = m_pBlockVector->m_Blocks.size();
13969 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13970 {
13971 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13972 pBlockInfo->m_OriginalBlockIndex = blockIndex;
13973 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13974 m_Blocks.push_back(pBlockInfo);
13975 }
13976
13977 // Sort them by m_pBlock pointer value.
13978 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13979 }
13980
~VmaDefragmentationAlgorithm_Generic()13981 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
13982 {
13983 for(size_t i = m_Blocks.size(); i--; )
13984 {
13985 vma_delete(m_hAllocator, m_Blocks[i]);
13986 }
13987 }
13988
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)13989 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13990 {
13991 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
13992 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
13993 {
13994 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
13995 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
13996 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
13997 {
13998 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
13999 (*it)->m_Allocations.push_back(allocInfo);
14000 }
14001 else
14002 {
14003 VMA_ASSERT(0);
14004 }
14005
14006 ++m_AllocationCount;
14007 }
14008 }
14009
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)14010 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14011 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14012 VkDeviceSize maxBytesToMove,
14013 uint32_t maxAllocationsToMove,
14014 bool freeOldAllocations)
14015 {
14016 if(m_Blocks.empty())
14017 {
14018 return VK_SUCCESS;
14019 }
14020
14021 // This is a choice based on research.
14022 // Option 1:
14023 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14024 // Option 2:
14025 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14026 // Option 3:
14027 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14028
14029 size_t srcBlockMinIndex = 0;
14030 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14031 /*
14032 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14033 {
14034 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14035 if(blocksWithNonMovableCount > 0)
14036 {
14037 srcBlockMinIndex = blocksWithNonMovableCount - 1;
14038 }
14039 }
14040 */
14041
14042 size_t srcBlockIndex = m_Blocks.size() - 1;
14043 size_t srcAllocIndex = SIZE_MAX;
14044 for(;;)
14045 {
14046 // 1. Find next allocation to move.
14047 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14048 // 1.2. Then start from last to first m_Allocations.
14049 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14050 {
14051 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14052 {
14053 // Finished: no more allocations to process.
14054 if(srcBlockIndex == srcBlockMinIndex)
14055 {
14056 return VK_SUCCESS;
14057 }
14058 else
14059 {
14060 --srcBlockIndex;
14061 srcAllocIndex = SIZE_MAX;
14062 }
14063 }
14064 else
14065 {
14066 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14067 }
14068 }
14069
14070 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14071 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14072
14073 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14074 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14075 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14076 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14077
14078 // 2. Try to find new place for this allocation in preceding or current block.
14079 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14080 {
14081 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14082 VmaAllocationRequest dstAllocRequest;
14083 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14084 m_CurrentFrameIndex,
14085 m_pBlockVector->GetFrameInUseCount(),
14086 m_pBlockVector->GetBufferImageGranularity(),
14087 size,
14088 alignment,
14089 false, // upperAddress
14090 suballocType,
14091 false, // canMakeOtherLost
14092 strategy,
14093 &dstAllocRequest) &&
14094 MoveMakesSense(
14095 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14096 {
14097 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14098
14099 // Reached limit on number of allocations or bytes to move.
14100 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14101 (m_BytesMoved + size > maxBytesToMove))
14102 {
14103 return VK_SUCCESS;
14104 }
14105
14106 VmaDefragmentationMove move = {};
14107 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14108 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14109 move.srcOffset = srcOffset;
14110 move.dstOffset = dstAllocRequest.offset;
14111 move.size = size;
14112 move.hAllocation = allocInfo.m_hAllocation;
14113 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14114 move.pDstBlock = pDstBlockInfo->m_pBlock;
14115
14116 moves.push_back(move);
14117
14118 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14119 dstAllocRequest,
14120 suballocType,
14121 size,
14122 allocInfo.m_hAllocation);
14123
14124 if(freeOldAllocations)
14125 {
14126 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14127 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14128 }
14129
14130 if(allocInfo.m_pChanged != VMA_NULL)
14131 {
14132 *allocInfo.m_pChanged = VK_TRUE;
14133 }
14134
14135 ++m_AllocationsMoved;
14136 m_BytesMoved += size;
14137
14138 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14139
14140 break;
14141 }
14142 }
14143
14144 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14145
14146 if(srcAllocIndex > 0)
14147 {
14148 --srcAllocIndex;
14149 }
14150 else
14151 {
14152 if(srcBlockIndex > 0)
14153 {
14154 --srcBlockIndex;
14155 srcAllocIndex = SIZE_MAX;
14156 }
14157 else
14158 {
14159 return VK_SUCCESS;
14160 }
14161 }
14162 }
14163 }
14164
CalcBlocksWithNonMovableCount()14165 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14166 {
14167 size_t result = 0;
14168 for(size_t i = 0; i < m_Blocks.size(); ++i)
14169 {
14170 if(m_Blocks[i]->m_HasNonMovableAllocations)
14171 {
14172 ++result;
14173 }
14174 }
14175 return result;
14176 }
14177
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14178 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14179 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14180 VkDeviceSize maxBytesToMove,
14181 uint32_t maxAllocationsToMove,
14182 VmaDefragmentationFlags flags)
14183 {
14184 if(!m_AllAllocations && m_AllocationCount == 0)
14185 {
14186 return VK_SUCCESS;
14187 }
14188
14189 const size_t blockCount = m_Blocks.size();
14190 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14191 {
14192 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14193
14194 if(m_AllAllocations)
14195 {
14196 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14197 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14198 it != pMetadata->m_Suballocations.end();
14199 ++it)
14200 {
14201 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14202 {
14203 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14204 pBlockInfo->m_Allocations.push_back(allocInfo);
14205 }
14206 }
14207 }
14208
14209 pBlockInfo->CalcHasNonMovableAllocations();
14210
14211 // This is a choice based on research.
14212 // Option 1:
14213 pBlockInfo->SortAllocationsByOffsetDescending();
14214 // Option 2:
14215 //pBlockInfo->SortAllocationsBySizeDescending();
14216 }
14217
14218 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14219 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14220
14221 // This is a choice based on research.
14222 const uint32_t roundCount = 2;
14223
14224 // Execute defragmentation rounds (the main part).
14225 VkResult result = VK_SUCCESS;
14226 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14227 {
14228 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14229 }
14230
14231 return result;
14232 }
14233
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14234 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14235 size_t dstBlockIndex, VkDeviceSize dstOffset,
14236 size_t srcBlockIndex, VkDeviceSize srcOffset)
14237 {
14238 if(dstBlockIndex < srcBlockIndex)
14239 {
14240 return true;
14241 }
14242 if(dstBlockIndex > srcBlockIndex)
14243 {
14244 return false;
14245 }
14246 if(dstOffset < srcOffset)
14247 {
14248 return true;
14249 }
14250 return false;
14251 }
14252
14253 ////////////////////////////////////////////////////////////////////////////////
14254 // VmaDefragmentationAlgorithm_Fast
14255
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14256 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14257 VmaAllocator hAllocator,
14258 VmaBlockVector* pBlockVector,
14259 uint32_t currentFrameIndex,
14260 bool overlappingMoveSupported) :
14261 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14262 m_OverlappingMoveSupported(overlappingMoveSupported),
14263 m_AllocationCount(0),
14264 m_AllAllocations(false),
14265 m_BytesMoved(0),
14266 m_AllocationsMoved(0),
14267 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14268 {
14269 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14270
14271 }
14272
~VmaDefragmentationAlgorithm_Fast()14273 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14274 {
14275 }
14276
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14277 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14278 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14279 VkDeviceSize maxBytesToMove,
14280 uint32_t maxAllocationsToMove,
14281 VmaDefragmentationFlags flags)
14282 {
14283 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14284
14285 const size_t blockCount = m_pBlockVector->GetBlockCount();
14286 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14287 {
14288 return VK_SUCCESS;
14289 }
14290
14291 PreprocessMetadata();
14292
14293 // Sort blocks in order from most destination.
14294
14295 m_BlockInfos.resize(blockCount);
14296 for(size_t i = 0; i < blockCount; ++i)
14297 {
14298 m_BlockInfos[i].origBlockIndex = i;
14299 }
14300
14301 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14302 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14303 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14304 });
14305
14306 // THE MAIN ALGORITHM
14307
14308 FreeSpaceDatabase freeSpaceDb;
14309
14310 size_t dstBlockInfoIndex = 0;
14311 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14312 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14313 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14314 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14315 VkDeviceSize dstOffset = 0;
14316
14317 bool end = false;
14318 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14319 {
14320 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14321 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14322 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14323 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14324 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14325 {
14326 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14327 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14328 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14329 if(m_AllocationsMoved == maxAllocationsToMove ||
14330 m_BytesMoved + srcAllocSize > maxBytesToMove)
14331 {
14332 end = true;
14333 break;
14334 }
14335 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14336
14337 VmaDefragmentationMove move = {};
14338 // Try to place it in one of free spaces from the database.
14339 size_t freeSpaceInfoIndex;
14340 VkDeviceSize dstAllocOffset;
14341 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14342 freeSpaceInfoIndex, dstAllocOffset))
14343 {
14344 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14345 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14346 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14347
14348 // Same block
14349 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14350 {
14351 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14352
14353 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14354
14355 VmaSuballocation suballoc = *srcSuballocIt;
14356 suballoc.offset = dstAllocOffset;
14357 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14358 m_BytesMoved += srcAllocSize;
14359 ++m_AllocationsMoved;
14360
14361 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14362 ++nextSuballocIt;
14363 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14364 srcSuballocIt = nextSuballocIt;
14365
14366 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14367
14368 move.srcBlockIndex = srcOrigBlockIndex;
14369 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14370 move.srcOffset = srcAllocOffset;
14371 move.dstOffset = dstAllocOffset;
14372 move.size = srcAllocSize;
14373
14374 moves.push_back(move);
14375 }
14376 // Different block
14377 else
14378 {
14379 // MOVE OPTION 2: Move the allocation to a different block.
14380
14381 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14382
14383 VmaSuballocation suballoc = *srcSuballocIt;
14384 suballoc.offset = dstAllocOffset;
14385 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14386 m_BytesMoved += srcAllocSize;
14387 ++m_AllocationsMoved;
14388
14389 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14390 ++nextSuballocIt;
14391 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14392 srcSuballocIt = nextSuballocIt;
14393
14394 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14395
14396 move.srcBlockIndex = srcOrigBlockIndex;
14397 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14398 move.srcOffset = srcAllocOffset;
14399 move.dstOffset = dstAllocOffset;
14400 move.size = srcAllocSize;
14401
14402 moves.push_back(move);
14403 }
14404 }
14405 else
14406 {
14407 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14408
14409 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14410 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14411 dstAllocOffset + srcAllocSize > dstBlockSize)
14412 {
14413 // But before that, register remaining free space at the end of dst block.
14414 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14415
14416 ++dstBlockInfoIndex;
14417 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14418 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14419 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14420 dstBlockSize = pDstMetadata->GetSize();
14421 dstOffset = 0;
14422 dstAllocOffset = 0;
14423 }
14424
14425 // Same block
14426 if(dstBlockInfoIndex == srcBlockInfoIndex)
14427 {
14428 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14429
14430 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14431
14432 bool skipOver = overlap;
14433 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14434 {
14435 // If destination and source place overlap, skip if it would move it
14436 // by only < 1/64 of its size.
14437 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14438 }
14439
14440 if(skipOver)
14441 {
14442 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14443
14444 dstOffset = srcAllocOffset + srcAllocSize;
14445 ++srcSuballocIt;
14446 }
14447 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14448 else
14449 {
14450 srcSuballocIt->offset = dstAllocOffset;
14451 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14452 dstOffset = dstAllocOffset + srcAllocSize;
14453 m_BytesMoved += srcAllocSize;
14454 ++m_AllocationsMoved;
14455 ++srcSuballocIt;
14456
14457 move.srcBlockIndex = srcOrigBlockIndex;
14458 move.dstBlockIndex = dstOrigBlockIndex;
14459 move.srcOffset = srcAllocOffset;
14460 move.dstOffset = dstAllocOffset;
14461 move.size = srcAllocSize;
14462
14463 moves.push_back(move);
14464 }
14465 }
14466 // Different block
14467 else
14468 {
14469 // MOVE OPTION 2: Move the allocation to a different block.
14470
14471 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14472 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14473
14474 VmaSuballocation suballoc = *srcSuballocIt;
14475 suballoc.offset = dstAllocOffset;
14476 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14477 dstOffset = dstAllocOffset + srcAllocSize;
14478 m_BytesMoved += srcAllocSize;
14479 ++m_AllocationsMoved;
14480
14481 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14482 ++nextSuballocIt;
14483 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14484 srcSuballocIt = nextSuballocIt;
14485
14486 pDstMetadata->m_Suballocations.push_back(suballoc);
14487
14488 move.srcBlockIndex = srcOrigBlockIndex;
14489 move.dstBlockIndex = dstOrigBlockIndex;
14490 move.srcOffset = srcAllocOffset;
14491 move.dstOffset = dstAllocOffset;
14492 move.size = srcAllocSize;
14493
14494 moves.push_back(move);
14495 }
14496 }
14497 }
14498 }
14499
14500 m_BlockInfos.clear();
14501
14502 PostprocessMetadata();
14503
14504 return VK_SUCCESS;
14505 }
14506
PreprocessMetadata()14507 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14508 {
14509 const size_t blockCount = m_pBlockVector->GetBlockCount();
14510 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14511 {
14512 VmaBlockMetadata_Generic* const pMetadata =
14513 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14514 pMetadata->m_FreeCount = 0;
14515 pMetadata->m_SumFreeSize = pMetadata->GetSize();
14516 pMetadata->m_FreeSuballocationsBySize.clear();
14517 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14518 it != pMetadata->m_Suballocations.end(); )
14519 {
14520 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14521 {
14522 VmaSuballocationList::iterator nextIt = it;
14523 ++nextIt;
14524 pMetadata->m_Suballocations.erase(it);
14525 it = nextIt;
14526 }
14527 else
14528 {
14529 ++it;
14530 }
14531 }
14532 }
14533 }
14534
PostprocessMetadata()14535 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14536 {
14537 const size_t blockCount = m_pBlockVector->GetBlockCount();
14538 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14539 {
14540 VmaBlockMetadata_Generic* const pMetadata =
14541 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14542 const VkDeviceSize blockSize = pMetadata->GetSize();
14543
14544 // No allocations in this block - entire area is free.
14545 if(pMetadata->m_Suballocations.empty())
14546 {
14547 pMetadata->m_FreeCount = 1;
14548 //pMetadata->m_SumFreeSize is already set to blockSize.
14549 VmaSuballocation suballoc = {
14550 0, // offset
14551 blockSize, // size
14552 VMA_NULL, // hAllocation
14553 VMA_SUBALLOCATION_TYPE_FREE };
14554 pMetadata->m_Suballocations.push_back(suballoc);
14555 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14556 }
14557 // There are some allocations in this block.
14558 else
14559 {
14560 VkDeviceSize offset = 0;
14561 VmaSuballocationList::iterator it;
14562 for(it = pMetadata->m_Suballocations.begin();
14563 it != pMetadata->m_Suballocations.end();
14564 ++it)
14565 {
14566 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14567 VMA_ASSERT(it->offset >= offset);
14568
14569 // Need to insert preceding free space.
14570 if(it->offset > offset)
14571 {
14572 ++pMetadata->m_FreeCount;
14573 const VkDeviceSize freeSize = it->offset - offset;
14574 VmaSuballocation suballoc = {
14575 offset, // offset
14576 freeSize, // size
14577 VMA_NULL, // hAllocation
14578 VMA_SUBALLOCATION_TYPE_FREE };
14579 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14580 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14581 {
14582 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14583 }
14584 }
14585
14586 pMetadata->m_SumFreeSize -= it->size;
14587 offset = it->offset + it->size;
14588 }
14589
14590 // Need to insert trailing free space.
14591 if(offset < blockSize)
14592 {
14593 ++pMetadata->m_FreeCount;
14594 const VkDeviceSize freeSize = blockSize - offset;
14595 VmaSuballocation suballoc = {
14596 offset, // offset
14597 freeSize, // size
14598 VMA_NULL, // hAllocation
14599 VMA_SUBALLOCATION_TYPE_FREE };
14600 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14601 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14602 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14603 {
14604 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14605 }
14606 }
14607
14608 VMA_SORT(
14609 pMetadata->m_FreeSuballocationsBySize.begin(),
14610 pMetadata->m_FreeSuballocationsBySize.end(),
14611 VmaSuballocationItemSizeLess());
14612 }
14613
14614 VMA_HEAVY_ASSERT(pMetadata->Validate());
14615 }
14616 }
14617
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14618 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14619 {
14620 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14621 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14622 while(it != pMetadata->m_Suballocations.end())
14623 {
14624 if(it->offset < suballoc.offset)
14625 {
14626 ++it;
14627 }
14628 }
14629 pMetadata->m_Suballocations.insert(it, suballoc);
14630 }
14631
14632 ////////////////////////////////////////////////////////////////////////////////
14633 // VmaBlockVectorDefragmentationContext
14634
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14635 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14636 VmaAllocator hAllocator,
14637 VmaPool hCustomPool,
14638 VmaBlockVector* pBlockVector,
14639 uint32_t currFrameIndex) :
14640 res(VK_SUCCESS),
14641 mutexLocked(false),
14642 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14643 defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14644 defragmentationMovesProcessed(0),
14645 defragmentationMovesCommitted(0),
14646 hasDefragmentationPlan(0),
14647 m_hAllocator(hAllocator),
14648 m_hCustomPool(hCustomPool),
14649 m_pBlockVector(pBlockVector),
14650 m_CurrFrameIndex(currFrameIndex),
14651 m_pAlgorithm(VMA_NULL),
14652 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14653 m_AllAllocations(false)
14654 {
14655 }
14656
~VmaBlockVectorDefragmentationContext()14657 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14658 {
14659 vma_delete(m_hAllocator, m_pAlgorithm);
14660 }
14661
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14662 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14663 {
14664 AllocInfo info = { hAlloc, pChanged };
14665 m_Allocations.push_back(info);
14666 }
14667
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14668 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14669 {
14670 const bool allAllocations = m_AllAllocations ||
14671 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14672
14673 /********************************
14674 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14675 ********************************/
14676
14677 /*
14678 Fast algorithm is supported only when certain criteria are met:
14679 - VMA_DEBUG_MARGIN is 0.
14680 - All allocations in this block vector are moveable.
14681 - There is no possibility of image/buffer granularity conflict.
14682 - The defragmentation is not incremental
14683 */
14684 if(VMA_DEBUG_MARGIN == 0 &&
14685 allAllocations &&
14686 !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14687 !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14688 {
14689 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14690 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14691 }
14692 else
14693 {
14694 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14695 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14696 }
14697
14698 if(allAllocations)
14699 {
14700 m_pAlgorithm->AddAll();
14701 }
14702 else
14703 {
14704 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14705 {
14706 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14707 }
14708 }
14709 }
14710
14711 ////////////////////////////////////////////////////////////////////////////////
14712 // VmaDefragmentationContext
14713
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14714 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14715 VmaAllocator hAllocator,
14716 uint32_t currFrameIndex,
14717 uint32_t flags,
14718 VmaDefragmentationStats* pStats) :
14719 m_hAllocator(hAllocator),
14720 m_CurrFrameIndex(currFrameIndex),
14721 m_Flags(flags),
14722 m_pStats(pStats),
14723 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14724 {
14725 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14726 }
14727
~VmaDefragmentationContext_T()14728 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14729 {
14730 for(size_t i = m_CustomPoolContexts.size(); i--; )
14731 {
14732 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14733 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14734 vma_delete(m_hAllocator, pBlockVectorCtx);
14735 }
14736 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14737 {
14738 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14739 if(pBlockVectorCtx)
14740 {
14741 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14742 vma_delete(m_hAllocator, pBlockVectorCtx);
14743 }
14744 }
14745 }
14746
AddPools(uint32_t poolCount,const VmaPool * pPools)14747 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14748 {
14749 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14750 {
14751 VmaPool pool = pPools[poolIndex];
14752 VMA_ASSERT(pool);
14753 // Pools with algorithm other than default are not defragmented.
14754 if(pool->m_BlockVector.GetAlgorithm() == 0)
14755 {
14756 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14757
14758 for(size_t i = m_CustomPoolContexts.size(); i--; )
14759 {
14760 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14761 {
14762 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14763 break;
14764 }
14765 }
14766
14767 if(!pBlockVectorDefragCtx)
14768 {
14769 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14770 m_hAllocator,
14771 pool,
14772 &pool->m_BlockVector,
14773 m_CurrFrameIndex);
14774 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14775 }
14776
14777 pBlockVectorDefragCtx->AddAll();
14778 }
14779 }
14780 }
14781
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14782 void VmaDefragmentationContext_T::AddAllocations(
14783 uint32_t allocationCount,
14784 const VmaAllocation* pAllocations,
14785 VkBool32* pAllocationsChanged)
14786 {
14787 // Dispatch pAllocations among defragmentators. Create them when necessary.
14788 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14789 {
14790 const VmaAllocation hAlloc = pAllocations[allocIndex];
14791 VMA_ASSERT(hAlloc);
14792 // DedicatedAlloc cannot be defragmented.
14793 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14794 // Lost allocation cannot be defragmented.
14795 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14796 {
14797 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14798
14799 const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14800 // This allocation belongs to custom pool.
14801 if(hAllocPool != VK_NULL_HANDLE)
14802 {
14803 // Pools with algorithm other than default are not defragmented.
14804 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14805 {
14806 for(size_t i = m_CustomPoolContexts.size(); i--; )
14807 {
14808 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14809 {
14810 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14811 break;
14812 }
14813 }
14814 if(!pBlockVectorDefragCtx)
14815 {
14816 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14817 m_hAllocator,
14818 hAllocPool,
14819 &hAllocPool->m_BlockVector,
14820 m_CurrFrameIndex);
14821 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14822 }
14823 }
14824 }
14825 // This allocation belongs to default pool.
14826 else
14827 {
14828 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14829 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14830 if(!pBlockVectorDefragCtx)
14831 {
14832 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14833 m_hAllocator,
14834 VMA_NULL, // hCustomPool
14835 m_hAllocator->m_pBlockVectors[memTypeIndex],
14836 m_CurrFrameIndex);
14837 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14838 }
14839 }
14840
14841 if(pBlockVectorDefragCtx)
14842 {
14843 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14844 &pAllocationsChanged[allocIndex] : VMA_NULL;
14845 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14846 }
14847 }
14848 }
14849 }
14850
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14851 VkResult VmaDefragmentationContext_T::Defragment(
14852 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14853 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14854 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14855 {
14856 if(pStats)
14857 {
14858 memset(pStats, 0, sizeof(VmaDefragmentationStats));
14859 }
14860
14861 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14862 {
14863 // For incremental defragmetnations, we just earmark how much we can move
14864 // The real meat is in the defragmentation steps
14865 m_MaxCpuBytesToMove = maxCpuBytesToMove;
14866 m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14867
14868 m_MaxGpuBytesToMove = maxGpuBytesToMove;
14869 m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14870
14871 if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14872 m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14873 return VK_SUCCESS;
14874
14875 return VK_NOT_READY;
14876 }
14877
14878 if(commandBuffer == VK_NULL_HANDLE)
14879 {
14880 maxGpuBytesToMove = 0;
14881 maxGpuAllocationsToMove = 0;
14882 }
14883
14884 VkResult res = VK_SUCCESS;
14885
14886 // Process default pools.
14887 for(uint32_t memTypeIndex = 0;
14888 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14889 ++memTypeIndex)
14890 {
14891 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14892 if(pBlockVectorCtx)
14893 {
14894 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14895 pBlockVectorCtx->GetBlockVector()->Defragment(
14896 pBlockVectorCtx,
14897 pStats, flags,
14898 maxCpuBytesToMove, maxCpuAllocationsToMove,
14899 maxGpuBytesToMove, maxGpuAllocationsToMove,
14900 commandBuffer);
14901 if(pBlockVectorCtx->res != VK_SUCCESS)
14902 {
14903 res = pBlockVectorCtx->res;
14904 }
14905 }
14906 }
14907
14908 // Process custom pools.
14909 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14910 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14911 ++customCtxIndex)
14912 {
14913 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14914 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14915 pBlockVectorCtx->GetBlockVector()->Defragment(
14916 pBlockVectorCtx,
14917 pStats, flags,
14918 maxCpuBytesToMove, maxCpuAllocationsToMove,
14919 maxGpuBytesToMove, maxGpuAllocationsToMove,
14920 commandBuffer);
14921 if(pBlockVectorCtx->res != VK_SUCCESS)
14922 {
14923 res = pBlockVectorCtx->res;
14924 }
14925 }
14926
14927 return res;
14928 }
14929
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14930 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14931 {
14932 VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14933 uint32_t movesLeft = pInfo->moveCount;
14934
14935 // Process default pools.
14936 for(uint32_t memTypeIndex = 0;
14937 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14938 ++memTypeIndex)
14939 {
14940 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14941 if(pBlockVectorCtx)
14942 {
14943 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14944
14945 if(!pBlockVectorCtx->hasDefragmentationPlan)
14946 {
14947 pBlockVectorCtx->GetBlockVector()->Defragment(
14948 pBlockVectorCtx,
14949 m_pStats, m_Flags,
14950 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14951 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14952 VK_NULL_HANDLE);
14953
14954 if(pBlockVectorCtx->res < VK_SUCCESS)
14955 continue;
14956
14957 pBlockVectorCtx->hasDefragmentationPlan = true;
14958 }
14959
14960 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14961 pBlockVectorCtx,
14962 pCurrentMove, movesLeft);
14963
14964 movesLeft -= processed;
14965 pCurrentMove += processed;
14966 }
14967 }
14968
14969 // Process custom pools.
14970 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14971 customCtxIndex < customCtxCount;
14972 ++customCtxIndex)
14973 {
14974 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14975 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14976
14977 if(!pBlockVectorCtx->hasDefragmentationPlan)
14978 {
14979 pBlockVectorCtx->GetBlockVector()->Defragment(
14980 pBlockVectorCtx,
14981 m_pStats, m_Flags,
14982 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14983 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14984 VK_NULL_HANDLE);
14985
14986 if(pBlockVectorCtx->res < VK_SUCCESS)
14987 continue;
14988
14989 pBlockVectorCtx->hasDefragmentationPlan = true;
14990 }
14991
14992 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14993 pBlockVectorCtx,
14994 pCurrentMove, movesLeft);
14995
14996 movesLeft -= processed;
14997 pCurrentMove += processed;
14998 }
14999
15000 pInfo->moveCount = pInfo->moveCount - movesLeft;
15001
15002 return VK_SUCCESS;
15003 }
DefragmentPassEnd()15004 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15005 {
15006 VkResult res = VK_SUCCESS;
15007
15008 // Process default pools.
15009 for(uint32_t memTypeIndex = 0;
15010 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15011 ++memTypeIndex)
15012 {
15013 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15014 if(pBlockVectorCtx)
15015 {
15016 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15017
15018 if(!pBlockVectorCtx->hasDefragmentationPlan)
15019 {
15020 res = VK_NOT_READY;
15021 continue;
15022 }
15023
15024 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15025 pBlockVectorCtx, m_pStats);
15026
15027 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15028 res = VK_NOT_READY;
15029 }
15030 }
15031
15032 // Process custom pools.
15033 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15034 customCtxIndex < customCtxCount;
15035 ++customCtxIndex)
15036 {
15037 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15038 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15039
15040 if(!pBlockVectorCtx->hasDefragmentationPlan)
15041 {
15042 res = VK_NOT_READY;
15043 continue;
15044 }
15045
15046 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15047 pBlockVectorCtx, m_pStats);
15048
15049 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15050 res = VK_NOT_READY;
15051 }
15052
15053 return res;
15054 }
15055
15056 ////////////////////////////////////////////////////////////////////////////////
15057 // VmaRecorder
15058
15059 #if VMA_RECORDING_ENABLED
15060
VmaRecorder()15061 VmaRecorder::VmaRecorder() :
15062 m_UseMutex(true),
15063 m_Flags(0),
15064 m_File(VMA_NULL),
15065 m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15066 {
15067 }
15068
Init(const VmaRecordSettings & settings,bool useMutex)15069 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15070 {
15071 m_UseMutex = useMutex;
15072 m_Flags = settings.flags;
15073
15074 #if defined(_WIN32)
15075 // Open file for writing.
15076 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15077
15078 if(err != 0)
15079 {
15080 return VK_ERROR_INITIALIZATION_FAILED;
15081 }
15082 #else
15083 // Open file for writing.
15084 m_File = fopen(settings.pFilePath, "wb");
15085
15086 if(m_File == 0)
15087 {
15088 return VK_ERROR_INITIALIZATION_FAILED;
15089 }
15090 #endif
15091
15092 // Write header.
15093 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15094 fprintf(m_File, "%s\n", "1,8");
15095
15096 return VK_SUCCESS;
15097 }
15098
~VmaRecorder()15099 VmaRecorder::~VmaRecorder()
15100 {
15101 if(m_File != VMA_NULL)
15102 {
15103 fclose(m_File);
15104 }
15105 }
15106
RecordCreateAllocator(uint32_t frameIndex)15107 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15108 {
15109 CallParams callParams;
15110 GetBasicParams(callParams);
15111
15112 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15113 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15114 Flush();
15115 }
15116
RecordDestroyAllocator(uint32_t frameIndex)15117 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15118 {
15119 CallParams callParams;
15120 GetBasicParams(callParams);
15121
15122 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15123 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15124 Flush();
15125 }
15126
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)15127 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15128 {
15129 CallParams callParams;
15130 GetBasicParams(callParams);
15131
15132 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15133 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15134 createInfo.memoryTypeIndex,
15135 createInfo.flags,
15136 createInfo.blockSize,
15137 (uint64_t)createInfo.minBlockCount,
15138 (uint64_t)createInfo.maxBlockCount,
15139 createInfo.frameInUseCount,
15140 pool);
15141 Flush();
15142 }
15143
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)15144 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15145 {
15146 CallParams callParams;
15147 GetBasicParams(callParams);
15148
15149 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15150 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15151 pool);
15152 Flush();
15153 }
15154
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15155 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15156 const VkMemoryRequirements& vkMemReq,
15157 const VmaAllocationCreateInfo& createInfo,
15158 VmaAllocation allocation)
15159 {
15160 CallParams callParams;
15161 GetBasicParams(callParams);
15162
15163 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15164 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15165 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15166 vkMemReq.size,
15167 vkMemReq.alignment,
15168 vkMemReq.memoryTypeBits,
15169 createInfo.flags,
15170 createInfo.usage,
15171 createInfo.requiredFlags,
15172 createInfo.preferredFlags,
15173 createInfo.memoryTypeBits,
15174 createInfo.pool,
15175 allocation,
15176 userDataStr.GetString());
15177 Flush();
15178 }
15179
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)15180 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15181 const VkMemoryRequirements& vkMemReq,
15182 const VmaAllocationCreateInfo& createInfo,
15183 uint64_t allocationCount,
15184 const VmaAllocation* pAllocations)
15185 {
15186 CallParams callParams;
15187 GetBasicParams(callParams);
15188
15189 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15190 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15191 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15192 vkMemReq.size,
15193 vkMemReq.alignment,
15194 vkMemReq.memoryTypeBits,
15195 createInfo.flags,
15196 createInfo.usage,
15197 createInfo.requiredFlags,
15198 createInfo.preferredFlags,
15199 createInfo.memoryTypeBits,
15200 createInfo.pool);
15201 PrintPointerList(allocationCount, pAllocations);
15202 fprintf(m_File, ",%s\n", userDataStr.GetString());
15203 Flush();
15204 }
15205
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15206 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15207 const VkMemoryRequirements& vkMemReq,
15208 bool requiresDedicatedAllocation,
15209 bool prefersDedicatedAllocation,
15210 const VmaAllocationCreateInfo& createInfo,
15211 VmaAllocation allocation)
15212 {
15213 CallParams callParams;
15214 GetBasicParams(callParams);
15215
15216 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15217 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15218 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,
15219 vkMemReq.size,
15220 vkMemReq.alignment,
15221 vkMemReq.memoryTypeBits,
15222 requiresDedicatedAllocation ? 1 : 0,
15223 prefersDedicatedAllocation ? 1 : 0,
15224 createInfo.flags,
15225 createInfo.usage,
15226 createInfo.requiredFlags,
15227 createInfo.preferredFlags,
15228 createInfo.memoryTypeBits,
15229 createInfo.pool,
15230 allocation,
15231 userDataStr.GetString());
15232 Flush();
15233 }
15234
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15235 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15236 const VkMemoryRequirements& vkMemReq,
15237 bool requiresDedicatedAllocation,
15238 bool prefersDedicatedAllocation,
15239 const VmaAllocationCreateInfo& createInfo,
15240 VmaAllocation allocation)
15241 {
15242 CallParams callParams;
15243 GetBasicParams(callParams);
15244
15245 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15246 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15247 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,
15248 vkMemReq.size,
15249 vkMemReq.alignment,
15250 vkMemReq.memoryTypeBits,
15251 requiresDedicatedAllocation ? 1 : 0,
15252 prefersDedicatedAllocation ? 1 : 0,
15253 createInfo.flags,
15254 createInfo.usage,
15255 createInfo.requiredFlags,
15256 createInfo.preferredFlags,
15257 createInfo.memoryTypeBits,
15258 createInfo.pool,
15259 allocation,
15260 userDataStr.GetString());
15261 Flush();
15262 }
15263
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15264 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15265 VmaAllocation allocation)
15266 {
15267 CallParams callParams;
15268 GetBasicParams(callParams);
15269
15270 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15271 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15272 allocation);
15273 Flush();
15274 }
15275
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15276 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15277 uint64_t allocationCount,
15278 const VmaAllocation* pAllocations)
15279 {
15280 CallParams callParams;
15281 GetBasicParams(callParams);
15282
15283 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15284 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15285 PrintPointerList(allocationCount, pAllocations);
15286 fprintf(m_File, "\n");
15287 Flush();
15288 }
15289
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15290 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15291 VmaAllocation allocation,
15292 const void* pUserData)
15293 {
15294 CallParams callParams;
15295 GetBasicParams(callParams);
15296
15297 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15298 UserDataString userDataStr(
15299 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15300 pUserData);
15301 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15302 allocation,
15303 userDataStr.GetString());
15304 Flush();
15305 }
15306
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15307 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15308 VmaAllocation allocation)
15309 {
15310 CallParams callParams;
15311 GetBasicParams(callParams);
15312
15313 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15314 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15315 allocation);
15316 Flush();
15317 }
15318
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15319 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15320 VmaAllocation allocation)
15321 {
15322 CallParams callParams;
15323 GetBasicParams(callParams);
15324
15325 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15326 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15327 allocation);
15328 Flush();
15329 }
15330
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15331 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15332 VmaAllocation allocation)
15333 {
15334 CallParams callParams;
15335 GetBasicParams(callParams);
15336
15337 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15338 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15339 allocation);
15340 Flush();
15341 }
15342
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15343 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15344 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15345 {
15346 CallParams callParams;
15347 GetBasicParams(callParams);
15348
15349 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15350 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15351 allocation,
15352 offset,
15353 size);
15354 Flush();
15355 }
15356
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15357 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15358 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15359 {
15360 CallParams callParams;
15361 GetBasicParams(callParams);
15362
15363 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15364 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15365 allocation,
15366 offset,
15367 size);
15368 Flush();
15369 }
15370
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15371 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15372 const VkBufferCreateInfo& bufCreateInfo,
15373 const VmaAllocationCreateInfo& allocCreateInfo,
15374 VmaAllocation allocation)
15375 {
15376 CallParams callParams;
15377 GetBasicParams(callParams);
15378
15379 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15380 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15381 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,
15382 bufCreateInfo.flags,
15383 bufCreateInfo.size,
15384 bufCreateInfo.usage,
15385 bufCreateInfo.sharingMode,
15386 allocCreateInfo.flags,
15387 allocCreateInfo.usage,
15388 allocCreateInfo.requiredFlags,
15389 allocCreateInfo.preferredFlags,
15390 allocCreateInfo.memoryTypeBits,
15391 allocCreateInfo.pool,
15392 allocation,
15393 userDataStr.GetString());
15394 Flush();
15395 }
15396
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15397 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15398 const VkImageCreateInfo& imageCreateInfo,
15399 const VmaAllocationCreateInfo& allocCreateInfo,
15400 VmaAllocation allocation)
15401 {
15402 CallParams callParams;
15403 GetBasicParams(callParams);
15404
15405 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15406 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15407 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,
15408 imageCreateInfo.flags,
15409 imageCreateInfo.imageType,
15410 imageCreateInfo.format,
15411 imageCreateInfo.extent.width,
15412 imageCreateInfo.extent.height,
15413 imageCreateInfo.extent.depth,
15414 imageCreateInfo.mipLevels,
15415 imageCreateInfo.arrayLayers,
15416 imageCreateInfo.samples,
15417 imageCreateInfo.tiling,
15418 imageCreateInfo.usage,
15419 imageCreateInfo.sharingMode,
15420 imageCreateInfo.initialLayout,
15421 allocCreateInfo.flags,
15422 allocCreateInfo.usage,
15423 allocCreateInfo.requiredFlags,
15424 allocCreateInfo.preferredFlags,
15425 allocCreateInfo.memoryTypeBits,
15426 allocCreateInfo.pool,
15427 allocation,
15428 userDataStr.GetString());
15429 Flush();
15430 }
15431
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15432 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15433 VmaAllocation allocation)
15434 {
15435 CallParams callParams;
15436 GetBasicParams(callParams);
15437
15438 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15439 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15440 allocation);
15441 Flush();
15442 }
15443
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15444 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15445 VmaAllocation allocation)
15446 {
15447 CallParams callParams;
15448 GetBasicParams(callParams);
15449
15450 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15451 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15452 allocation);
15453 Flush();
15454 }
15455
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15456 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15457 VmaAllocation allocation)
15458 {
15459 CallParams callParams;
15460 GetBasicParams(callParams);
15461
15462 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15463 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15464 allocation);
15465 Flush();
15466 }
15467
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15468 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15469 VmaAllocation allocation)
15470 {
15471 CallParams callParams;
15472 GetBasicParams(callParams);
15473
15474 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15475 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15476 allocation);
15477 Flush();
15478 }
15479
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15480 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15481 VmaPool pool)
15482 {
15483 CallParams callParams;
15484 GetBasicParams(callParams);
15485
15486 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15487 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15488 pool);
15489 Flush();
15490 }
15491
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15492 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15493 const VmaDefragmentationInfo2& info,
15494 VmaDefragmentationContext ctx)
15495 {
15496 CallParams callParams;
15497 GetBasicParams(callParams);
15498
15499 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15500 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15501 info.flags);
15502 PrintPointerList(info.allocationCount, info.pAllocations);
15503 fprintf(m_File, ",");
15504 PrintPointerList(info.poolCount, info.pPools);
15505 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15506 info.maxCpuBytesToMove,
15507 info.maxCpuAllocationsToMove,
15508 info.maxGpuBytesToMove,
15509 info.maxGpuAllocationsToMove,
15510 info.commandBuffer,
15511 ctx);
15512 Flush();
15513 }
15514
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15515 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15516 VmaDefragmentationContext ctx)
15517 {
15518 CallParams callParams;
15519 GetBasicParams(callParams);
15520
15521 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15522 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15523 ctx);
15524 Flush();
15525 }
15526
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15527 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15528 VmaPool pool,
15529 const char* name)
15530 {
15531 CallParams callParams;
15532 GetBasicParams(callParams);
15533
15534 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15535 fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15536 pool, name != VMA_NULL ? name : "");
15537 Flush();
15538 }
15539
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15540 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15541 {
15542 if(pUserData != VMA_NULL)
15543 {
15544 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15545 {
15546 m_Str = (const char*)pUserData;
15547 }
15548 else
15549 {
15550 // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15551 snprintf(m_PtrStr, 17, "%p", pUserData);
15552 m_Str = m_PtrStr;
15553 }
15554 }
15555 else
15556 {
15557 m_Str = "";
15558 }
15559 }
15560
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15561 void VmaRecorder::WriteConfiguration(
15562 const VkPhysicalDeviceProperties& devProps,
15563 const VkPhysicalDeviceMemoryProperties& memProps,
15564 uint32_t vulkanApiVersion,
15565 bool dedicatedAllocationExtensionEnabled,
15566 bool bindMemory2ExtensionEnabled,
15567 bool memoryBudgetExtensionEnabled,
15568 bool deviceCoherentMemoryExtensionEnabled)
15569 {
15570 fprintf(m_File, "Config,Begin\n");
15571
15572 fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15573
15574 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15575 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15576 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15577 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15578 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15579 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15580
15581 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15582 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15583 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15584
15585 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15586 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15587 {
15588 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15589 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15590 }
15591 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15592 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15593 {
15594 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15595 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15596 }
15597
15598 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15599 fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15600 fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15601 fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15602
15603 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15604 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15605 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15606 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15607 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15608 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15609 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15610 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15611 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15612
15613 fprintf(m_File, "Config,End\n");
15614 }
15615
GetBasicParams(CallParams & outParams)15616 void VmaRecorder::GetBasicParams(CallParams& outParams)
15617 {
15618 #if defined(_WIN32)
15619 outParams.threadId = GetCurrentThreadId();
15620 #else
15621 // Use C++11 features to get thread id and convert it to uint32_t.
15622 // There is room for optimization since sstream is quite slow.
15623 // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15624 std::thread::id thread_id = std::this_thread::get_id();
15625 stringstream thread_id_to_string_converter;
15626 thread_id_to_string_converter << thread_id;
15627 string thread_id_as_string = thread_id_to_string_converter.str();
15628 outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15629 #endif
15630
15631 auto current_time = std::chrono::high_resolution_clock::now();
15632
15633 outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15634 }
15635
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15636 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15637 {
15638 if(count)
15639 {
15640 fprintf(m_File, "%p", pItems[0]);
15641 for(uint64_t i = 1; i < count; ++i)
15642 {
15643 fprintf(m_File, " %p", pItems[i]);
15644 }
15645 }
15646 }
15647
Flush()15648 void VmaRecorder::Flush()
15649 {
15650 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15651 {
15652 fflush(m_File);
15653 }
15654 }
15655
15656 #endif // #if VMA_RECORDING_ENABLED
15657
15658 ////////////////////////////////////////////////////////////////////////////////
15659 // VmaAllocationObjectAllocator
15660
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15661 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15662 m_Allocator(pAllocationCallbacks, 1024)
15663 {
15664 }
15665
Allocate(Types...args)15666 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15667 {
15668 VmaMutexLock mutexLock(m_Mutex);
15669 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15670 }
15671
Free(VmaAllocation hAlloc)15672 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15673 {
15674 VmaMutexLock mutexLock(m_Mutex);
15675 m_Allocator.Free(hAlloc);
15676 }
15677
15678 ////////////////////////////////////////////////////////////////////////////////
15679 // VmaAllocator_T
15680
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15681 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15682 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15683 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15684 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15685 m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15686 m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15687 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15688 m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15689 m_hDevice(pCreateInfo->device),
15690 m_hInstance(pCreateInfo->instance),
15691 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15692 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15693 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15694 m_AllocationObjectAllocator(&m_AllocationCallbacks),
15695 m_HeapSizeLimitMask(0),
15696 m_PreferredLargeHeapBlockSize(0),
15697 m_PhysicalDevice(pCreateInfo->physicalDevice),
15698 m_CurrentFrameIndex(0),
15699 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15700 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15701 m_NextPoolId(0),
15702 m_GlobalMemoryTypeBits(UINT32_MAX)
15703 #if VMA_RECORDING_ENABLED
15704 ,m_pRecorder(VMA_NULL)
15705 #endif
15706 {
15707 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15708 {
15709 m_UseKhrDedicatedAllocation = false;
15710 m_UseKhrBindMemory2 = false;
15711 }
15712
15713 if(VMA_DEBUG_DETECT_CORRUPTION)
15714 {
15715 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15716 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15717 }
15718
15719 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15720
15721 if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15722 {
15723 #if !(VMA_DEDICATED_ALLOCATION)
15724 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15725 {
15726 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15727 }
15728 #endif
15729 #if !(VMA_BIND_MEMORY2)
15730 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15731 {
15732 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15733 }
15734 #endif
15735 }
15736 #if !(VMA_MEMORY_BUDGET)
15737 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15738 {
15739 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15740 }
15741 #endif
15742 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15743 if(m_UseKhrBufferDeviceAddress)
15744 {
15745 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.");
15746 }
15747 #endif
15748 #if VMA_VULKAN_VERSION < 1002000
15749 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15750 {
15751 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15752 }
15753 #endif
15754 #if VMA_VULKAN_VERSION < 1001000
15755 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15756 {
15757 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15758 }
15759 #endif
15760
15761 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15762 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15763 memset(&m_MemProps, 0, sizeof(m_MemProps));
15764
15765 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15766 memset(&m_pReservedBlockVectors, 0, sizeof(m_pReservedBlockVectors));
15767 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15768 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15769
15770 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15771 {
15772 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15773 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15774 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15775 }
15776
15777 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15778
15779 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15780 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15781
15782 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15783 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15784 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15785 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15786
15787 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15788 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15789
15790 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15791
15792 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15793 {
15794 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15795 {
15796 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15797 if(limit != VK_WHOLE_SIZE)
15798 {
15799 m_HeapSizeLimitMask |= 1u << heapIndex;
15800 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15801 {
15802 m_MemProps.memoryHeaps[heapIndex].size = limit;
15803 }
15804 }
15805 }
15806 }
15807
15808 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15809 {
15810 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15811
15812 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15813 this,
15814 VK_NULL_HANDLE, // hParentPool
15815 memTypeIndex,
15816 preferredBlockSize,
15817 0,
15818 pCreateInfo->maxBlockCount,
15819 GetBufferImageGranularity(),
15820 pCreateInfo->frameInUseCount,
15821 false, // explicitBlockSize
15822 false); // linearAlgorithm
15823 m_pReservedBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15824 this,
15825 VK_NULL_HANDLE, // hParentPool
15826 memTypeIndex,
15827 preferredBlockSize,
15828 0,
15829 1, // max block count 1
15830 GetBufferImageGranularity(),
15831 pCreateInfo->frameInUseCount,
15832 false, // explicitBlockSize
15833 false); // linearAlgorithm
15834 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15835 // becase minBlockCount is 0.
15836 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15837
15838 }
15839 }
15840
Init(const VmaAllocatorCreateInfo * pCreateInfo)15841 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15842 {
15843 VkResult res = VK_SUCCESS;
15844
15845 if(pCreateInfo->pRecordSettings != VMA_NULL &&
15846 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15847 {
15848 #if VMA_RECORDING_ENABLED
15849 m_pRecorder = vma_new(this, VmaRecorder)();
15850 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15851 if(res != VK_SUCCESS)
15852 {
15853 return res;
15854 }
15855 m_pRecorder->WriteConfiguration(
15856 m_PhysicalDeviceProperties,
15857 m_MemProps,
15858 m_VulkanApiVersion,
15859 m_UseKhrDedicatedAllocation,
15860 m_UseKhrBindMemory2,
15861 m_UseExtMemoryBudget,
15862 m_UseAmdDeviceCoherentMemory);
15863 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15864 #else
15865 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15866 return VK_ERROR_FEATURE_NOT_PRESENT;
15867 #endif
15868 }
15869
15870 #if VMA_MEMORY_BUDGET
15871 if(m_UseExtMemoryBudget)
15872 {
15873 UpdateVulkanBudget();
15874 }
15875 #endif // #if VMA_MEMORY_BUDGET
15876
15877 return res;
15878 }
15879
~VmaAllocator_T()15880 VmaAllocator_T::~VmaAllocator_T()
15881 {
15882 #if VMA_RECORDING_ENABLED
15883 if(m_pRecorder != VMA_NULL)
15884 {
15885 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15886 vma_delete(this, m_pRecorder);
15887 }
15888 #endif
15889
15890 VMA_ASSERT(m_Pools.empty());
15891
15892 for(size_t i = GetMemoryTypeCount(); i--; )
15893 {
15894 if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15895 {
15896 VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15897 }
15898
15899 vma_delete(this, m_pDedicatedAllocations[i]);
15900 vma_delete(this, m_pBlockVectors[i]);
15901 vma_delete(this, m_pReservedBlockVectors[i]);
15902 }
15903 }
15904
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15905 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15906 {
15907 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15908 ImportVulkanFunctions_Static();
15909 #endif
15910
15911 if(pVulkanFunctions != VMA_NULL)
15912 {
15913 ImportVulkanFunctions_Custom(pVulkanFunctions);
15914 }
15915
15916 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15917 ImportVulkanFunctions_Dynamic();
15918 #endif
15919
15920 ValidateVulkanFunctions();
15921 }
15922
15923 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15924
ImportVulkanFunctions_Static()15925 void VmaAllocator_T::ImportVulkanFunctions_Static()
15926 {
15927 // Vulkan 1.0
15928 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15929 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15930 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15931 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15932 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15933 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15934 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15935 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15936 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15937 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15938 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15939 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15940 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15941 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15942 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15943 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15944 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15945
15946 // Vulkan 1.1
15947 #if VMA_VULKAN_VERSION >= 1001000
15948 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15949 {
15950 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15951 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15952 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15953 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15954 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15955 }
15956 #endif
15957 }
15958
15959 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15960
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15961 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15962 {
15963 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15964
15965 #define VMA_COPY_IF_NOT_NULL(funcName) \
15966 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15967
15968 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15969 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15970 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15971 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15972 VMA_COPY_IF_NOT_NULL(vkMapMemory);
15973 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15974 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15975 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15976 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15977 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15978 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15979 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15980 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
15981 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
15982 VMA_COPY_IF_NOT_NULL(vkCreateImage);
15983 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
15984 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
15985
15986 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
15987 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
15988 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
15989 #endif
15990
15991 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
15992 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
15993 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
15994 #endif
15995
15996 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
15997 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
15998 #endif
15999
16000 #undef VMA_COPY_IF_NOT_NULL
16001 }
16002
16003 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16004
ImportVulkanFunctions_Dynamic()16005 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16006 {
16007 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16008 if(m_VulkanFunctions.memberName == VMA_NULL) \
16009 m_VulkanFunctions.memberName = \
16010 (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16011 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16012 if(m_VulkanFunctions.memberName == VMA_NULL) \
16013 m_VulkanFunctions.memberName = \
16014 (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16015
16016 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16017 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16018 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16019 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16020 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16021 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16022 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16023 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16024 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16025 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16026 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16027 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16028 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16029 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16030 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16031 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16032 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16033
16034 #if VMA_DEDICATED_ALLOCATION
16035 if(m_UseKhrDedicatedAllocation)
16036 {
16037 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16038 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16039 }
16040 #endif
16041
16042 #if VMA_BIND_MEMORY2
16043 if(m_UseKhrBindMemory2)
16044 {
16045 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16046 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16047 }
16048 #endif // #if VMA_BIND_MEMORY2
16049
16050 #if VMA_MEMORY_BUDGET
16051 if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16052 {
16053 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16054 }
16055 #endif // #if VMA_MEMORY_BUDGET
16056
16057 #undef VMA_FETCH_DEVICE_FUNC
16058 #undef VMA_FETCH_INSTANCE_FUNC
16059 }
16060
16061 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16062
ValidateVulkanFunctions()16063 void VmaAllocator_T::ValidateVulkanFunctions()
16064 {
16065 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16066 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16067 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16068 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16069 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16070 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16071 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16072 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16073 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16074 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16075 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16076 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16077 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16078 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16079 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16080 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16081 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16082
16083 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16084 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16085 {
16086 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16087 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16088 }
16089 #endif
16090
16091 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16092 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16093 {
16094 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16095 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16096 }
16097 #endif
16098
16099 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16100 if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16101 {
16102 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16103 }
16104 #endif
16105 }
16106
CalcPreferredBlockSize(uint32_t memTypeIndex)16107 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16108 {
16109 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16110 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16111 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16112 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16113 }
16114
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)16115 VkResult VmaAllocator_T::AllocateMemoryOfType(
16116 VkDeviceSize size,
16117 VkDeviceSize alignment,
16118 bool dedicatedAllocation,
16119 VkBuffer dedicatedBuffer,
16120 VkBufferUsageFlags dedicatedBufferUsage,
16121 VkImage dedicatedImage,
16122 const VmaAllocationCreateInfo& createInfo,
16123 uint32_t memTypeIndex,
16124 VmaSuballocationType suballocType,
16125 size_t allocationCount,
16126 VmaAllocation* pAllocations)
16127 {
16128 VMA_ASSERT(pAllocations != VMA_NULL);
16129 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16130
16131 VmaAllocationCreateInfo finalCreateInfo = createInfo;
16132
16133 // If memory type is not HOST_VISIBLE, disable MAPPED.
16134 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16135 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16136 {
16137 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16138 }
16139 // If memory is lazily allocated, it should be always dedicated.
16140 if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16141 {
16142 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16143 }
16144
16145 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16146 VMA_ASSERT(blockVector);
16147
16148 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16149 bool preferDedicatedMemory =
16150 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16151 dedicatedAllocation ||
16152 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16153 size > preferredBlockSize / 2;
16154
16155 if(preferDedicatedMemory &&
16156 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16157 finalCreateInfo.pool == VK_NULL_HANDLE)
16158 {
16159 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16160 }
16161
16162 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16163 {
16164 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16165 {
16166 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16167 }
16168 else
16169 {
16170 return AllocateDedicatedMemory(
16171 size,
16172 suballocType,
16173 memTypeIndex,
16174 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16175 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16176 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16177 finalCreateInfo.pUserData,
16178 dedicatedBuffer,
16179 dedicatedBufferUsage,
16180 dedicatedImage,
16181 allocationCount,
16182 pAllocations);
16183 }
16184 }
16185 else
16186 {
16187 VkResult res = blockVector->Allocate(
16188 m_CurrentFrameIndex.load(),
16189 size,
16190 alignment,
16191 finalCreateInfo,
16192 suballocType,
16193 allocationCount,
16194 pAllocations);
16195 if(res == VK_SUCCESS)
16196 {
16197 return res;
16198 }
16199
16200 // 5. Try dedicated memory.
16201 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16202 {
16203 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16204 }
16205 else
16206 {
16207 res = AllocateDedicatedMemory(
16208 size,
16209 suballocType,
16210 memTypeIndex,
16211 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16212 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16213 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16214 finalCreateInfo.pUserData,
16215 dedicatedBuffer,
16216 dedicatedBufferUsage,
16217 dedicatedImage,
16218 allocationCount,
16219 pAllocations);
16220 if(res == VK_SUCCESS)
16221 {
16222 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16223 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16224 return VK_SUCCESS;
16225 }
16226 else
16227 {
16228 // Everything failed: Return error code.
16229 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16230 return res;
16231 }
16232 }
16233 }
16234 }
16235
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)16236 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16237 VkDeviceSize size,
16238 VmaSuballocationType suballocType,
16239 uint32_t memTypeIndex,
16240 bool withinBudget,
16241 bool map,
16242 bool isUserDataString,
16243 void* pUserData,
16244 VkBuffer dedicatedBuffer,
16245 VkBufferUsageFlags dedicatedBufferUsage,
16246 VkImage dedicatedImage,
16247 size_t allocationCount,
16248 VmaAllocation* pAllocations)
16249 {
16250 VMA_ASSERT(allocationCount > 0 && pAllocations);
16251
16252 if(withinBudget)
16253 {
16254 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16255 VmaBudget heapBudget = {};
16256 GetBudget(&heapBudget, heapIndex, 1);
16257 if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16258 {
16259 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16260 }
16261 }
16262
16263 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16264 allocInfo.memoryTypeIndex = memTypeIndex;
16265 allocInfo.allocationSize = size;
16266
16267 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16268 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16269 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16270 {
16271 if(dedicatedBuffer != VK_NULL_HANDLE)
16272 {
16273 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16274 dedicatedAllocInfo.buffer = dedicatedBuffer;
16275 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16276 }
16277 else if(dedicatedImage != VK_NULL_HANDLE)
16278 {
16279 dedicatedAllocInfo.image = dedicatedImage;
16280 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16281 }
16282 }
16283 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16284
16285 #if VMA_BUFFER_DEVICE_ADDRESS
16286 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16287 if(m_UseKhrBufferDeviceAddress)
16288 {
16289 bool canContainBufferWithDeviceAddress = true;
16290 if(dedicatedBuffer != VK_NULL_HANDLE)
16291 {
16292 canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16293 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16294 }
16295 else if(dedicatedImage != VK_NULL_HANDLE)
16296 {
16297 canContainBufferWithDeviceAddress = false;
16298 }
16299 if(canContainBufferWithDeviceAddress)
16300 {
16301 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16302 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16303 }
16304 }
16305 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16306
16307 size_t allocIndex;
16308 VkResult res = VK_SUCCESS;
16309 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16310 {
16311 res = AllocateDedicatedMemoryPage(
16312 size,
16313 suballocType,
16314 memTypeIndex,
16315 allocInfo,
16316 map,
16317 isUserDataString,
16318 pUserData,
16319 pAllocations + allocIndex);
16320 if(res != VK_SUCCESS)
16321 {
16322 break;
16323 }
16324 }
16325
16326 if(res == VK_SUCCESS)
16327 {
16328 // Register them in m_pDedicatedAllocations.
16329 {
16330 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16331 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16332 VMA_ASSERT(pDedicatedAllocations);
16333 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16334 {
16335 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16336 }
16337 }
16338
16339 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16340 }
16341 else
16342 {
16343 // Free all already created allocations.
16344 while(allocIndex--)
16345 {
16346 VmaAllocation currAlloc = pAllocations[allocIndex];
16347 VkDeviceMemory hMemory = currAlloc->GetMemory();
16348
16349 /*
16350 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16351 before vkFreeMemory.
16352
16353 if(currAlloc->GetMappedData() != VMA_NULL)
16354 {
16355 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16356 }
16357 */
16358
16359 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16360 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16361 currAlloc->SetUserData(this, VMA_NULL);
16362 m_AllocationObjectAllocator.Free(currAlloc);
16363 }
16364
16365 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16366 }
16367
16368 return res;
16369 }
16370
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16371 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16372 VkDeviceSize size,
16373 VmaSuballocationType suballocType,
16374 uint32_t memTypeIndex,
16375 const VkMemoryAllocateInfo& allocInfo,
16376 bool map,
16377 bool isUserDataString,
16378 void* pUserData,
16379 VmaAllocation* pAllocation)
16380 {
16381 VkDeviceMemory hMemory = VK_NULL_HANDLE;
16382 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16383 if(res < 0)
16384 {
16385 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16386 return res;
16387 }
16388
16389 void* pMappedData = VMA_NULL;
16390 if(map)
16391 {
16392 res = (*m_VulkanFunctions.vkMapMemory)(
16393 m_hDevice,
16394 hMemory,
16395 0,
16396 VK_WHOLE_SIZE,
16397 0,
16398 &pMappedData);
16399 if(res < 0)
16400 {
16401 VMA_DEBUG_LOG(" vkMapMemory FAILED");
16402 FreeVulkanMemory(memTypeIndex, size, hMemory);
16403 return res;
16404 }
16405 }
16406
16407 *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16408 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16409 (*pAllocation)->SetUserData(this, pUserData);
16410 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16411 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16412 {
16413 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16414 }
16415
16416 return VK_SUCCESS;
16417 }
16418
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16419 void VmaAllocator_T::GetBufferMemoryRequirements(
16420 VkBuffer hBuffer,
16421 VkMemoryRequirements& memReq,
16422 bool& requiresDedicatedAllocation,
16423 bool& prefersDedicatedAllocation) const
16424 {
16425 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16426 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16427 {
16428 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16429 memReqInfo.buffer = hBuffer;
16430
16431 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16432
16433 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16434 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16435
16436 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16437
16438 memReq = memReq2.memoryRequirements;
16439 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16440 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16441 }
16442 else
16443 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16444 {
16445 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16446 requiresDedicatedAllocation = false;
16447 prefersDedicatedAllocation = false;
16448 }
16449 }
16450
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16451 void VmaAllocator_T::GetImageMemoryRequirements(
16452 VkImage hImage,
16453 VkMemoryRequirements& memReq,
16454 bool& requiresDedicatedAllocation,
16455 bool& prefersDedicatedAllocation) const
16456 {
16457 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16458 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16459 {
16460 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16461 memReqInfo.image = hImage;
16462
16463 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16464
16465 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16466 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16467
16468 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16469
16470 memReq = memReq2.memoryRequirements;
16471 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16472 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16473 }
16474 else
16475 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16476 {
16477 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16478 requiresDedicatedAllocation = false;
16479 prefersDedicatedAllocation = false;
16480 }
16481 }
16482
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16483 VkResult VmaAllocator_T::AllocateMemory(
16484 const VkMemoryRequirements& vkMemReq,
16485 bool requiresDedicatedAllocation,
16486 bool prefersDedicatedAllocation,
16487 VkBuffer dedicatedBuffer,
16488 VkBufferUsageFlags dedicatedBufferUsage,
16489 VkImage dedicatedImage,
16490 const VmaAllocationCreateInfo& createInfo,
16491 VmaSuballocationType suballocType,
16492 size_t allocationCount,
16493 VmaAllocation* pAllocations)
16494 {
16495 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16496
16497 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16498
16499 if(vkMemReq.size == 0)
16500 {
16501 return VK_ERROR_VALIDATION_FAILED_EXT;
16502 }
16503 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16504 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16505 {
16506 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16507 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16508 }
16509 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16510 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16511 {
16512 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16513 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16514 }
16515 if(requiresDedicatedAllocation)
16516 {
16517 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16518 {
16519 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16520 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16521 }
16522 if(createInfo.pool != VK_NULL_HANDLE)
16523 {
16524 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16525 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16526 }
16527 }
16528 if((createInfo.pool != VK_NULL_HANDLE) &&
16529 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16530 {
16531 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16532 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16533 }
16534
16535 if(createInfo.pool != VK_NULL_HANDLE)
16536 {
16537 const VkDeviceSize alignmentForPool = VMA_MAX(
16538 vkMemReq.alignment,
16539 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16540
16541 VmaAllocationCreateInfo createInfoForPool = createInfo;
16542 // If memory type is not HOST_VISIBLE, disable MAPPED.
16543 if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16544 (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16545 {
16546 createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16547 }
16548
16549 return createInfo.pool->m_BlockVector.Allocate(
16550 m_CurrentFrameIndex.load(),
16551 vkMemReq.size,
16552 alignmentForPool,
16553 createInfoForPool,
16554 suballocType,
16555 allocationCount,
16556 pAllocations);
16557 }
16558 else
16559 {
16560 // Bit mask of memory Vulkan types acceptable for this allocation.
16561 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16562 uint32_t memTypeIndex = UINT32_MAX;
16563 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16564 if(res == VK_SUCCESS)
16565 {
16566 VkDeviceSize alignmentForMemType = VMA_MAX(
16567 vkMemReq.alignment,
16568 GetMemoryTypeMinAlignment(memTypeIndex));
16569
16570 res = AllocateMemoryOfType(
16571 vkMemReq.size,
16572 alignmentForMemType,
16573 requiresDedicatedAllocation || prefersDedicatedAllocation,
16574 dedicatedBuffer,
16575 dedicatedBufferUsage,
16576 dedicatedImage,
16577 createInfo,
16578 memTypeIndex,
16579 suballocType,
16580 allocationCount,
16581 pAllocations);
16582 // Succeeded on first try.
16583 if(res == VK_SUCCESS)
16584 {
16585 return res;
16586 }
16587 // Allocation from this memory type failed. Try other compatible memory types.
16588 else
16589 {
16590 for(;;)
16591 {
16592 // Remove old memTypeIndex from list of possibilities.
16593 memoryTypeBits &= ~(1u << memTypeIndex);
16594 // Find alternative memTypeIndex.
16595 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16596 if(res == VK_SUCCESS)
16597 {
16598 alignmentForMemType = VMA_MAX(
16599 vkMemReq.alignment,
16600 GetMemoryTypeMinAlignment(memTypeIndex));
16601
16602 res = AllocateMemoryOfType(
16603 vkMemReq.size,
16604 alignmentForMemType,
16605 requiresDedicatedAllocation || prefersDedicatedAllocation,
16606 dedicatedBuffer,
16607 dedicatedBufferUsage,
16608 dedicatedImage,
16609 createInfo,
16610 memTypeIndex,
16611 suballocType,
16612 allocationCount,
16613 pAllocations);
16614 // Allocation from this alternative memory type succeeded.
16615 if(res == VK_SUCCESS)
16616 {
16617 return res;
16618 }
16619 // else: Allocation from this memory type failed. Try next one - next loop iteration.
16620 }
16621 // No other matching memory type index could be found.
16622 else
16623 {
16624 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16625 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16626 }
16627 }
16628 }
16629 }
16630 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16631 else
16632 return res;
16633 }
16634 }
16635
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16636 void VmaAllocator_T::FreeMemory(
16637 size_t allocationCount,
16638 const VmaAllocation* pAllocations)
16639 {
16640 VMA_ASSERT(pAllocations);
16641
16642 for(size_t allocIndex = allocationCount; allocIndex--; )
16643 {
16644 VmaAllocation allocation = pAllocations[allocIndex];
16645
16646 if(allocation != VK_NULL_HANDLE)
16647 {
16648 if(TouchAllocation(allocation))
16649 {
16650 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16651 {
16652 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16653 }
16654
16655 switch(allocation->GetType())
16656 {
16657 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16658 {
16659 VmaBlockVector* pBlockVector = VMA_NULL;
16660 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16661 if(hPool != VK_NULL_HANDLE)
16662 {
16663 pBlockVector = &hPool->m_BlockVector;
16664 }
16665 else
16666 {
16667 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16668 pBlockVector = m_pBlockVectors[memTypeIndex];
16669 }
16670 pBlockVector->Free(allocation);
16671 }
16672 break;
16673 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16674 FreeDedicatedMemory(allocation);
16675 break;
16676 default:
16677 VMA_ASSERT(0);
16678 }
16679 }
16680
16681 // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16682 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16683 allocation->SetUserData(this, VMA_NULL);
16684 m_AllocationObjectAllocator.Free(allocation);
16685 }
16686 }
16687 }
16688
16689 // OH ISSUE: VMA preAlloc
AllocateReservedMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16690 VkResult VmaAllocator_T::AllocateReservedMemory(
16691 const VkMemoryRequirements& vkMemReq,
16692 bool requiresDedicatedAllocation,
16693 bool prefersDedicatedAllocation,
16694 VkBuffer dedicatedBuffer,
16695 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
16696 VkImage dedicatedImage,
16697 const VmaAllocationCreateInfo& createInfo,
16698 VmaSuballocationType suballocType,
16699 size_t allocationCount,
16700 VmaAllocation* pAllocations)
16701 {
16702 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16703 if (allocationCount != 1) {
16704 return VK_NOT_READY;
16705 }
16706 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16707 uint32_t memTypeIndex = UINT32_MAX;
16708 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16709 if(res != VK_SUCCESS) {
16710 return res;
16711 }
16712 VkDeviceSize alignmentForMemType = VMA_MAX(
16713 vkMemReq.alignment,
16714 GetMemoryTypeMinAlignment(memTypeIndex));
16715 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16716 VMA_ASSERT(reservedBlockVector);
16717
16718 res = reservedBlockVector->AllocateReserved(
16719 m_CurrentFrameIndex.load(),
16720 vkMemReq.size,
16721 alignmentForMemType,
16722 createInfo,
16723 suballocType,
16724 pAllocations);
16725 return res;
16726 }
16727
FreeReservedMemory(size_t allocationCount,const VmaAllocation * pAllocations)16728 void VmaAllocator_T::FreeReservedMemory(
16729 size_t allocationCount,
16730 const VmaAllocation* pAllocations)
16731 {
16732 VMA_ASSERT(pAllocations);
16733
16734 for(size_t allocIndex = allocationCount; allocIndex--; )
16735 {
16736 VmaAllocation allocation = pAllocations[allocIndex];
16737
16738 if(allocation != VK_NULL_HANDLE)
16739 {
16740 if(TouchAllocation(allocation))
16741 {
16742 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16743 {
16744 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16745 }
16746
16747 switch(allocation->GetType())
16748 {
16749 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16750 {
16751 VmaBlockVector* pBlockVector = VMA_NULL;
16752 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16753 if(hPool != VK_NULL_HANDLE)
16754 {
16755 pBlockVector = &hPool->m_BlockVector;
16756 }
16757 else
16758 {
16759 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16760 pBlockVector = m_pReservedBlockVectors[memTypeIndex];
16761 }
16762 VMA_ASSERT(pBlockVector);
16763 pBlockVector->FreeReserved(allocation);
16764 }
16765 break;
16766 default:
16767 VMA_ASSERT(0);
16768 }
16769 }
16770 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16771 }
16772 }
16773 }
16774
SwapReservedBlock(VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16775 VkResult VmaAllocator_T::SwapReservedBlock(
16776 VkImage image,
16777 const VmaAllocationCreateInfo* pCreateInfo,
16778 VmaAllocation* pAllocation,
16779 VmaAllocationInfo* pAllocationInfo)
16780 {
16781 VmaAllocation oldAllocation = *pAllocation;
16782 if (oldAllocation->GetType() != VmaAllocation_T::ALLOCATION_TYPE_BLOCK) {
16783 return VK_ERROR_UNKNOWN;
16784 }
16785 VmaPool hPool = oldAllocation->GetBlock()->GetParentPool();
16786 if(hPool != VK_NULL_HANDLE) {
16787 return VK_ERROR_UNKNOWN;
16788 }
16789 const uint32_t memTypeIndex = oldAllocation->GetMemoryTypeIndex();
16790 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16791 if (reservedBlockVector->IsEmpty()) {
16792 return VK_NOT_READY;
16793 }
16794 VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
16795 SwapLastBlock(blockVector, reservedBlockVector);
16796 return VK_SUCCESS;
16797 }
16798
GetPreAllocBlockSize()16799 uint32_t VmaAllocator_T::GetPreAllocBlockSize()
16800 {
16801 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[0];
16802 VMA_ASSERT(reservedBlockVector);
16803 return reservedBlockVector->GetBlockCount();
16804 }
16805
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16806 VkResult VmaAllocator_T::ResizeAllocation(
16807 const VmaAllocation alloc,
16808 VkDeviceSize newSize)
16809 {
16810 // This function is deprecated and so it does nothing. It's left for backward compatibility.
16811 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16812 {
16813 return VK_ERROR_VALIDATION_FAILED_EXT;
16814 }
16815 if(newSize == alloc->GetSize())
16816 {
16817 return VK_SUCCESS;
16818 }
16819 return VK_ERROR_OUT_OF_POOL_MEMORY;
16820 }
16821
CalculateStats(VmaStats * pStats)16822 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16823 {
16824 // Initialize.
16825 InitStatInfo(pStats->total);
16826 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16827 InitStatInfo(pStats->memoryType[i]);
16828 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16829 InitStatInfo(pStats->memoryHeap[i]);
16830
16831 // Process default pools.
16832 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16833 {
16834 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16835 VMA_ASSERT(pBlockVector);
16836 pBlockVector->AddStats(pStats);
16837 }
16838
16839 // Process custom pools.
16840 {
16841 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16842 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16843 {
16844 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16845 }
16846 }
16847
16848 // Process dedicated allocations.
16849 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16850 {
16851 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16852 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16853 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16854 VMA_ASSERT(pDedicatedAllocVector);
16855 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16856 {
16857 VmaStatInfo allocationStatInfo;
16858 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16859 VmaAddStatInfo(pStats->total, allocationStatInfo);
16860 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16861 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16862 }
16863 }
16864
16865 // Postprocess.
16866 VmaPostprocessCalcStatInfo(pStats->total);
16867 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16868 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16869 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16870 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16871 }
16872
FreeEmptyBlock()16873 void VmaAllocator_T::FreeEmptyBlock()
16874 {
16875 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
16876 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16877 VMA_ASSERT(pBlockVector);
16878 pBlockVector->FreeEmptyBlock();
16879
16880 VmaBlockVector* const pReservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16881 VMA_ASSERT(pReservedBlockVector);
16882 pReservedBlockVector->FreeEmptyBlock();
16883 }
16884 }
16885
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16886 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16887 {
16888 #if VMA_MEMORY_BUDGET
16889 if(m_UseExtMemoryBudget)
16890 {
16891 if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16892 {
16893 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16894 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16895 {
16896 const uint32_t heapIndex = firstHeap + i;
16897
16898 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16899 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16900
16901 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16902 {
16903 outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16904 outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16905 }
16906 else
16907 {
16908 outBudget->usage = 0;
16909 }
16910
16911 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16912 outBudget->budget = VMA_MIN(
16913 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16914 }
16915 }
16916 else
16917 {
16918 UpdateVulkanBudget(); // Outside of mutex lock
16919 GetBudget(outBudget, firstHeap, heapCount); // Recursion
16920 }
16921 }
16922 else
16923 #endif
16924 {
16925 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16926 {
16927 const uint32_t heapIndex = firstHeap + i;
16928
16929 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16930 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16931
16932 outBudget->usage = outBudget->blockBytes;
16933 outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16934 }
16935 }
16936 }
16937
16938 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16939
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16940 VkResult VmaAllocator_T::DefragmentationBegin(
16941 const VmaDefragmentationInfo2& info,
16942 VmaDefragmentationStats* pStats,
16943 VmaDefragmentationContext* pContext)
16944 {
16945 if(info.pAllocationsChanged != VMA_NULL)
16946 {
16947 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16948 }
16949
16950 *pContext = vma_new(this, VmaDefragmentationContext_T)(
16951 this, m_CurrentFrameIndex.load(), info.flags, pStats);
16952
16953 (*pContext)->AddPools(info.poolCount, info.pPools);
16954 (*pContext)->AddAllocations(
16955 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16956
16957 VkResult res = (*pContext)->Defragment(
16958 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16959 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16960 info.commandBuffer, pStats, info.flags);
16961
16962 if(res != VK_NOT_READY)
16963 {
16964 vma_delete(this, *pContext);
16965 *pContext = VMA_NULL;
16966 }
16967
16968 return res;
16969 }
16970
DefragmentationEnd(VmaDefragmentationContext context)16971 VkResult VmaAllocator_T::DefragmentationEnd(
16972 VmaDefragmentationContext context)
16973 {
16974 vma_delete(this, context);
16975 return VK_SUCCESS;
16976 }
16977
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)16978 VkResult VmaAllocator_T::DefragmentationPassBegin(
16979 VmaDefragmentationPassInfo* pInfo,
16980 VmaDefragmentationContext context)
16981 {
16982 return context->DefragmentPassBegin(pInfo);
16983 }
DefragmentationPassEnd(VmaDefragmentationContext context)16984 VkResult VmaAllocator_T::DefragmentationPassEnd(
16985 VmaDefragmentationContext context)
16986 {
16987 return context->DefragmentPassEnd();
16988
16989 }
16990
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)16991 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
16992 {
16993 if(hAllocation->CanBecomeLost())
16994 {
16995 /*
16996 Warning: This is a carefully designed algorithm.
16997 Do not modify unless you really know what you're doing :)
16998 */
16999 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17000 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17001 for(;;)
17002 {
17003 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17004 {
17005 pAllocationInfo->memoryType = UINT32_MAX;
17006 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17007 pAllocationInfo->offset = 0;
17008 pAllocationInfo->size = hAllocation->GetSize();
17009 pAllocationInfo->pMappedData = VMA_NULL;
17010 pAllocationInfo->pUserData = hAllocation->GetUserData();
17011 return;
17012 }
17013 else if(localLastUseFrameIndex == localCurrFrameIndex)
17014 {
17015 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17016 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17017 pAllocationInfo->offset = hAllocation->GetOffset();
17018 pAllocationInfo->size = hAllocation->GetSize();
17019 pAllocationInfo->pMappedData = VMA_NULL;
17020 pAllocationInfo->pUserData = hAllocation->GetUserData();
17021 return;
17022 }
17023 else // Last use time earlier than current time.
17024 {
17025 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17026 {
17027 localLastUseFrameIndex = localCurrFrameIndex;
17028 }
17029 }
17030 }
17031 }
17032 else
17033 {
17034 #if VMA_STATS_STRING_ENABLED
17035 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17036 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17037 for(;;)
17038 {
17039 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17040 if(localLastUseFrameIndex == localCurrFrameIndex)
17041 {
17042 break;
17043 }
17044 else // Last use time earlier than current time.
17045 {
17046 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17047 {
17048 localLastUseFrameIndex = localCurrFrameIndex;
17049 }
17050 }
17051 }
17052 #endif
17053
17054 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17055 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17056 pAllocationInfo->offset = hAllocation->GetOffset();
17057 pAllocationInfo->size = hAllocation->GetSize();
17058 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17059 pAllocationInfo->pUserData = hAllocation->GetUserData();
17060 }
17061 }
17062
TouchAllocation(VmaAllocation hAllocation)17063 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17064 {
17065 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17066 if(hAllocation->CanBecomeLost())
17067 {
17068 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17069 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17070 for(;;)
17071 {
17072 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17073 {
17074 return false;
17075 }
17076 else if(localLastUseFrameIndex == localCurrFrameIndex)
17077 {
17078 return true;
17079 }
17080 else // Last use time earlier than current time.
17081 {
17082 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17083 {
17084 localLastUseFrameIndex = localCurrFrameIndex;
17085 }
17086 }
17087 }
17088 }
17089 else
17090 {
17091 #if VMA_STATS_STRING_ENABLED
17092 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17093 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17094 for(;;)
17095 {
17096 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17097 if(localLastUseFrameIndex == localCurrFrameIndex)
17098 {
17099 break;
17100 }
17101 else // Last use time earlier than current time.
17102 {
17103 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17104 {
17105 localLastUseFrameIndex = localCurrFrameIndex;
17106 }
17107 }
17108 }
17109 #endif
17110
17111 return true;
17112 }
17113 }
17114
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17115 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17116 {
17117 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17118
17119 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17120
17121 if(newCreateInfo.maxBlockCount == 0)
17122 {
17123 newCreateInfo.maxBlockCount = SIZE_MAX;
17124 }
17125 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17126 {
17127 return VK_ERROR_INITIALIZATION_FAILED;
17128 }
17129 // Memory type index out of range or forbidden.
17130 if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17131 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17132 {
17133 return VK_ERROR_FEATURE_NOT_PRESENT;
17134 }
17135
17136 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17137
17138 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17139
17140 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17141 if(res != VK_SUCCESS)
17142 {
17143 vma_delete(this, *pPool);
17144 *pPool = VMA_NULL;
17145 return res;
17146 }
17147
17148 // Add to m_Pools.
17149 {
17150 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17151 (*pPool)->SetId(m_NextPoolId++);
17152 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
17153 }
17154
17155 return VK_SUCCESS;
17156 }
17157
DestroyPool(VmaPool pool)17158 void VmaAllocator_T::DestroyPool(VmaPool pool)
17159 {
17160 // Remove from m_Pools.
17161 {
17162 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17163 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
17164 VMA_ASSERT(success && "Pool not found in Allocator.");
17165 }
17166
17167 vma_delete(this, pool);
17168 }
17169
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)17170 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17171 {
17172 pool->m_BlockVector.GetPoolStats(pPoolStats);
17173 }
17174
SetCurrentFrameIndex(uint32_t frameIndex)17175 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17176 {
17177 m_CurrentFrameIndex.store(frameIndex);
17178
17179 #if VMA_MEMORY_BUDGET
17180 if(m_UseExtMemoryBudget)
17181 {
17182 UpdateVulkanBudget();
17183 }
17184 #endif // #if VMA_MEMORY_BUDGET
17185 }
17186
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)17187 void VmaAllocator_T::MakePoolAllocationsLost(
17188 VmaPool hPool,
17189 size_t* pLostAllocationCount)
17190 {
17191 hPool->m_BlockVector.MakePoolAllocationsLost(
17192 m_CurrentFrameIndex.load(),
17193 pLostAllocationCount);
17194 }
17195
CheckPoolCorruption(VmaPool hPool)17196 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17197 {
17198 return hPool->m_BlockVector.CheckCorruption();
17199 }
17200
CheckCorruption(uint32_t memoryTypeBits)17201 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17202 {
17203 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17204
17205 // Process default pools.
17206 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17207 {
17208 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17209 {
17210 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17211 VMA_ASSERT(pBlockVector);
17212 VkResult localRes = pBlockVector->CheckCorruption();
17213 switch(localRes)
17214 {
17215 case VK_ERROR_FEATURE_NOT_PRESENT:
17216 break;
17217 case VK_SUCCESS:
17218 finalRes = VK_SUCCESS;
17219 break;
17220 default:
17221 return localRes;
17222 }
17223 }
17224 }
17225
17226 // Process custom pools.
17227 {
17228 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17229 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17230 {
17231 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17232 {
17233 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17234 switch(localRes)
17235 {
17236 case VK_ERROR_FEATURE_NOT_PRESENT:
17237 break;
17238 case VK_SUCCESS:
17239 finalRes = VK_SUCCESS;
17240 break;
17241 default:
17242 return localRes;
17243 }
17244 }
17245 }
17246 }
17247
17248 return finalRes;
17249 }
17250
CreateLostAllocation(VmaAllocation * pAllocation)17251 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17252 {
17253 *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17254 (*pAllocation)->InitLost();
17255 }
17256
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)17257 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17258 {
17259 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17260
17261 // HeapSizeLimit is in effect for this heap.
17262 if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17263 {
17264 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17265 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17266 for(;;)
17267 {
17268 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17269 if(blockBytesAfterAllocation > heapSize)
17270 {
17271 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17272 }
17273 if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17274 {
17275 break;
17276 }
17277 }
17278 }
17279 else
17280 {
17281 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17282 }
17283
17284 // VULKAN CALL vkAllocateMemory.
17285 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17286
17287 if(res == VK_SUCCESS)
17288 {
17289 #if VMA_MEMORY_BUDGET
17290 ++m_Budget.m_OperationsSinceBudgetFetch;
17291 #endif
17292
17293 // Informative callback.
17294 if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17295 {
17296 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17297 }
17298 }
17299 else
17300 {
17301 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17302 }
17303
17304 return res;
17305 }
17306
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)17307 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17308 {
17309 // Informative callback.
17310 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17311 {
17312 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17313 }
17314
17315 // VULKAN CALL vkFreeMemory.
17316 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17317
17318 m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17319 }
17320
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)17321 VkResult VmaAllocator_T::BindVulkanBuffer(
17322 VkDeviceMemory memory,
17323 VkDeviceSize memoryOffset,
17324 VkBuffer buffer,
17325 const void* pNext)
17326 {
17327 if(pNext != VMA_NULL)
17328 {
17329 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17330 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17331 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17332 {
17333 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17334 bindBufferMemoryInfo.pNext = pNext;
17335 bindBufferMemoryInfo.buffer = buffer;
17336 bindBufferMemoryInfo.memory = memory;
17337 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17338 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17339 }
17340 else
17341 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17342 {
17343 return VK_ERROR_EXTENSION_NOT_PRESENT;
17344 }
17345 }
17346 else
17347 {
17348 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17349 }
17350 }
17351
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17352 VkResult VmaAllocator_T::BindVulkanImage(
17353 VkDeviceMemory memory,
17354 VkDeviceSize memoryOffset,
17355 VkImage image,
17356 const void* pNext)
17357 {
17358 if(pNext != VMA_NULL)
17359 {
17360 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17361 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17362 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17363 {
17364 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17365 bindBufferMemoryInfo.pNext = pNext;
17366 bindBufferMemoryInfo.image = image;
17367 bindBufferMemoryInfo.memory = memory;
17368 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17369 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17370 }
17371 else
17372 #endif // #if VMA_BIND_MEMORY2
17373 {
17374 return VK_ERROR_EXTENSION_NOT_PRESENT;
17375 }
17376 }
17377 else
17378 {
17379 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17380 }
17381 }
17382
Map(VmaAllocation hAllocation,void ** ppData)17383 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17384 {
17385 if(hAllocation->CanBecomeLost())
17386 {
17387 return VK_ERROR_MEMORY_MAP_FAILED;
17388 }
17389
17390 switch(hAllocation->GetType())
17391 {
17392 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17393 {
17394 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17395 char *pBytes = VMA_NULL;
17396 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17397 if(res == VK_SUCCESS)
17398 {
17399 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17400 hAllocation->BlockAllocMap();
17401 }
17402 return res;
17403 }
17404 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17405 return hAllocation->DedicatedAllocMap(this, ppData);
17406 default:
17407 VMA_ASSERT(0);
17408 return VK_ERROR_MEMORY_MAP_FAILED;
17409 }
17410 }
17411
Unmap(VmaAllocation hAllocation)17412 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17413 {
17414 switch(hAllocation->GetType())
17415 {
17416 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17417 {
17418 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17419 hAllocation->BlockAllocUnmap();
17420 pBlock->Unmap(this, 1);
17421 }
17422 break;
17423 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17424 hAllocation->DedicatedAllocUnmap(this);
17425 break;
17426 default:
17427 VMA_ASSERT(0);
17428 }
17429 }
17430
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17431 VkResult VmaAllocator_T::BindBufferMemory(
17432 VmaAllocation hAllocation,
17433 VkDeviceSize allocationLocalOffset,
17434 VkBuffer hBuffer,
17435 const void* pNext)
17436 {
17437 VkResult res = VK_SUCCESS;
17438 switch(hAllocation->GetType())
17439 {
17440 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17441 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17442 break;
17443 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17444 {
17445 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17446 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17447 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17448 break;
17449 }
17450 default:
17451 VMA_ASSERT(0);
17452 }
17453 return res;
17454 }
17455
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17456 VkResult VmaAllocator_T::BindImageMemory(
17457 VmaAllocation hAllocation,
17458 VkDeviceSize allocationLocalOffset,
17459 VkImage hImage,
17460 const void* pNext)
17461 {
17462 VkResult res = VK_SUCCESS;
17463 switch(hAllocation->GetType())
17464 {
17465 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17466 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17467 break;
17468 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17469 {
17470 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17471 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17472 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17473 break;
17474 }
17475 default:
17476 VMA_ASSERT(0);
17477 }
17478 return res;
17479 }
17480
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17481 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17482 VmaAllocation hAllocation,
17483 VkDeviceSize offset, VkDeviceSize size,
17484 VMA_CACHE_OPERATION op)
17485 {
17486 VkResult res = VK_SUCCESS;
17487
17488 VkMappedMemoryRange memRange = {};
17489 if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17490 {
17491 switch(op)
17492 {
17493 case VMA_CACHE_FLUSH:
17494 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17495 break;
17496 case VMA_CACHE_INVALIDATE:
17497 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17498 break;
17499 default:
17500 VMA_ASSERT(0);
17501 }
17502 }
17503 // else: Just ignore this call.
17504 return res;
17505 }
17506
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17507 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17508 uint32_t allocationCount,
17509 const VmaAllocation* allocations,
17510 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17511 VMA_CACHE_OPERATION op)
17512 {
17513 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17514 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17515 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17516
17517 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17518 {
17519 const VmaAllocation alloc = allocations[allocIndex];
17520 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17521 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17522 VkMappedMemoryRange newRange;
17523 if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17524 {
17525 ranges.push_back(newRange);
17526 }
17527 }
17528
17529 VkResult res = VK_SUCCESS;
17530 if(!ranges.empty())
17531 {
17532 switch(op)
17533 {
17534 case VMA_CACHE_FLUSH:
17535 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17536 break;
17537 case VMA_CACHE_INVALIDATE:
17538 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17539 break;
17540 default:
17541 VMA_ASSERT(0);
17542 }
17543 }
17544 // else: Just ignore this call.
17545 return res;
17546 }
17547
FreeDedicatedMemory(const VmaAllocation allocation)17548 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17549 {
17550 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17551
17552 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17553 {
17554 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17555 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17556 VMA_ASSERT(pDedicatedAllocations);
17557 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17558 VMA_ASSERT(success);
17559 }
17560
17561 VkDeviceMemory hMemory = allocation->GetMemory();
17562
17563 /*
17564 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17565 before vkFreeMemory.
17566
17567 if(allocation->GetMappedData() != VMA_NULL)
17568 {
17569 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17570 }
17571 */
17572
17573 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17574
17575 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17576 }
17577
CalculateGpuDefragmentationMemoryTypeBits()17578 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17579 {
17580 VkBufferCreateInfo dummyBufCreateInfo;
17581 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17582
17583 uint32_t memoryTypeBits = 0;
17584
17585 // Create buffer.
17586 VkBuffer buf = VK_NULL_HANDLE;
17587 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17588 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17589 if(res == VK_SUCCESS)
17590 {
17591 // Query for supported memory types.
17592 VkMemoryRequirements memReq;
17593 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17594 memoryTypeBits = memReq.memoryTypeBits;
17595
17596 // Destroy buffer.
17597 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17598 }
17599
17600 return memoryTypeBits;
17601 }
17602
CalculateGlobalMemoryTypeBits()17603 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17604 {
17605 // Make sure memory information is already fetched.
17606 VMA_ASSERT(GetMemoryTypeCount() > 0);
17607
17608 uint32_t memoryTypeBits = UINT32_MAX;
17609
17610 if(!m_UseAmdDeviceCoherentMemory)
17611 {
17612 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17613 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17614 {
17615 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17616 {
17617 memoryTypeBits &= ~(1u << memTypeIndex);
17618 }
17619 }
17620 }
17621
17622 return memoryTypeBits;
17623 }
17624
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17625 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17626 VmaAllocation allocation,
17627 VkDeviceSize offset, VkDeviceSize size,
17628 VkMappedMemoryRange& outRange) const
17629 {
17630 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17631 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17632 {
17633 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17634 const VkDeviceSize allocationSize = allocation->GetSize();
17635 VMA_ASSERT(offset <= allocationSize);
17636
17637 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17638 outRange.pNext = VMA_NULL;
17639 outRange.memory = allocation->GetMemory();
17640
17641 switch(allocation->GetType())
17642 {
17643 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17644 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17645 if(size == VK_WHOLE_SIZE)
17646 {
17647 outRange.size = allocationSize - outRange.offset;
17648 }
17649 else
17650 {
17651 VMA_ASSERT(offset + size <= allocationSize);
17652 outRange.size = VMA_MIN(
17653 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17654 allocationSize - outRange.offset);
17655 }
17656 break;
17657 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17658 {
17659 // 1. Still within this allocation.
17660 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17661 if(size == VK_WHOLE_SIZE)
17662 {
17663 size = allocationSize - offset;
17664 }
17665 else
17666 {
17667 VMA_ASSERT(offset + size <= allocationSize);
17668 }
17669 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17670
17671 // 2. Adjust to whole block.
17672 const VkDeviceSize allocationOffset = allocation->GetOffset();
17673 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17674 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17675 outRange.offset += allocationOffset;
17676 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17677
17678 break;
17679 }
17680 default:
17681 VMA_ASSERT(0);
17682 }
17683 return true;
17684 }
17685 return false;
17686 }
17687
17688 #if VMA_MEMORY_BUDGET
17689
UpdateVulkanBudget()17690 void VmaAllocator_T::UpdateVulkanBudget()
17691 {
17692 VMA_ASSERT(m_UseExtMemoryBudget);
17693
17694 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17695
17696 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17697 VmaPnextChainPushFront(&memProps, &budgetProps);
17698
17699 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17700
17701 {
17702 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17703
17704 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17705 {
17706 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17707 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17708 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17709
17710 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17711 if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17712 {
17713 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17714 }
17715 else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17716 {
17717 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17718 }
17719 if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17720 {
17721 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17722 }
17723 }
17724 m_Budget.m_OperationsSinceBudgetFetch = 0;
17725 }
17726 }
17727
17728 #endif // #if VMA_MEMORY_BUDGET
17729
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17730 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17731 {
17732 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17733 !hAllocation->CanBecomeLost() &&
17734 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17735 {
17736 void* pData = VMA_NULL;
17737 VkResult res = Map(hAllocation, &pData);
17738 if(res == VK_SUCCESS)
17739 {
17740 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17741 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17742 Unmap(hAllocation);
17743 }
17744 else
17745 {
17746 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17747 }
17748 }
17749 }
17750
GetGpuDefragmentationMemoryTypeBits()17751 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17752 {
17753 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17754 if(memoryTypeBits == UINT32_MAX)
17755 {
17756 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17757 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17758 }
17759 return memoryTypeBits;
17760 }
17761
17762 #if VMA_STATS_STRING_ENABLED
17763
PrintDetailedMap(VmaJsonWriter & json)17764 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17765 {
17766 bool dedicatedAllocationsStarted = false;
17767 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17768 {
17769 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17770 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17771 VMA_ASSERT(pDedicatedAllocVector);
17772 if(pDedicatedAllocVector->empty() == false)
17773 {
17774 if(dedicatedAllocationsStarted == false)
17775 {
17776 dedicatedAllocationsStarted = true;
17777 json.WriteString("DedicatedAllocations");
17778 json.BeginObject();
17779 }
17780
17781 json.BeginString("Type ");
17782 json.ContinueString(memTypeIndex);
17783 json.EndString();
17784
17785 json.BeginArray();
17786
17787 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17788 {
17789 json.BeginObject(true);
17790 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17791 hAlloc->PrintParameters(json);
17792 json.EndObject();
17793 }
17794
17795 json.EndArray();
17796 }
17797 }
17798 if(dedicatedAllocationsStarted)
17799 {
17800 json.EndObject();
17801 }
17802
17803 {
17804 bool allocationsStarted = false;
17805 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17806 {
17807 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17808 {
17809 if(allocationsStarted == false)
17810 {
17811 allocationsStarted = true;
17812 json.WriteString("DefaultPools");
17813 json.BeginObject();
17814 }
17815
17816 json.BeginString("Type ");
17817 json.ContinueString(memTypeIndex);
17818 json.EndString();
17819
17820 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17821 }
17822 }
17823 if(allocationsStarted)
17824 {
17825 json.EndObject();
17826 }
17827 }
17828
17829 // Custom pools
17830 {
17831 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17832 const size_t poolCount = m_Pools.size();
17833 if(poolCount > 0)
17834 {
17835 json.WriteString("Pools");
17836 json.BeginObject();
17837 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17838 {
17839 json.BeginString();
17840 json.ContinueString(m_Pools[poolIndex]->GetId());
17841 json.EndString();
17842
17843 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17844 }
17845 json.EndObject();
17846 }
17847 }
17848 }
17849
17850 #endif // #if VMA_STATS_STRING_ENABLED
17851
17852 ////////////////////////////////////////////////////////////////////////////////
17853 // Public interface
17854
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17855 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17856 const VmaAllocatorCreateInfo* pCreateInfo,
17857 VmaAllocator* pAllocator)
17858 {
17859 VMA_ASSERT(pCreateInfo && pAllocator);
17860 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17861 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17862 VMA_DEBUG_LOG("vmaCreateAllocator");
17863 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17864 return (*pAllocator)->Init(pCreateInfo);
17865 }
17866
vmaDestroyAllocator(VmaAllocator allocator)17867 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17868 VmaAllocator allocator)
17869 {
17870 if(allocator != VK_NULL_HANDLE)
17871 {
17872 VMA_DEBUG_LOG("vmaDestroyAllocator");
17873 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17874 vma_delete(&allocationCallbacks, allocator);
17875 }
17876 }
17877
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17878 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17879 {
17880 VMA_ASSERT(allocator && pAllocatorInfo);
17881 pAllocatorInfo->instance = allocator->m_hInstance;
17882 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17883 pAllocatorInfo->device = allocator->m_hDevice;
17884 }
17885
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17886 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17887 VmaAllocator allocator,
17888 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17889 {
17890 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17891 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17892 }
17893
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17894 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17895 VmaAllocator allocator,
17896 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17897 {
17898 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17899 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17900 }
17901
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17902 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17903 VmaAllocator allocator,
17904 uint32_t memoryTypeIndex,
17905 VkMemoryPropertyFlags* pFlags)
17906 {
17907 VMA_ASSERT(allocator && pFlags);
17908 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17909 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17910 }
17911
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17912 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17913 VmaAllocator allocator,
17914 uint32_t frameIndex)
17915 {
17916 VMA_ASSERT(allocator);
17917 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17918
17919 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17920
17921 allocator->SetCurrentFrameIndex(frameIndex);
17922 }
17923
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17924 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17925 VmaAllocator allocator,
17926 VmaStats* pStats)
17927 {
17928 VMA_ASSERT(allocator && pStats);
17929 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17930 allocator->CalculateStats(pStats);
17931 }
17932
vmaFreeEmptyBlock(VmaAllocator allocator)17933 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
17934 VmaAllocator allocator)
17935 {
17936 VMA_ASSERT(allocator);
17937 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17938 allocator->FreeEmptyBlock();
17939 }
17940
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17941 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17942 VmaAllocator allocator,
17943 VmaBudget* pBudget)
17944 {
17945 VMA_ASSERT(allocator && pBudget);
17946 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17947 allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17948 }
17949
17950 #if VMA_STATS_STRING_ENABLED
17951
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17952 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17953 VmaAllocator allocator,
17954 char** ppStatsString,
17955 VkBool32 detailedMap)
17956 {
17957 VMA_ASSERT(allocator && ppStatsString);
17958 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17959
17960 VmaStringBuilder sb(allocator);
17961 {
17962 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17963 json.BeginObject();
17964
17965 VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17966 allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17967
17968 VmaStats stats;
17969 allocator->CalculateStats(&stats);
17970
17971 json.WriteString("Total");
17972 VmaPrintStatInfo(json, stats.total);
17973
17974 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
17975 {
17976 json.BeginString("Heap ");
17977 json.ContinueString(heapIndex);
17978 json.EndString();
17979 json.BeginObject();
17980
17981 json.WriteString("Size");
17982 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
17983
17984 json.WriteString("Flags");
17985 json.BeginArray(true);
17986 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
17987 {
17988 json.WriteString("DEVICE_LOCAL");
17989 }
17990 json.EndArray();
17991
17992 json.WriteString("Budget");
17993 json.BeginObject();
17994 {
17995 json.WriteString("BlockBytes");
17996 json.WriteNumber(budget[heapIndex].blockBytes);
17997 json.WriteString("AllocationBytes");
17998 json.WriteNumber(budget[heapIndex].allocationBytes);
17999 json.WriteString("Usage");
18000 json.WriteNumber(budget[heapIndex].usage);
18001 json.WriteString("Budget");
18002 json.WriteNumber(budget[heapIndex].budget);
18003 }
18004 json.EndObject();
18005
18006 if(stats.memoryHeap[heapIndex].blockCount > 0)
18007 {
18008 json.WriteString("Stats");
18009 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18010 }
18011
18012 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18013 {
18014 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18015 {
18016 json.BeginString("Type ");
18017 json.ContinueString(typeIndex);
18018 json.EndString();
18019
18020 json.BeginObject();
18021
18022 json.WriteString("Flags");
18023 json.BeginArray(true);
18024 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18025 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18026 {
18027 json.WriteString("DEVICE_LOCAL");
18028 }
18029 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18030 {
18031 json.WriteString("HOST_VISIBLE");
18032 }
18033 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18034 {
18035 json.WriteString("HOST_COHERENT");
18036 }
18037 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18038 {
18039 json.WriteString("HOST_CACHED");
18040 }
18041 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18042 {
18043 json.WriteString("LAZILY_ALLOCATED");
18044 }
18045 if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18046 {
18047 json.WriteString(" PROTECTED");
18048 }
18049 if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18050 {
18051 json.WriteString(" DEVICE_COHERENT");
18052 }
18053 if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18054 {
18055 json.WriteString(" DEVICE_UNCACHED");
18056 }
18057 json.EndArray();
18058
18059 if(stats.memoryType[typeIndex].blockCount > 0)
18060 {
18061 json.WriteString("Stats");
18062 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18063 }
18064
18065 json.EndObject();
18066 }
18067 }
18068
18069 json.EndObject();
18070 }
18071 if(detailedMap == VK_TRUE)
18072 {
18073 allocator->PrintDetailedMap(json);
18074 }
18075
18076 json.EndObject();
18077 }
18078
18079 const size_t len = sb.GetLength();
18080 char* const pChars = vma_new_array(allocator, char, len + 1);
18081 if(len > 0)
18082 {
18083 memcpy(pChars, sb.GetData(), len);
18084 }
18085 pChars[len] = '\0';
18086 *ppStatsString = pChars;
18087 }
18088
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)18089 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18090 VmaAllocator allocator,
18091 char* pStatsString)
18092 {
18093 if(pStatsString != VMA_NULL)
18094 {
18095 VMA_ASSERT(allocator);
18096 size_t len = strlen(pStatsString);
18097 vma_delete_array(allocator, pStatsString, len + 1);
18098 }
18099 }
18100
18101 #endif // #if VMA_STATS_STRING_ENABLED
18102
18103 /*
18104 This function is not protected by any mutex because it just reads immutable data.
18105 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18106 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18107 VmaAllocator allocator,
18108 uint32_t memoryTypeBits,
18109 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18110 uint32_t* pMemoryTypeIndex)
18111 {
18112 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18113 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18114 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18115
18116 memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18117
18118 if(pAllocationCreateInfo->memoryTypeBits != 0)
18119 {
18120 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18121 }
18122
18123 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18124 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18125 uint32_t notPreferredFlags = 0;
18126
18127 // Convert usage to requiredFlags and preferredFlags.
18128 switch(pAllocationCreateInfo->usage)
18129 {
18130 case VMA_MEMORY_USAGE_UNKNOWN:
18131 break;
18132 case VMA_MEMORY_USAGE_GPU_ONLY:
18133 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18134 {
18135 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18136 }
18137 break;
18138 case VMA_MEMORY_USAGE_CPU_ONLY:
18139 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18140 break;
18141 case VMA_MEMORY_USAGE_CPU_TO_GPU:
18142 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18143 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18144 {
18145 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18146 }
18147 break;
18148 case VMA_MEMORY_USAGE_GPU_TO_CPU:
18149 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18150 preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18151 break;
18152 case VMA_MEMORY_USAGE_CPU_COPY:
18153 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18154 break;
18155 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
18156 requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18157 break;
18158 default:
18159 VMA_ASSERT(0);
18160 break;
18161 }
18162
18163 // Avoid DEVICE_COHERENT unless explicitly requested.
18164 if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18165 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18166 {
18167 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18168 }
18169
18170 *pMemoryTypeIndex = UINT32_MAX;
18171 uint32_t minCost = UINT32_MAX;
18172 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18173 memTypeIndex < allocator->GetMemoryTypeCount();
18174 ++memTypeIndex, memTypeBit <<= 1)
18175 {
18176 // This memory type is acceptable according to memoryTypeBits bitmask.
18177 if((memTypeBit & memoryTypeBits) != 0)
18178 {
18179 const VkMemoryPropertyFlags currFlags =
18180 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18181 // This memory type contains requiredFlags.
18182 if((requiredFlags & ~currFlags) == 0)
18183 {
18184 // Calculate cost as number of bits from preferredFlags not present in this memory type.
18185 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18186 VmaCountBitsSet(currFlags & notPreferredFlags);
18187 // Remember memory type with lowest cost.
18188 if(currCost < minCost)
18189 {
18190 *pMemoryTypeIndex = memTypeIndex;
18191 if(currCost == 0)
18192 {
18193 return VK_SUCCESS;
18194 }
18195 minCost = currCost;
18196 }
18197 }
18198 }
18199 }
18200 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18201 }
18202
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18203 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18204 VmaAllocator allocator,
18205 const VkBufferCreateInfo* pBufferCreateInfo,
18206 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18207 uint32_t* pMemoryTypeIndex)
18208 {
18209 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18210 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18211 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18212 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18213
18214 const VkDevice hDev = allocator->m_hDevice;
18215 VkBuffer hBuffer = VK_NULL_HANDLE;
18216 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18217 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18218 if(res == VK_SUCCESS)
18219 {
18220 VkMemoryRequirements memReq = {};
18221 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18222 hDev, hBuffer, &memReq);
18223
18224 res = vmaFindMemoryTypeIndex(
18225 allocator,
18226 memReq.memoryTypeBits,
18227 pAllocationCreateInfo,
18228 pMemoryTypeIndex);
18229
18230 allocator->GetVulkanFunctions().vkDestroyBuffer(
18231 hDev, hBuffer, allocator->GetAllocationCallbacks());
18232 }
18233 return res;
18234 }
18235
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18236 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18237 VmaAllocator allocator,
18238 const VkImageCreateInfo* pImageCreateInfo,
18239 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18240 uint32_t* pMemoryTypeIndex)
18241 {
18242 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18243 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18244 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18245 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18246
18247 const VkDevice hDev = allocator->m_hDevice;
18248 VkImage hImage = VK_NULL_HANDLE;
18249 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18250 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18251 if(res == VK_SUCCESS)
18252 {
18253 VkMemoryRequirements memReq = {};
18254 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18255 hDev, hImage, &memReq);
18256
18257 res = vmaFindMemoryTypeIndex(
18258 allocator,
18259 memReq.memoryTypeBits,
18260 pAllocationCreateInfo,
18261 pMemoryTypeIndex);
18262
18263 allocator->GetVulkanFunctions().vkDestroyImage(
18264 hDev, hImage, allocator->GetAllocationCallbacks());
18265 }
18266 return res;
18267 }
18268
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)18269 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18270 VmaAllocator allocator,
18271 const VmaPoolCreateInfo* pCreateInfo,
18272 VmaPool* pPool)
18273 {
18274 VMA_ASSERT(allocator && pCreateInfo && pPool);
18275
18276 VMA_DEBUG_LOG("vmaCreatePool");
18277
18278 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18279
18280 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18281
18282 #if VMA_RECORDING_ENABLED
18283 if(allocator->GetRecorder() != VMA_NULL)
18284 {
18285 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18286 }
18287 #endif
18288
18289 return res;
18290 }
18291
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)18292 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18293 VmaAllocator allocator,
18294 VmaPool pool)
18295 {
18296 VMA_ASSERT(allocator);
18297
18298 if(pool == VK_NULL_HANDLE)
18299 {
18300 return;
18301 }
18302
18303 VMA_DEBUG_LOG("vmaDestroyPool");
18304
18305 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18306
18307 #if VMA_RECORDING_ENABLED
18308 if(allocator->GetRecorder() != VMA_NULL)
18309 {
18310 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18311 }
18312 #endif
18313
18314 allocator->DestroyPool(pool);
18315 }
18316
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)18317 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18318 VmaAllocator allocator,
18319 VmaPool pool,
18320 VmaPoolStats* pPoolStats)
18321 {
18322 VMA_ASSERT(allocator && pool && pPoolStats);
18323
18324 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18325
18326 allocator->GetPoolStats(pool, pPoolStats);
18327 }
18328
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)18329 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18330 VmaAllocator allocator,
18331 VmaPool pool,
18332 size_t* pLostAllocationCount)
18333 {
18334 VMA_ASSERT(allocator && pool);
18335
18336 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18337
18338 #if VMA_RECORDING_ENABLED
18339 if(allocator->GetRecorder() != VMA_NULL)
18340 {
18341 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18342 }
18343 #endif
18344
18345 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18346 }
18347
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)18348 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18349 {
18350 VMA_ASSERT(allocator && pool);
18351
18352 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18353
18354 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18355
18356 return allocator->CheckPoolCorruption(pool);
18357 }
18358
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18359 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18360 VmaAllocator allocator,
18361 VmaPool pool,
18362 const char** ppName)
18363 {
18364 VMA_ASSERT(allocator && pool && ppName);
18365
18366 VMA_DEBUG_LOG("vmaGetPoolName");
18367
18368 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18369
18370 *ppName = pool->GetName();
18371 }
18372
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18373 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18374 VmaAllocator allocator,
18375 VmaPool pool,
18376 const char* pName)
18377 {
18378 VMA_ASSERT(allocator && pool);
18379
18380 VMA_DEBUG_LOG("vmaSetPoolName");
18381
18382 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18383
18384 pool->SetName(pName);
18385
18386 #if VMA_RECORDING_ENABLED
18387 if(allocator->GetRecorder() != VMA_NULL)
18388 {
18389 allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18390 }
18391 #endif
18392 }
18393
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18394 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18395 VmaAllocator allocator,
18396 const VkMemoryRequirements* pVkMemoryRequirements,
18397 const VmaAllocationCreateInfo* pCreateInfo,
18398 VmaAllocation* pAllocation,
18399 VmaAllocationInfo* pAllocationInfo)
18400 {
18401 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18402
18403 VMA_DEBUG_LOG("vmaAllocateMemory");
18404
18405 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18406
18407 VkResult result = allocator->AllocateMemory(
18408 *pVkMemoryRequirements,
18409 false, // requiresDedicatedAllocation
18410 false, // prefersDedicatedAllocation
18411 VK_NULL_HANDLE, // dedicatedBuffer
18412 UINT32_MAX, // dedicatedBufferUsage
18413 VK_NULL_HANDLE, // dedicatedImage
18414 *pCreateInfo,
18415 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18416 1, // allocationCount
18417 pAllocation);
18418
18419 #if VMA_RECORDING_ENABLED
18420 if(allocator->GetRecorder() != VMA_NULL)
18421 {
18422 allocator->GetRecorder()->RecordAllocateMemory(
18423 allocator->GetCurrentFrameIndex(),
18424 *pVkMemoryRequirements,
18425 *pCreateInfo,
18426 *pAllocation);
18427 }
18428 #endif
18429
18430 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18431 {
18432 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18433 }
18434
18435 return result;
18436 }
18437
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18438 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18439 VmaAllocator allocator,
18440 const VkMemoryRequirements* pVkMemoryRequirements,
18441 const VmaAllocationCreateInfo* pCreateInfo,
18442 size_t allocationCount,
18443 VmaAllocation* pAllocations,
18444 VmaAllocationInfo* pAllocationInfo)
18445 {
18446 if(allocationCount == 0)
18447 {
18448 return VK_SUCCESS;
18449 }
18450
18451 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18452
18453 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18454
18455 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18456
18457 VkResult result = allocator->AllocateMemory(
18458 *pVkMemoryRequirements,
18459 false, // requiresDedicatedAllocation
18460 false, // prefersDedicatedAllocation
18461 VK_NULL_HANDLE, // dedicatedBuffer
18462 UINT32_MAX, // dedicatedBufferUsage
18463 VK_NULL_HANDLE, // dedicatedImage
18464 *pCreateInfo,
18465 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18466 allocationCount,
18467 pAllocations);
18468
18469 #if VMA_RECORDING_ENABLED
18470 if(allocator->GetRecorder() != VMA_NULL)
18471 {
18472 allocator->GetRecorder()->RecordAllocateMemoryPages(
18473 allocator->GetCurrentFrameIndex(),
18474 *pVkMemoryRequirements,
18475 *pCreateInfo,
18476 (uint64_t)allocationCount,
18477 pAllocations);
18478 }
18479 #endif
18480
18481 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18482 {
18483 for(size_t i = 0; i < allocationCount; ++i)
18484 {
18485 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18486 }
18487 }
18488
18489 return result;
18490 }
18491
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18492 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18493 VmaAllocator allocator,
18494 VkBuffer buffer,
18495 const VmaAllocationCreateInfo* pCreateInfo,
18496 VmaAllocation* pAllocation,
18497 VmaAllocationInfo* pAllocationInfo)
18498 {
18499 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18500
18501 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18502
18503 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18504
18505 VkMemoryRequirements vkMemReq = {};
18506 bool requiresDedicatedAllocation = false;
18507 bool prefersDedicatedAllocation = false;
18508 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18509 requiresDedicatedAllocation,
18510 prefersDedicatedAllocation);
18511
18512 VkResult result = allocator->AllocateMemory(
18513 vkMemReq,
18514 requiresDedicatedAllocation,
18515 prefersDedicatedAllocation,
18516 buffer, // dedicatedBuffer
18517 UINT32_MAX, // dedicatedBufferUsage
18518 VK_NULL_HANDLE, // dedicatedImage
18519 *pCreateInfo,
18520 VMA_SUBALLOCATION_TYPE_BUFFER,
18521 1, // allocationCount
18522 pAllocation);
18523
18524 #if VMA_RECORDING_ENABLED
18525 if(allocator->GetRecorder() != VMA_NULL)
18526 {
18527 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18528 allocator->GetCurrentFrameIndex(),
18529 vkMemReq,
18530 requiresDedicatedAllocation,
18531 prefersDedicatedAllocation,
18532 *pCreateInfo,
18533 *pAllocation);
18534 }
18535 #endif
18536
18537 if(pAllocationInfo && result == VK_SUCCESS)
18538 {
18539 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18540 }
18541
18542 return result;
18543 }
18544
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18545 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18546 VmaAllocator allocator,
18547 VkImage image,
18548 const VmaAllocationCreateInfo* pCreateInfo,
18549 VmaAllocation* pAllocation,
18550 VmaAllocationInfo* pAllocationInfo)
18551 {
18552 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18553
18554 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18555
18556 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18557
18558 VkMemoryRequirements vkMemReq = {};
18559 bool requiresDedicatedAllocation = false;
18560 bool prefersDedicatedAllocation = false;
18561 allocator->GetImageMemoryRequirements(image, vkMemReq,
18562 requiresDedicatedAllocation, prefersDedicatedAllocation);
18563
18564 VkResult result = allocator->AllocateMemory(
18565 vkMemReq,
18566 requiresDedicatedAllocation,
18567 prefersDedicatedAllocation,
18568 VK_NULL_HANDLE, // dedicatedBuffer
18569 UINT32_MAX, // dedicatedBufferUsage
18570 image, // dedicatedImage
18571 *pCreateInfo,
18572 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18573 1, // allocationCount
18574 pAllocation);
18575
18576 #if VMA_RECORDING_ENABLED
18577 if(allocator->GetRecorder() != VMA_NULL)
18578 {
18579 allocator->GetRecorder()->RecordAllocateMemoryForImage(
18580 allocator->GetCurrentFrameIndex(),
18581 vkMemReq,
18582 requiresDedicatedAllocation,
18583 prefersDedicatedAllocation,
18584 *pCreateInfo,
18585 *pAllocation);
18586 }
18587 #endif
18588
18589 if(pAllocationInfo && result == VK_SUCCESS)
18590 {
18591 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18592 }
18593
18594 return result;
18595 }
18596
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18597 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18598 VmaAllocator allocator,
18599 VmaAllocation allocation)
18600 {
18601 VMA_ASSERT(allocator);
18602
18603 if(allocation == VK_NULL_HANDLE)
18604 {
18605 return;
18606 }
18607
18608 VMA_DEBUG_LOG("vmaFreeMemory");
18609
18610 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18611
18612 #if VMA_RECORDING_ENABLED
18613 if(allocator->GetRecorder() != VMA_NULL)
18614 {
18615 allocator->GetRecorder()->RecordFreeMemory(
18616 allocator->GetCurrentFrameIndex(),
18617 allocation);
18618 }
18619 #endif
18620
18621 allocator->FreeMemory(
18622 1, // allocationCount
18623 &allocation);
18624 }
18625
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18626 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18627 VmaAllocator allocator,
18628 size_t allocationCount,
18629 const VmaAllocation* pAllocations)
18630 {
18631 if(allocationCount == 0)
18632 {
18633 return;
18634 }
18635
18636 VMA_ASSERT(allocator);
18637
18638 VMA_DEBUG_LOG("vmaFreeMemoryPages");
18639
18640 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18641
18642 #if VMA_RECORDING_ENABLED
18643 if(allocator->GetRecorder() != VMA_NULL)
18644 {
18645 allocator->GetRecorder()->RecordFreeMemoryPages(
18646 allocator->GetCurrentFrameIndex(),
18647 (uint64_t)allocationCount,
18648 pAllocations);
18649 }
18650 #endif
18651
18652 allocator->FreeMemory(allocationCount, pAllocations);
18653 }
18654
18655 // OH ISSUE: VMA preAlloc
vmaAllocateReservedMemoryForImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,const VmaAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,VmaAllocationInfo * VMA_NULLABLE pAllocationInfo)18656 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
18657 VmaAllocator VMA_NOT_NULL allocator,
18658 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
18659 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
18660 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
18661 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo)
18662 {
18663 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18664 VMA_DEBUG_LOG("vmaAllocateReservedMemoryForImage");
18665 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18666
18667 VkMemoryRequirements vkMemReq = {};
18668 bool requiresDedicatedAllocation = false;
18669 bool prefersDedicatedAllocation = false;
18670 allocator->GetImageMemoryRequirements(image, vkMemReq,
18671 requiresDedicatedAllocation, prefersDedicatedAllocation);
18672
18673 VkResult result = allocator->AllocateReservedMemory(
18674 vkMemReq,
18675 requiresDedicatedAllocation,
18676 prefersDedicatedAllocation,
18677 VK_NULL_HANDLE, // dedicatedBuffer
18678 UINT32_MAX, // dedicatedBufferUsage
18679 image, // dedicatedImage
18680 *pCreateInfo,
18681 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18682 1, // allocationCount
18683 pAllocation);
18684
18685 if(pAllocationInfo && result == VK_SUCCESS)
18686 {
18687 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18688 }
18689
18690 return result;
18691 }
18692
vmaGetNewBlockStats(VmaAllocation allocation,bool * pStats)18693 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
18694 VmaAllocation allocation,
18695 bool* pStats)
18696 {
18697 VMA_ASSERT(allocation);
18698 VMA_DEBUG_LOG("vmaGetNewBlockStats");
18699 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18700
18701 if (pStats != NULL) {
18702 *pStats = allocation->IsNewBlockFlag();
18703 }
18704 return VK_SUCCESS;
18705 }
18706
vmaClearNewBlockStats(VmaAllocation allocation)18707 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
18708 VmaAllocation allocation)
18709 {
18710 VMA_ASSERT(allocation);
18711 VMA_DEBUG_LOG("vmaClearNewBlockStats");
18712 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18713
18714 allocation->ClearNewBlockFlag();
18715 return VK_SUCCESS;
18716 }
18717
vmaSwapReservedBlock(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18718 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
18719 VmaAllocator allocator,
18720 VkImage image,
18721 const VmaAllocationCreateInfo* pCreateInfo,
18722 VmaAllocation* pAllocation,
18723 VmaAllocationInfo* pAllocationInfo)
18724 {
18725 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18726 VMA_DEBUG_LOG("vmaSwapReservedBlock");
18727 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18728
18729 return allocator->SwapReservedBlock(image, pCreateInfo, pAllocation, pAllocationInfo);
18730 }
18731
vmaFreeReservedMemory(VmaAllocator allocator,VmaAllocation allocation)18732 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
18733 VmaAllocator allocator,
18734 VmaAllocation allocation)
18735 {
18736 VMA_ASSERT(allocator);
18737 if (allocation == VK_NULL_HANDLE)
18738 {
18739 return;
18740 }
18741 VMA_DEBUG_LOG("vmaFreeReservedMemory");
18742 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18743
18744 allocator->FreeReservedMemory(
18745 1, // allocationCount
18746 &allocation);
18747 }
18748
vmaGetPreAllocBlockSize(VmaAllocator allocator,uint32_t * pStats)18749 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(VmaAllocator allocator, uint32_t* pStats)
18750 {
18751 VMA_ASSERT(allocator);
18752 VMA_DEBUG_LOG("vmaGetPreAllocBlockSize");
18753
18754 if (pStats != NULL) {
18755 *pStats = allocator->GetPreAllocBlockSize();
18756 }
18757 return VK_SUCCESS;
18758 }
18759
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18760 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18761 VmaAllocator allocator,
18762 VmaAllocation allocation,
18763 VkDeviceSize newSize)
18764 {
18765 VMA_ASSERT(allocator && allocation);
18766
18767 VMA_DEBUG_LOG("vmaResizeAllocation");
18768
18769 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18770
18771 return allocator->ResizeAllocation(allocation, newSize);
18772 }
18773
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18774 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18775 VmaAllocator allocator,
18776 VmaAllocation allocation,
18777 VmaAllocationInfo* pAllocationInfo)
18778 {
18779 VMA_ASSERT(allocator && allocation && pAllocationInfo);
18780
18781 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18782
18783 #if VMA_RECORDING_ENABLED
18784 if(allocator->GetRecorder() != VMA_NULL)
18785 {
18786 allocator->GetRecorder()->RecordGetAllocationInfo(
18787 allocator->GetCurrentFrameIndex(),
18788 allocation);
18789 }
18790 #endif
18791
18792 allocator->GetAllocationInfo(allocation, pAllocationInfo);
18793 }
18794
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18795 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18796 VmaAllocator allocator,
18797 VmaAllocation allocation)
18798 {
18799 VMA_ASSERT(allocator && allocation);
18800
18801 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18802
18803 #if VMA_RECORDING_ENABLED
18804 if(allocator->GetRecorder() != VMA_NULL)
18805 {
18806 allocator->GetRecorder()->RecordTouchAllocation(
18807 allocator->GetCurrentFrameIndex(),
18808 allocation);
18809 }
18810 #endif
18811
18812 return allocator->TouchAllocation(allocation);
18813 }
18814
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18815 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18816 VmaAllocator allocator,
18817 VmaAllocation allocation,
18818 void* pUserData)
18819 {
18820 VMA_ASSERT(allocator && allocation);
18821
18822 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18823
18824 allocation->SetUserData(allocator, pUserData);
18825
18826 #if VMA_RECORDING_ENABLED
18827 if(allocator->GetRecorder() != VMA_NULL)
18828 {
18829 allocator->GetRecorder()->RecordSetAllocationUserData(
18830 allocator->GetCurrentFrameIndex(),
18831 allocation,
18832 pUserData);
18833 }
18834 #endif
18835 }
18836
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18837 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18838 VmaAllocator allocator,
18839 VmaAllocation* pAllocation)
18840 {
18841 VMA_ASSERT(allocator && pAllocation);
18842
18843 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18844
18845 allocator->CreateLostAllocation(pAllocation);
18846
18847 #if VMA_RECORDING_ENABLED
18848 if(allocator->GetRecorder() != VMA_NULL)
18849 {
18850 allocator->GetRecorder()->RecordCreateLostAllocation(
18851 allocator->GetCurrentFrameIndex(),
18852 *pAllocation);
18853 }
18854 #endif
18855 }
18856
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18857 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18858 VmaAllocator allocator,
18859 VmaAllocation allocation,
18860 void** ppData)
18861 {
18862 VMA_ASSERT(allocator && allocation && ppData);
18863
18864 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18865
18866 VkResult res = allocator->Map(allocation, ppData);
18867
18868 #if VMA_RECORDING_ENABLED
18869 if(allocator->GetRecorder() != VMA_NULL)
18870 {
18871 allocator->GetRecorder()->RecordMapMemory(
18872 allocator->GetCurrentFrameIndex(),
18873 allocation);
18874 }
18875 #endif
18876
18877 return res;
18878 }
18879
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18880 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18881 VmaAllocator allocator,
18882 VmaAllocation allocation)
18883 {
18884 VMA_ASSERT(allocator && allocation);
18885
18886 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18887
18888 #if VMA_RECORDING_ENABLED
18889 if(allocator->GetRecorder() != VMA_NULL)
18890 {
18891 allocator->GetRecorder()->RecordUnmapMemory(
18892 allocator->GetCurrentFrameIndex(),
18893 allocation);
18894 }
18895 #endif
18896
18897 allocator->Unmap(allocation);
18898 }
18899
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18900 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18901 {
18902 VMA_ASSERT(allocator && allocation);
18903
18904 VMA_DEBUG_LOG("vmaFlushAllocation");
18905
18906 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18907
18908 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18909
18910 #if VMA_RECORDING_ENABLED
18911 if(allocator->GetRecorder() != VMA_NULL)
18912 {
18913 allocator->GetRecorder()->RecordFlushAllocation(
18914 allocator->GetCurrentFrameIndex(),
18915 allocation, offset, size);
18916 }
18917 #endif
18918
18919 return res;
18920 }
18921
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18922 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18923 {
18924 VMA_ASSERT(allocator && allocation);
18925
18926 VMA_DEBUG_LOG("vmaInvalidateAllocation");
18927
18928 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18929
18930 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18931
18932 #if VMA_RECORDING_ENABLED
18933 if(allocator->GetRecorder() != VMA_NULL)
18934 {
18935 allocator->GetRecorder()->RecordInvalidateAllocation(
18936 allocator->GetCurrentFrameIndex(),
18937 allocation, offset, size);
18938 }
18939 #endif
18940
18941 return res;
18942 }
18943
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18944 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18945 VmaAllocator allocator,
18946 uint32_t allocationCount,
18947 const VmaAllocation* allocations,
18948 const VkDeviceSize* offsets,
18949 const VkDeviceSize* sizes)
18950 {
18951 VMA_ASSERT(allocator);
18952
18953 if(allocationCount == 0)
18954 {
18955 return VK_SUCCESS;
18956 }
18957
18958 VMA_ASSERT(allocations);
18959
18960 VMA_DEBUG_LOG("vmaFlushAllocations");
18961
18962 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18963
18964 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18965
18966 #if VMA_RECORDING_ENABLED
18967 if(allocator->GetRecorder() != VMA_NULL)
18968 {
18969 //TODO
18970 }
18971 #endif
18972
18973 return res;
18974 }
18975
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18976 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
18977 VmaAllocator allocator,
18978 uint32_t allocationCount,
18979 const VmaAllocation* allocations,
18980 const VkDeviceSize* offsets,
18981 const VkDeviceSize* sizes)
18982 {
18983 VMA_ASSERT(allocator);
18984
18985 if(allocationCount == 0)
18986 {
18987 return VK_SUCCESS;
18988 }
18989
18990 VMA_ASSERT(allocations);
18991
18992 VMA_DEBUG_LOG("vmaInvalidateAllocations");
18993
18994 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18995
18996 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
18997
18998 #if VMA_RECORDING_ENABLED
18999 if(allocator->GetRecorder() != VMA_NULL)
19000 {
19001 //TODO
19002 }
19003 #endif
19004
19005 return res;
19006 }
19007
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)19008 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19009 {
19010 VMA_ASSERT(allocator);
19011
19012 VMA_DEBUG_LOG("vmaCheckCorruption");
19013
19014 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19015
19016 return allocator->CheckCorruption(memoryTypeBits);
19017 }
19018
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)19019 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19020 VmaAllocator allocator,
19021 const VmaAllocation* pAllocations,
19022 size_t allocationCount,
19023 VkBool32* pAllocationsChanged,
19024 const VmaDefragmentationInfo *pDefragmentationInfo,
19025 VmaDefragmentationStats* pDefragmentationStats)
19026 {
19027 // Deprecated interface, reimplemented using new one.
19028
19029 VmaDefragmentationInfo2 info2 = {};
19030 info2.allocationCount = (uint32_t)allocationCount;
19031 info2.pAllocations = pAllocations;
19032 info2.pAllocationsChanged = pAllocationsChanged;
19033 if(pDefragmentationInfo != VMA_NULL)
19034 {
19035 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19036 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19037 }
19038 else
19039 {
19040 info2.maxCpuAllocationsToMove = UINT32_MAX;
19041 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19042 }
19043 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19044
19045 VmaDefragmentationContext ctx;
19046 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19047 if(res == VK_NOT_READY)
19048 {
19049 res = vmaDefragmentationEnd( allocator, ctx);
19050 }
19051 return res;
19052 }
19053
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)19054 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19055 VmaAllocator allocator,
19056 const VmaDefragmentationInfo2* pInfo,
19057 VmaDefragmentationStats* pStats,
19058 VmaDefragmentationContext *pContext)
19059 {
19060 VMA_ASSERT(allocator && pInfo && pContext);
19061
19062 // Degenerate case: Nothing to defragment.
19063 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19064 {
19065 return VK_SUCCESS;
19066 }
19067
19068 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19069 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19070 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19071 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19072
19073 VMA_DEBUG_LOG("vmaDefragmentationBegin");
19074
19075 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19076
19077 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19078
19079 #if VMA_RECORDING_ENABLED
19080 if(allocator->GetRecorder() != VMA_NULL)
19081 {
19082 allocator->GetRecorder()->RecordDefragmentationBegin(
19083 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19084 }
19085 #endif
19086
19087 return res;
19088 }
19089
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)19090 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19091 VmaAllocator allocator,
19092 VmaDefragmentationContext context)
19093 {
19094 VMA_ASSERT(allocator);
19095
19096 VMA_DEBUG_LOG("vmaDefragmentationEnd");
19097
19098 if(context != VK_NULL_HANDLE)
19099 {
19100 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19101
19102 #if VMA_RECORDING_ENABLED
19103 if(allocator->GetRecorder() != VMA_NULL)
19104 {
19105 allocator->GetRecorder()->RecordDefragmentationEnd(
19106 allocator->GetCurrentFrameIndex(), context);
19107 }
19108 #endif
19109
19110 return allocator->DefragmentationEnd(context);
19111 }
19112 else
19113 {
19114 return VK_SUCCESS;
19115 }
19116 }
19117
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)19118 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19119 VmaAllocator allocator,
19120 VmaDefragmentationContext context,
19121 VmaDefragmentationPassInfo* pInfo
19122 )
19123 {
19124 VMA_ASSERT(allocator);
19125 VMA_ASSERT(pInfo);
19126 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
19127
19128 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19129
19130 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19131
19132 if(context == VK_NULL_HANDLE)
19133 {
19134 pInfo->moveCount = 0;
19135 return VK_SUCCESS;
19136 }
19137
19138 return allocator->DefragmentationPassBegin(pInfo, context);
19139 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)19140 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19141 VmaAllocator allocator,
19142 VmaDefragmentationContext context)
19143 {
19144 VMA_ASSERT(allocator);
19145
19146 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19147 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19148
19149 if(context == VK_NULL_HANDLE)
19150 return VK_SUCCESS;
19151
19152 return allocator->DefragmentationPassEnd(context);
19153 }
19154
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)19155 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19156 VmaAllocator allocator,
19157 VmaAllocation allocation,
19158 VkBuffer buffer)
19159 {
19160 VMA_ASSERT(allocator && allocation && buffer);
19161
19162 VMA_DEBUG_LOG("vmaBindBufferMemory");
19163
19164 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19165
19166 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19167 }
19168
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)19169 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19170 VmaAllocator allocator,
19171 VmaAllocation allocation,
19172 VkDeviceSize allocationLocalOffset,
19173 VkBuffer buffer,
19174 const void* pNext)
19175 {
19176 VMA_ASSERT(allocator && allocation && buffer);
19177
19178 VMA_DEBUG_LOG("vmaBindBufferMemory2");
19179
19180 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19181
19182 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19183 }
19184
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)19185 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19186 VmaAllocator allocator,
19187 VmaAllocation allocation,
19188 VkImage image)
19189 {
19190 VMA_ASSERT(allocator && allocation && image);
19191
19192 VMA_DEBUG_LOG("vmaBindImageMemory");
19193
19194 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19195
19196 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19197 }
19198
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)19199 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19200 VmaAllocator allocator,
19201 VmaAllocation allocation,
19202 VkDeviceSize allocationLocalOffset,
19203 VkImage image,
19204 const void* pNext)
19205 {
19206 VMA_ASSERT(allocator && allocation && image);
19207
19208 VMA_DEBUG_LOG("vmaBindImageMemory2");
19209
19210 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19211
19212 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19213 }
19214
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19215 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19216 VmaAllocator allocator,
19217 const VkBufferCreateInfo* pBufferCreateInfo,
19218 const VmaAllocationCreateInfo* pAllocationCreateInfo,
19219 VkBuffer* pBuffer,
19220 VmaAllocation* pAllocation,
19221 VmaAllocationInfo* pAllocationInfo)
19222 {
19223 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19224
19225 if(pBufferCreateInfo->size == 0)
19226 {
19227 return VK_ERROR_VALIDATION_FAILED_EXT;
19228 }
19229 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19230 !allocator->m_UseKhrBufferDeviceAddress)
19231 {
19232 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.");
19233 return VK_ERROR_VALIDATION_FAILED_EXT;
19234 }
19235
19236 VMA_DEBUG_LOG("vmaCreateBuffer");
19237
19238 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19239
19240 *pBuffer = VK_NULL_HANDLE;
19241 *pAllocation = VK_NULL_HANDLE;
19242
19243 // 1. Create VkBuffer.
19244 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19245 allocator->m_hDevice,
19246 pBufferCreateInfo,
19247 allocator->GetAllocationCallbacks(),
19248 pBuffer);
19249 if(res >= 0)
19250 {
19251 // 2. vkGetBufferMemoryRequirements.
19252 VkMemoryRequirements vkMemReq = {};
19253 bool requiresDedicatedAllocation = false;
19254 bool prefersDedicatedAllocation = false;
19255 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19256 requiresDedicatedAllocation, prefersDedicatedAllocation);
19257
19258 // 3. Allocate memory using allocator.
19259 res = allocator->AllocateMemory(
19260 vkMemReq,
19261 requiresDedicatedAllocation,
19262 prefersDedicatedAllocation,
19263 *pBuffer, // dedicatedBuffer
19264 pBufferCreateInfo->usage, // dedicatedBufferUsage
19265 VK_NULL_HANDLE, // dedicatedImage
19266 *pAllocationCreateInfo,
19267 VMA_SUBALLOCATION_TYPE_BUFFER,
19268 1, // allocationCount
19269 pAllocation);
19270
19271 #if VMA_RECORDING_ENABLED
19272 if(allocator->GetRecorder() != VMA_NULL)
19273 {
19274 allocator->GetRecorder()->RecordCreateBuffer(
19275 allocator->GetCurrentFrameIndex(),
19276 *pBufferCreateInfo,
19277 *pAllocationCreateInfo,
19278 *pAllocation);
19279 }
19280 #endif
19281
19282 if(res >= 0)
19283 {
19284 // 3. Bind buffer with memory.
19285 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19286 {
19287 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19288 }
19289 if(res >= 0)
19290 {
19291 // All steps succeeded.
19292 #if VMA_STATS_STRING_ENABLED
19293 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19294 #endif
19295 if(pAllocationInfo != VMA_NULL)
19296 {
19297 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19298 }
19299
19300 return VK_SUCCESS;
19301 }
19302 allocator->FreeMemory(
19303 1, // allocationCount
19304 pAllocation);
19305 *pAllocation = VK_NULL_HANDLE;
19306 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19307 *pBuffer = VK_NULL_HANDLE;
19308 return res;
19309 }
19310 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19311 *pBuffer = VK_NULL_HANDLE;
19312 return res;
19313 }
19314 return res;
19315 }
19316
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)19317 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19318 VmaAllocator allocator,
19319 VkBuffer buffer,
19320 VmaAllocation allocation)
19321 {
19322 VMA_ASSERT(allocator);
19323
19324 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19325 {
19326 return;
19327 }
19328
19329 VMA_DEBUG_LOG("vmaDestroyBuffer");
19330
19331 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19332
19333 #if VMA_RECORDING_ENABLED
19334 if(allocator->GetRecorder() != VMA_NULL)
19335 {
19336 allocator->GetRecorder()->RecordDestroyBuffer(
19337 allocator->GetCurrentFrameIndex(),
19338 allocation);
19339 }
19340 #endif
19341
19342 if(buffer != VK_NULL_HANDLE)
19343 {
19344 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19345 }
19346
19347 if(allocation != VK_NULL_HANDLE)
19348 {
19349 allocator->FreeMemory(
19350 1, // allocationCount
19351 &allocation);
19352 }
19353 }
19354
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19355 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19356 VmaAllocator allocator,
19357 const VkImageCreateInfo* pImageCreateInfo,
19358 const VmaAllocationCreateInfo* pAllocationCreateInfo,
19359 VkImage* pImage,
19360 VmaAllocation* pAllocation,
19361 VmaAllocationInfo* pAllocationInfo)
19362 {
19363 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19364
19365 if(pImageCreateInfo->extent.width == 0 ||
19366 pImageCreateInfo->extent.height == 0 ||
19367 pImageCreateInfo->extent.depth == 0 ||
19368 pImageCreateInfo->mipLevels == 0 ||
19369 pImageCreateInfo->arrayLayers == 0)
19370 {
19371 return VK_ERROR_VALIDATION_FAILED_EXT;
19372 }
19373
19374 VMA_DEBUG_LOG("vmaCreateImage");
19375
19376 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19377
19378 *pImage = VK_NULL_HANDLE;
19379 *pAllocation = VK_NULL_HANDLE;
19380
19381 // 1. Create VkImage.
19382 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19383 allocator->m_hDevice,
19384 pImageCreateInfo,
19385 allocator->GetAllocationCallbacks(),
19386 pImage);
19387 if(res >= 0)
19388 {
19389 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19390 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19391 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19392
19393 // 2. Allocate memory using allocator.
19394 VkMemoryRequirements vkMemReq = {};
19395 bool requiresDedicatedAllocation = false;
19396 bool prefersDedicatedAllocation = false;
19397 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19398 requiresDedicatedAllocation, prefersDedicatedAllocation);
19399
19400 res = allocator->AllocateMemory(
19401 vkMemReq,
19402 requiresDedicatedAllocation,
19403 prefersDedicatedAllocation,
19404 VK_NULL_HANDLE, // dedicatedBuffer
19405 UINT32_MAX, // dedicatedBufferUsage
19406 *pImage, // dedicatedImage
19407 *pAllocationCreateInfo,
19408 suballocType,
19409 1, // allocationCount
19410 pAllocation);
19411
19412 #if VMA_RECORDING_ENABLED
19413 if(allocator->GetRecorder() != VMA_NULL)
19414 {
19415 allocator->GetRecorder()->RecordCreateImage(
19416 allocator->GetCurrentFrameIndex(),
19417 *pImageCreateInfo,
19418 *pAllocationCreateInfo,
19419 *pAllocation);
19420 }
19421 #endif
19422
19423 if(res >= 0)
19424 {
19425 // 3. Bind image with memory.
19426 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19427 {
19428 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19429 }
19430 if(res >= 0)
19431 {
19432 // All steps succeeded.
19433 #if VMA_STATS_STRING_ENABLED
19434 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19435 #endif
19436 if(pAllocationInfo != VMA_NULL)
19437 {
19438 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19439 }
19440
19441 return VK_SUCCESS;
19442 }
19443 allocator->FreeMemory(
19444 1, // allocationCount
19445 pAllocation);
19446 *pAllocation = VK_NULL_HANDLE;
19447 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19448 *pImage = VK_NULL_HANDLE;
19449 return res;
19450 }
19451 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19452 *pImage = VK_NULL_HANDLE;
19453 return res;
19454 }
19455 return res;
19456 }
19457
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)19458 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19459 VmaAllocator allocator,
19460 VkImage image,
19461 VmaAllocation allocation)
19462 {
19463 VMA_ASSERT(allocator);
19464
19465 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19466 {
19467 return;
19468 }
19469
19470 VMA_DEBUG_LOG("vmaDestroyImage");
19471
19472 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19473
19474 #if VMA_RECORDING_ENABLED
19475 if(allocator->GetRecorder() != VMA_NULL)
19476 {
19477 allocator->GetRecorder()->RecordDestroyImage(
19478 allocator->GetCurrentFrameIndex(),
19479 allocation);
19480 }
19481 #endif
19482
19483 if(image != VK_NULL_HANDLE)
19484 {
19485 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19486 }
19487 if(allocation != VK_NULL_HANDLE)
19488 {
19489 allocator->FreeMemory(
19490 1, // allocationCount
19491 &allocation);
19492 }
19493 }
19494
19495 // OH ISSUE: VMA preAlloc
vmaCreateFakeImage(VmaAllocator allocator,VkImage * pImage)19496 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
19497 VmaAllocator allocator,
19498 VkImage* pImage)
19499 {
19500 VMA_ASSERT(allocator && pImage);
19501 VMA_DEBUG_LOG("vmaCreateFakeImage");
19502 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19503
19504 *pImage = VK_NULL_HANDLE;
19505 const VkImageCreateInfo imageCreateInfo = {
19506 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
19507 nullptr, // pNext
19508 0, // VkImageCreateFlags
19509 VK_IMAGE_TYPE_2D, // VkImageType
19510 VK_FORMAT_R8G8B8A8_UNORM, // VkFormat
19511 { 10, 10, 1 }, // VkExtent3D
19512 1, // mipLevels
19513 1, // arrayLayers
19514 VK_SAMPLE_COUNT_1_BIT, // samples
19515 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling
19516 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // VkImageUsageFlags
19517 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode
19518 0, // queueFamilyCount
19519 nullptr, // pQueueFamilyIndices
19520 VK_IMAGE_LAYOUT_UNDEFINED // initialLayout
19521 };
19522
19523 // 1. Create VkImage.
19524 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19525 allocator->m_hDevice,
19526 &imageCreateInfo,
19527 nullptr,
19528 pImage);
19529 return res;
19530 }
19531
vmaDestroyFakeImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image)19532 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
19533 VmaAllocator VMA_NOT_NULL allocator,
19534 VkImage VMA_NULLABLE_NON_DISPATCHABLE image)
19535 {
19536 VMA_ASSERT(allocator);
19537 if(image == VK_NULL_HANDLE)
19538 {
19539 return;
19540 }
19541 VMA_DEBUG_LOG("vmaDestroyFakeImage");
19542 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19543
19544 if(image != VK_NULL_HANDLE)
19545 {
19546 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19547 }
19548 }
19549
19550 #endif // #ifdef VMA_IMPLEMENTATION
19551