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
SetBindCompleteFlag(bool flag)6808 void SetBindCompleteFlag(bool flag) { m_BindComplete = flag; }
GetBindCompleteFlag()6809 bool GetBindCompleteFlag() const { return m_BindComplete; }
6810
GetParentPool()6811 VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()6812 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()6813 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()6814 uint32_t GetId() const { return m_Id; }
GetMappedData()6815 void* GetMappedData() const { return m_pMappedData; }
6816
6817 // Validates all data structures inside this object. If not valid, returns false.
6818 bool Validate() const;
6819
6820 VkResult CheckCorruption(VmaAllocator hAllocator);
6821
6822 // ppData can be null.
6823 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
6824 void Unmap(VmaAllocator hAllocator, uint32_t count);
6825
6826 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6827 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
6828
6829 VkResult BindBufferMemory(
6830 const VmaAllocator hAllocator,
6831 const VmaAllocation hAllocation,
6832 VkDeviceSize allocationLocalOffset,
6833 VkBuffer hBuffer,
6834 const void* pNext);
6835 VkResult BindImageMemory(
6836 const VmaAllocator hAllocator,
6837 const VmaAllocation hAllocation,
6838 VkDeviceSize allocationLocalOffset,
6839 VkImage hImage,
6840 const void* pNext);
6841
6842 private:
6843 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6844 uint32_t m_MemoryTypeIndex;
6845 uint32_t m_Id;
6846 VkDeviceMemory m_hMemory;
6847
6848 /*
6849 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
6850 Also protects m_MapCount, m_pMappedData.
6851 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
6852 */
6853 VMA_MUTEX m_Mutex;
6854 uint32_t m_MapCount;
6855 void* m_pMappedData;
6856 bool m_BindComplete;
6857 };
6858
6859 struct VmaPointerLess
6860 {
operatorVmaPointerLess6861 bool operator()(const void* lhs, const void* rhs) const
6862 {
6863 return lhs < rhs;
6864 }
6865 };
6866
6867 struct VmaDefragmentationMove
6868 {
6869 size_t srcBlockIndex;
6870 size_t dstBlockIndex;
6871 VkDeviceSize srcOffset;
6872 VkDeviceSize dstOffset;
6873 VkDeviceSize size;
6874 VmaAllocation hAllocation;
6875 VmaDeviceMemoryBlock* pSrcBlock;
6876 VmaDeviceMemoryBlock* pDstBlock;
6877 };
6878
6879 class VmaDefragmentationAlgorithm;
6880
6881 /*
6882 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
6883 Vulkan memory type.
6884
6885 Synchronized internally with a mutex.
6886 */
6887 struct VmaBlockVector
6888 {
6889 VMA_CLASS_NO_COPY(VmaBlockVector)
6890 public:
6891 VmaBlockVector(
6892 VmaAllocator hAllocator,
6893 VmaPool hParentPool,
6894 uint32_t memoryTypeIndex,
6895 VkDeviceSize preferredBlockSize,
6896 size_t minBlockCount,
6897 size_t maxBlockCount,
6898 VkDeviceSize bufferImageGranularity,
6899 uint32_t frameInUseCount,
6900 bool explicitBlockSize,
6901 uint32_t algorithm);
6902 ~VmaBlockVector();
6903
6904 VkResult CreateMinBlocks();
6905
GetAllocatorVmaBlockVector6906 VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPoolVmaBlockVector6907 VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPoolVmaBlockVector6908 bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndexVmaBlockVector6909 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector6910 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector6911 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector6912 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
GetAlgorithmVmaBlockVector6913 uint32_t GetAlgorithm() const { return m_Algorithm; }
6914
6915 void GetPoolStats(VmaPoolStats* pStats);
6916
6917 bool IsEmpty();
6918 bool IsLastBlockBindComplete();
6919 bool IsCorruptionDetectionEnabled() const;
6920
6921 VkResult Allocate(
6922 uint32_t currentFrameIndex,
6923 VkDeviceSize size,
6924 VkDeviceSize alignment,
6925 const VmaAllocationCreateInfo& createInfo,
6926 VmaSuballocationType suballocType,
6927 size_t allocationCount,
6928 VmaAllocation* pAllocations);
6929
6930 // OH ISSUE: VMA preAlloc
6931 VkResult AllocateReserved(
6932 uint32_t currentFrameIndex,
6933 VkDeviceSize size,
6934 VkDeviceSize alignment,
6935 const VmaAllocationCreateInfo& createInfo,
6936 VmaSuballocationType suballocType,
6937 VmaAllocation* pAllocation);
6938
6939 bool SwapLastBlock(VmaBlockVector* blockVector);
6940
6941 void Free(const VmaAllocation hAllocation);
6942
6943 void FreeReserved(const VmaAllocation hAllocation);
6944
6945 // Adds statistics of this BlockVector to pStats.
6946 void AddStats(VmaStats* pStats);
6947
6948 void FreeEmptyBlock();
6949
6950 #if VMA_STATS_STRING_ENABLED
6951 void PrintDetailedMap(class VmaJsonWriter& json);
6952 #endif
6953
6954 void MakePoolAllocationsLost(
6955 uint32_t currentFrameIndex,
6956 size_t* pLostAllocationCount);
6957 VkResult CheckCorruption();
6958
6959 // Saves results in pCtx->res.
6960 void Defragment(
6961 class VmaBlockVectorDefragmentationContext* pCtx,
6962 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
6963 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
6964 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
6965 VkCommandBuffer commandBuffer);
6966 void DefragmentationEnd(
6967 class VmaBlockVectorDefragmentationContext* pCtx,
6968 uint32_t flags,
6969 VmaDefragmentationStats* pStats);
6970
6971 uint32_t ProcessDefragmentations(
6972 class VmaBlockVectorDefragmentationContext *pCtx,
6973 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves);
6974
6975 void CommitDefragmentations(
6976 class VmaBlockVectorDefragmentationContext *pCtx,
6977 VmaDefragmentationStats* pStats);
6978
6979 ////////////////////////////////////////////////////////////////////////////////
6980 // To be used only while the m_Mutex is locked. Used during defragmentation.
6981
GetBlockCountVmaBlockVector6982 size_t GetBlockCount() const { return m_Blocks.size(); }
GetBlockVmaBlockVector6983 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
6984 size_t CalcAllocationCount() const;
6985 bool IsBufferImageGranularityConflictPossible() const;
IsNewBlockFlagVmaBlockVector6986 bool IsNewBlockFlag() const { return m_NewBlockFlag; }
ClearNewBlockFlagVmaBlockVector6987 void ClearNewBlockFlag() { m_NewBlockFlag = false; }
6988
6989 private:
6990 friend class VmaDefragmentationAlgorithm_Generic;
6991
6992 const VmaAllocator m_hAllocator;
6993 const VmaPool m_hParentPool;
6994 const uint32_t m_MemoryTypeIndex;
6995 const VkDeviceSize m_PreferredBlockSize;
6996 const size_t m_MinBlockCount;
6997 const size_t m_MaxBlockCount;
6998 const VkDeviceSize m_BufferImageGranularity;
6999 const uint32_t m_FrameInUseCount;
7000 const bool m_ExplicitBlockSize;
7001 const uint32_t m_Algorithm;
7002 VMA_RW_MUTEX m_Mutex;
7003
7004 /* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
7005 a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
7006 bool m_HasEmptyBlock;
7007 // Incrementally sorted by sumFreeSize, ascending.
7008 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
7009 uint32_t m_NextBlockId;
7010 bool m_NewBlockFlag = false;
7011
7012 VkDeviceSize CalcMaxBlockSize() const;
7013
7014 // Finds and removes given block from vector.
7015 void Remove(VmaDeviceMemoryBlock* pBlock);
7016
7017 // Performs single step in sorting m_Blocks. They may not be fully sorted
7018 // after this call.
7019 void IncrementallySortBlocks();
7020
7021 VkResult AllocatePage(
7022 uint32_t currentFrameIndex,
7023 VkDeviceSize size,
7024 VkDeviceSize alignment,
7025 const VmaAllocationCreateInfo& createInfo,
7026 VmaSuballocationType suballocType,
7027 VmaAllocation* pAllocation);
7028
7029 // To be used only without CAN_MAKE_OTHER_LOST flag.
7030 VkResult AllocateFromBlock(
7031 VmaDeviceMemoryBlock* pBlock,
7032 uint32_t currentFrameIndex,
7033 VkDeviceSize size,
7034 VkDeviceSize alignment,
7035 VmaAllocationCreateFlags allocFlags,
7036 void* pUserData,
7037 VmaSuballocationType suballocType,
7038 uint32_t strategy,
7039 VmaAllocation* pAllocation);
7040
7041 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
7042
7043 // Saves result to pCtx->res.
7044 void ApplyDefragmentationMovesCpu(
7045 class VmaBlockVectorDefragmentationContext* pDefragCtx,
7046 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
7047 // Saves result to pCtx->res.
7048 void ApplyDefragmentationMovesGpu(
7049 class VmaBlockVectorDefragmentationContext* pDefragCtx,
7050 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7051 VkCommandBuffer commandBuffer);
7052
7053 /*
7054 Used during defragmentation. pDefragmentationStats is optional. It's in/out
7055 - updated with new data.
7056 */
7057 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
7058
7059 void UpdateHasEmptyBlock();
7060 };
7061
7062 struct VmaPool_T
7063 {
7064 VMA_CLASS_NO_COPY(VmaPool_T)
7065 public:
7066 VmaBlockVector m_BlockVector;
7067
7068 VmaPool_T(
7069 VmaAllocator hAllocator,
7070 const VmaPoolCreateInfo& createInfo,
7071 VkDeviceSize preferredBlockSize);
7072 ~VmaPool_T();
7073
GetIdVmaPool_T7074 uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T7075 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
7076
GetNameVmaPool_T7077 const char* GetName() const { return m_Name; }
7078 void SetName(const char* pName);
7079
7080 #if VMA_STATS_STRING_ENABLED
7081 //void PrintDetailedMap(class VmaStringBuilder& sb);
7082 #endif
7083
7084 private:
7085 uint32_t m_Id;
7086 char* m_Name;
7087 };
7088
7089 /*
7090 Performs defragmentation:
7091
7092 - Updates `pBlockVector->m_pMetadata`.
7093 - Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
7094 - Does not move actual data, only returns requested moves as `moves`.
7095 */
7096 class VmaDefragmentationAlgorithm
7097 {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)7098 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
7099 public:
7100 VmaDefragmentationAlgorithm(
7101 VmaAllocator hAllocator,
7102 VmaBlockVector* pBlockVector,
7103 uint32_t currentFrameIndex) :
7104 m_hAllocator(hAllocator),
7105 m_pBlockVector(pBlockVector),
7106 m_CurrentFrameIndex(currentFrameIndex)
7107 {
7108 }
~VmaDefragmentationAlgorithm()7109 virtual ~VmaDefragmentationAlgorithm()
7110 {
7111 }
7112
7113 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
7114 virtual void AddAll() = 0;
7115
7116 virtual VkResult Defragment(
7117 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7118 VkDeviceSize maxBytesToMove,
7119 uint32_t maxAllocationsToMove,
7120 VmaDefragmentationFlags flags) = 0;
7121
7122 virtual VkDeviceSize GetBytesMoved() const = 0;
7123 virtual uint32_t GetAllocationsMoved() const = 0;
7124
7125 protected:
7126 VmaAllocator const m_hAllocator;
7127 VmaBlockVector* const m_pBlockVector;
7128 const uint32_t m_CurrentFrameIndex;
7129
7130 struct AllocationInfo
7131 {
7132 VmaAllocation m_hAllocation;
7133 VkBool32* m_pChanged;
7134
AllocationInfoAllocationInfo7135 AllocationInfo() :
7136 m_hAllocation(VK_NULL_HANDLE),
7137 m_pChanged(VMA_NULL)
7138 {
7139 }
AllocationInfoAllocationInfo7140 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
7141 m_hAllocation(hAlloc),
7142 m_pChanged(pChanged)
7143 {
7144 }
7145 };
7146 };
7147
7148 class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
7149 {
7150 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
7151 public:
7152 VmaDefragmentationAlgorithm_Generic(
7153 VmaAllocator hAllocator,
7154 VmaBlockVector* pBlockVector,
7155 uint32_t currentFrameIndex,
7156 bool overlappingMoveSupported);
7157 virtual ~VmaDefragmentationAlgorithm_Generic();
7158
7159 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7160 virtual void AddAll() { m_AllAllocations = true; }
7161
7162 virtual VkResult Defragment(
7163 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7164 VkDeviceSize maxBytesToMove,
7165 uint32_t maxAllocationsToMove,
7166 VmaDefragmentationFlags flags);
7167
GetBytesMoved()7168 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7169 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7170
7171 private:
7172 uint32_t m_AllocationCount;
7173 bool m_AllAllocations;
7174
7175 VkDeviceSize m_BytesMoved;
7176 uint32_t m_AllocationsMoved;
7177
7178 struct AllocationInfoSizeGreater
7179 {
operatorAllocationInfoSizeGreater7180 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7181 {
7182 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
7183 }
7184 };
7185
7186 struct AllocationInfoOffsetGreater
7187 {
operatorAllocationInfoOffsetGreater7188 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
7189 {
7190 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
7191 }
7192 };
7193
7194 struct BlockInfo
7195 {
7196 size_t m_OriginalBlockIndex;
7197 VmaDeviceMemoryBlock* m_pBlock;
7198 bool m_HasNonMovableAllocations;
7199 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
7200
BlockInfoBlockInfo7201 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
7202 m_OriginalBlockIndex(SIZE_MAX),
7203 m_pBlock(VMA_NULL),
7204 m_HasNonMovableAllocations(true),
7205 m_Allocations(pAllocationCallbacks)
7206 {
7207 }
7208
CalcHasNonMovableAllocationsBlockInfo7209 void CalcHasNonMovableAllocations()
7210 {
7211 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
7212 const size_t defragmentAllocCount = m_Allocations.size();
7213 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
7214 }
7215
SortAllocationsBySizeDescendingBlockInfo7216 void SortAllocationsBySizeDescending()
7217 {
7218 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
7219 }
7220
SortAllocationsByOffsetDescendingBlockInfo7221 void SortAllocationsByOffsetDescending()
7222 {
7223 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
7224 }
7225 };
7226
7227 struct BlockPointerLess
7228 {
operatorBlockPointerLess7229 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
7230 {
7231 return pLhsBlockInfo->m_pBlock < pRhsBlock;
7232 }
operatorBlockPointerLess7233 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7234 {
7235 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
7236 }
7237 };
7238
7239 // 1. Blocks with some non-movable allocations go first.
7240 // 2. Blocks with smaller sumFreeSize go first.
7241 struct BlockInfoCompareMoveDestination
7242 {
operatorBlockInfoCompareMoveDestination7243 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
7244 {
7245 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
7246 {
7247 return true;
7248 }
7249 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
7250 {
7251 return false;
7252 }
7253 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
7254 {
7255 return true;
7256 }
7257 return false;
7258 }
7259 };
7260
7261 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
7262 BlockInfoVector m_Blocks;
7263
7264 VkResult DefragmentRound(
7265 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7266 VkDeviceSize maxBytesToMove,
7267 uint32_t maxAllocationsToMove,
7268 bool freeOldAllocations);
7269
7270 size_t CalcBlocksWithNonMovableCount() const;
7271
7272 static bool MoveMakesSense(
7273 size_t dstBlockIndex, VkDeviceSize dstOffset,
7274 size_t srcBlockIndex, VkDeviceSize srcOffset);
7275 };
7276
7277 class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
7278 {
7279 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
7280 public:
7281 VmaDefragmentationAlgorithm_Fast(
7282 VmaAllocator hAllocator,
7283 VmaBlockVector* pBlockVector,
7284 uint32_t currentFrameIndex,
7285 bool overlappingMoveSupported);
7286 virtual ~VmaDefragmentationAlgorithm_Fast();
7287
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7288 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
AddAll()7289 virtual void AddAll() { m_AllAllocations = true; }
7290
7291 virtual VkResult Defragment(
7292 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
7293 VkDeviceSize maxBytesToMove,
7294 uint32_t maxAllocationsToMove,
7295 VmaDefragmentationFlags flags);
7296
GetBytesMoved()7297 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()7298 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
7299
7300 private:
7301 struct BlockInfo
7302 {
7303 size_t origBlockIndex;
7304 };
7305
7306 class FreeSpaceDatabase
7307 {
7308 public:
FreeSpaceDatabase()7309 FreeSpaceDatabase()
7310 {
7311 FreeSpace s = {};
7312 s.blockInfoIndex = SIZE_MAX;
7313 for(size_t i = 0; i < MAX_COUNT; ++i)
7314 {
7315 m_FreeSpaces[i] = s;
7316 }
7317 }
7318
Register(size_t blockInfoIndex,VkDeviceSize offset,VkDeviceSize size)7319 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
7320 {
7321 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7322 {
7323 return;
7324 }
7325
7326 // Find first invalid or the smallest structure.
7327 size_t bestIndex = SIZE_MAX;
7328 for(size_t i = 0; i < MAX_COUNT; ++i)
7329 {
7330 // Empty structure.
7331 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
7332 {
7333 bestIndex = i;
7334 break;
7335 }
7336 if(m_FreeSpaces[i].size < size &&
7337 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
7338 {
7339 bestIndex = i;
7340 }
7341 }
7342
7343 if(bestIndex != SIZE_MAX)
7344 {
7345 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
7346 m_FreeSpaces[bestIndex].offset = offset;
7347 m_FreeSpaces[bestIndex].size = size;
7348 }
7349 }
7350
Fetch(VkDeviceSize alignment,VkDeviceSize size,size_t & outBlockInfoIndex,VkDeviceSize & outDstOffset)7351 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
7352 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
7353 {
7354 size_t bestIndex = SIZE_MAX;
7355 VkDeviceSize bestFreeSpaceAfter = 0;
7356 for(size_t i = 0; i < MAX_COUNT; ++i)
7357 {
7358 // Structure is valid.
7359 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
7360 {
7361 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
7362 // Allocation fits into this structure.
7363 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
7364 {
7365 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
7366 (dstOffset + size);
7367 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
7368 {
7369 bestIndex = i;
7370 bestFreeSpaceAfter = freeSpaceAfter;
7371 }
7372 }
7373 }
7374 }
7375
7376 if(bestIndex != SIZE_MAX)
7377 {
7378 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
7379 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
7380
7381 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7382 {
7383 // Leave this structure for remaining empty space.
7384 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
7385 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
7386 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
7387 }
7388 else
7389 {
7390 // This structure becomes invalid.
7391 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
7392 }
7393
7394 return true;
7395 }
7396
7397 return false;
7398 }
7399
7400 private:
7401 static const size_t MAX_COUNT = 4;
7402
7403 struct FreeSpace
7404 {
7405 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
7406 VkDeviceSize offset;
7407 VkDeviceSize size;
7408 } m_FreeSpaces[MAX_COUNT];
7409 };
7410
7411 const bool m_OverlappingMoveSupported;
7412
7413 uint32_t m_AllocationCount;
7414 bool m_AllAllocations;
7415
7416 VkDeviceSize m_BytesMoved;
7417 uint32_t m_AllocationsMoved;
7418
7419 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
7420
7421 void PreprocessMetadata();
7422 void PostprocessMetadata();
7423 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
7424 };
7425
7426 struct VmaBlockDefragmentationContext
7427 {
7428 enum BLOCK_FLAG
7429 {
7430 BLOCK_FLAG_USED = 0x00000001,
7431 };
7432 uint32_t flags;
7433 VkBuffer hBuffer;
7434 };
7435
7436 class VmaBlockVectorDefragmentationContext
7437 {
7438 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
7439 public:
7440 VkResult res;
7441 bool mutexLocked;
7442 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
7443 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > defragmentationMoves;
7444 uint32_t defragmentationMovesProcessed;
7445 uint32_t defragmentationMovesCommitted;
7446 bool hasDefragmentationPlan;
7447
7448 VmaBlockVectorDefragmentationContext(
7449 VmaAllocator hAllocator,
7450 VmaPool hCustomPool, // Optional.
7451 VmaBlockVector* pBlockVector,
7452 uint32_t currFrameIndex);
7453 ~VmaBlockVectorDefragmentationContext();
7454
GetCustomPool()7455 VmaPool GetCustomPool() const { return m_hCustomPool; }
GetBlockVector()7456 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
GetAlgorithm()7457 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
7458
7459 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
AddAll()7460 void AddAll() { m_AllAllocations = true; }
7461
7462 void Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags);
7463
7464 private:
7465 const VmaAllocator m_hAllocator;
7466 // Null if not from custom pool.
7467 const VmaPool m_hCustomPool;
7468 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
7469 VmaBlockVector* const m_pBlockVector;
7470 const uint32_t m_CurrFrameIndex;
7471 // Owner of this object.
7472 VmaDefragmentationAlgorithm* m_pAlgorithm;
7473
7474 struct AllocInfo
7475 {
7476 VmaAllocation hAlloc;
7477 VkBool32* pChanged;
7478 };
7479 // Used between constructor and Begin.
7480 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
7481 bool m_AllAllocations;
7482 };
7483
7484 struct VmaDefragmentationContext_T
7485 {
7486 private:
7487 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
7488 public:
7489 VmaDefragmentationContext_T(
7490 VmaAllocator hAllocator,
7491 uint32_t currFrameIndex,
7492 uint32_t flags,
7493 VmaDefragmentationStats* pStats);
7494 ~VmaDefragmentationContext_T();
7495
7496 void AddPools(uint32_t poolCount, const VmaPool* pPools);
7497 void AddAllocations(
7498 uint32_t allocationCount,
7499 const VmaAllocation* pAllocations,
7500 VkBool32* pAllocationsChanged);
7501
7502 /*
7503 Returns:
7504 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
7505 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
7506 - Negative value if error occured and object can be destroyed immediately.
7507 */
7508 VkResult Defragment(
7509 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
7510 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
7511 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags);
7512
7513 VkResult DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo);
7514 VkResult DefragmentPassEnd();
7515
7516 private:
7517 const VmaAllocator m_hAllocator;
7518 const uint32_t m_CurrFrameIndex;
7519 const uint32_t m_Flags;
7520 VmaDefragmentationStats* const m_pStats;
7521
7522 VkDeviceSize m_MaxCpuBytesToMove;
7523 uint32_t m_MaxCpuAllocationsToMove;
7524 VkDeviceSize m_MaxGpuBytesToMove;
7525 uint32_t m_MaxGpuAllocationsToMove;
7526
7527 // Owner of these objects.
7528 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
7529 // Owner of these objects.
7530 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
7531 };
7532
7533 #if VMA_RECORDING_ENABLED
7534
7535 class VmaRecorder
7536 {
7537 public:
7538 VmaRecorder();
7539 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
7540 void WriteConfiguration(
7541 const VkPhysicalDeviceProperties& devProps,
7542 const VkPhysicalDeviceMemoryProperties& memProps,
7543 uint32_t vulkanApiVersion,
7544 bool dedicatedAllocationExtensionEnabled,
7545 bool bindMemory2ExtensionEnabled,
7546 bool memoryBudgetExtensionEnabled,
7547 bool deviceCoherentMemoryExtensionEnabled);
7548 ~VmaRecorder();
7549
7550 void RecordCreateAllocator(uint32_t frameIndex);
7551 void RecordDestroyAllocator(uint32_t frameIndex);
7552 void RecordCreatePool(uint32_t frameIndex,
7553 const VmaPoolCreateInfo& createInfo,
7554 VmaPool pool);
7555 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
7556 void RecordAllocateMemory(uint32_t frameIndex,
7557 const VkMemoryRequirements& vkMemReq,
7558 const VmaAllocationCreateInfo& createInfo,
7559 VmaAllocation allocation);
7560 void RecordAllocateMemoryPages(uint32_t frameIndex,
7561 const VkMemoryRequirements& vkMemReq,
7562 const VmaAllocationCreateInfo& createInfo,
7563 uint64_t allocationCount,
7564 const VmaAllocation* pAllocations);
7565 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
7566 const VkMemoryRequirements& vkMemReq,
7567 bool requiresDedicatedAllocation,
7568 bool prefersDedicatedAllocation,
7569 const VmaAllocationCreateInfo& createInfo,
7570 VmaAllocation allocation);
7571 void RecordAllocateMemoryForImage(uint32_t frameIndex,
7572 const VkMemoryRequirements& vkMemReq,
7573 bool requiresDedicatedAllocation,
7574 bool prefersDedicatedAllocation,
7575 const VmaAllocationCreateInfo& createInfo,
7576 VmaAllocation allocation);
7577 void RecordFreeMemory(uint32_t frameIndex,
7578 VmaAllocation allocation);
7579 void RecordFreeMemoryPages(uint32_t frameIndex,
7580 uint64_t allocationCount,
7581 const VmaAllocation* pAllocations);
7582 void RecordSetAllocationUserData(uint32_t frameIndex,
7583 VmaAllocation allocation,
7584 const void* pUserData);
7585 void RecordCreateLostAllocation(uint32_t frameIndex,
7586 VmaAllocation allocation);
7587 void RecordMapMemory(uint32_t frameIndex,
7588 VmaAllocation allocation);
7589 void RecordUnmapMemory(uint32_t frameIndex,
7590 VmaAllocation allocation);
7591 void RecordFlushAllocation(uint32_t frameIndex,
7592 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7593 void RecordInvalidateAllocation(uint32_t frameIndex,
7594 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
7595 void RecordCreateBuffer(uint32_t frameIndex,
7596 const VkBufferCreateInfo& bufCreateInfo,
7597 const VmaAllocationCreateInfo& allocCreateInfo,
7598 VmaAllocation allocation);
7599 void RecordCreateImage(uint32_t frameIndex,
7600 const VkImageCreateInfo& imageCreateInfo,
7601 const VmaAllocationCreateInfo& allocCreateInfo,
7602 VmaAllocation allocation);
7603 void RecordDestroyBuffer(uint32_t frameIndex,
7604 VmaAllocation allocation);
7605 void RecordDestroyImage(uint32_t frameIndex,
7606 VmaAllocation allocation);
7607 void RecordTouchAllocation(uint32_t frameIndex,
7608 VmaAllocation allocation);
7609 void RecordGetAllocationInfo(uint32_t frameIndex,
7610 VmaAllocation allocation);
7611 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
7612 VmaPool pool);
7613 void RecordDefragmentationBegin(uint32_t frameIndex,
7614 const VmaDefragmentationInfo2& info,
7615 VmaDefragmentationContext ctx);
7616 void RecordDefragmentationEnd(uint32_t frameIndex,
7617 VmaDefragmentationContext ctx);
7618 void RecordSetPoolName(uint32_t frameIndex,
7619 VmaPool pool,
7620 const char* name);
7621
7622 private:
7623 struct CallParams
7624 {
7625 uint32_t threadId;
7626 double time;
7627 };
7628
7629 class UserDataString
7630 {
7631 public:
7632 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
GetString()7633 const char* GetString() const { return m_Str; }
7634
7635 private:
7636 char m_PtrStr[17];
7637 const char* m_Str;
7638 };
7639
7640 bool m_UseMutex;
7641 VmaRecordFlags m_Flags;
7642 FILE* m_File;
7643 VMA_MUTEX m_FileMutex;
7644 std::chrono::time_point<std::chrono::high_resolution_clock> m_RecordingStartTime;
7645
7646 void GetBasicParams(CallParams& outParams);
7647
7648 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
7649 template<typename T>
PrintPointerList(uint64_t count,const T * pItems)7650 void PrintPointerList(uint64_t count, const T* pItems)
7651 {
7652 if(count)
7653 {
7654 fprintf(m_File, "%p", pItems[0]);
7655 for(uint64_t i = 1; i < count; ++i)
7656 {
7657 fprintf(m_File, " %p", pItems[i]);
7658 }
7659 }
7660 }
7661
7662 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
7663 void Flush();
7664 };
7665
7666 #endif // #if VMA_RECORDING_ENABLED
7667
7668 /*
7669 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
7670 */
7671 class VmaAllocationObjectAllocator
7672 {
7673 VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
7674 public:
7675 VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks);
7676
7677 template<typename... Types> VmaAllocation Allocate(Types... args);
7678 void Free(VmaAllocation hAlloc);
7679
7680 private:
7681 VMA_MUTEX m_Mutex;
7682 VmaPoolAllocator<VmaAllocation_T> m_Allocator;
7683 };
7684
7685 struct VmaCurrentBudgetData
7686 {
7687 VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
7688 VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
7689
7690 #if VMA_MEMORY_BUDGET
7691 VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
7692 VMA_RW_MUTEX m_BudgetMutex;
7693 uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
7694 uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
7695 uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
7696 #endif // #if VMA_MEMORY_BUDGET
7697
VmaCurrentBudgetDataVmaCurrentBudgetData7698 VmaCurrentBudgetData()
7699 {
7700 for(uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
7701 {
7702 m_BlockBytes[heapIndex] = 0;
7703 m_AllocationBytes[heapIndex] = 0;
7704 #if VMA_MEMORY_BUDGET
7705 m_VulkanUsage[heapIndex] = 0;
7706 m_VulkanBudget[heapIndex] = 0;
7707 m_BlockBytesAtBudgetFetch[heapIndex] = 0;
7708 #endif
7709 }
7710
7711 #if VMA_MEMORY_BUDGET
7712 m_OperationsSinceBudgetFetch = 0;
7713 #endif
7714 }
7715
AddAllocationVmaCurrentBudgetData7716 void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7717 {
7718 m_AllocationBytes[heapIndex] += allocationSize;
7719 #if VMA_MEMORY_BUDGET
7720 ++m_OperationsSinceBudgetFetch;
7721 #endif
7722 }
7723
RemoveAllocationVmaCurrentBudgetData7724 void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
7725 {
7726 VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); // DELME
7727 m_AllocationBytes[heapIndex] -= allocationSize;
7728 #if VMA_MEMORY_BUDGET
7729 ++m_OperationsSinceBudgetFetch;
7730 #endif
7731 }
7732 };
7733
7734 // Main allocator object.
7735 struct VmaAllocator_T
7736 {
7737 VMA_CLASS_NO_COPY(VmaAllocator_T)
7738 public:
7739 bool m_UseMutex;
7740 uint32_t m_VulkanApiVersion;
7741 bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7742 bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
7743 bool m_UseExtMemoryBudget;
7744 bool m_UseAmdDeviceCoherentMemory;
7745 bool m_UseKhrBufferDeviceAddress;
7746 VkDevice m_hDevice;
7747 VkInstance m_hInstance;
7748 bool m_AllocationCallbacksSpecified;
7749 VkAllocationCallbacks m_AllocationCallbacks;
7750 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
7751 VmaAllocationObjectAllocator m_AllocationObjectAllocator;
7752
7753 // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
7754 uint32_t m_HeapSizeLimitMask;
7755
7756 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
7757 VkPhysicalDeviceMemoryProperties m_MemProps;
7758
7759 // Default pools.
7760 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
7761 // Reserved pools.
7762 VmaBlockVector* m_pReservedBlockVectors[VK_MAX_MEMORY_TYPES];
7763
7764 // Each vector is sorted by memory (handle value).
7765 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
7766 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
7767 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
7768
7769 VmaCurrentBudgetData m_Budget;
7770
7771 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
7772 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
7773 ~VmaAllocator_T();
7774
GetAllocationCallbacksVmaAllocator_T7775 const VkAllocationCallbacks* GetAllocationCallbacks() const
7776 {
7777 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
7778 }
GetVulkanFunctionsVmaAllocator_T7779 const VmaVulkanFunctions& GetVulkanFunctions() const
7780 {
7781 return m_VulkanFunctions;
7782 }
7783
GetPhysicalDeviceVmaAllocator_T7784 VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
7785
GetBufferImageGranularityVmaAllocator_T7786 VkDeviceSize GetBufferImageGranularity() const
7787 {
7788 return VMA_MAX(
7789 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
7790 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
7791 }
7792
GetMemoryHeapCountVmaAllocator_T7793 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T7794 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
7795
MemoryTypeIndexToHeapIndexVmaAllocator_T7796 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
7797 {
7798 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
7799 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
7800 }
7801 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T7802 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
7803 {
7804 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
7805 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
7806 }
7807 // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T7808 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
7809 {
7810 return IsMemoryTypeNonCoherent(memTypeIndex) ?
7811 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
7812 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
7813 }
7814
IsIntegratedGpuVmaAllocator_T7815 bool IsIntegratedGpu() const
7816 {
7817 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
7818 }
7819
GetGlobalMemoryTypeBitsVmaAllocator_T7820 uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
7821
7822 #if VMA_RECORDING_ENABLED
GetRecorderVmaAllocator_T7823 VmaRecorder* GetRecorder() const { return m_pRecorder; }
7824 #endif
7825
7826 void GetBufferMemoryRequirements(
7827 VkBuffer hBuffer,
7828 VkMemoryRequirements& memReq,
7829 bool& requiresDedicatedAllocation,
7830 bool& prefersDedicatedAllocation) const;
7831 void GetImageMemoryRequirements(
7832 VkImage hImage,
7833 VkMemoryRequirements& memReq,
7834 bool& requiresDedicatedAllocation,
7835 bool& prefersDedicatedAllocation) const;
7836
7837 // Main allocation function.
7838 VkResult AllocateMemory(
7839 const VkMemoryRequirements& vkMemReq,
7840 bool requiresDedicatedAllocation,
7841 bool prefersDedicatedAllocation,
7842 VkBuffer dedicatedBuffer,
7843 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7844 VkImage dedicatedImage,
7845 const VmaAllocationCreateInfo& createInfo,
7846 VmaSuballocationType suballocType,
7847 size_t allocationCount,
7848 VmaAllocation* pAllocations);
7849
7850 // Main deallocation function.
7851 void FreeMemory(
7852 size_t allocationCount,
7853 const VmaAllocation* pAllocations);
7854
7855 // OH ISSUE: VMA preAlloc
7856 // pre-allocation
7857 VkResult AllocateReservedMemory(
7858 const VkMemoryRequirements& vkMemReq,
7859 bool requiresDedicatedAllocation,
7860 bool prefersDedicatedAllocation,
7861 VkBuffer dedicatedBuffer,
7862 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
7863 VkImage dedicatedImage,
7864 const VmaAllocationCreateInfo& createInfo,
7865 VmaSuballocationType suballocType,
7866 size_t allocationCount,
7867 VmaAllocation* pAllocations);
7868
7869 // pre-deallocation function.
7870 void FreeReservedMemory(
7871 size_t allocationCount,
7872 const VmaAllocation* pAllocations);
7873
7874 VkResult SwapReservedBlock(
7875 VkImage image,
7876 const VmaAllocationCreateInfo* pCreateInfo,
7877 VmaAllocation* pAllocation,
7878 VmaAllocationInfo* pAllocationInfo);
7879
7880 uint32_t GetPreAllocBlockSize();
7881
7882 VkResult ResizeAllocation(
7883 const VmaAllocation alloc,
7884 VkDeviceSize newSize);
7885
7886 void CalculateStats(VmaStats* pStats);
7887
7888 void FreeEmptyBlock();
7889
7890 void GetBudget(
7891 VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount);
7892
7893 #if VMA_STATS_STRING_ENABLED
7894 void PrintDetailedMap(class VmaJsonWriter& json);
7895 #endif
7896
7897 VkResult DefragmentationBegin(
7898 const VmaDefragmentationInfo2& info,
7899 VmaDefragmentationStats* pStats,
7900 VmaDefragmentationContext* pContext);
7901 VkResult DefragmentationEnd(
7902 VmaDefragmentationContext context);
7903
7904 VkResult DefragmentationPassBegin(
7905 VmaDefragmentationPassInfo* pInfo,
7906 VmaDefragmentationContext context);
7907 VkResult DefragmentationPassEnd(
7908 VmaDefragmentationContext context);
7909
7910 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
7911 bool TouchAllocation(VmaAllocation hAllocation);
7912
7913 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
7914 void DestroyPool(VmaPool pool);
7915 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
7916
7917 void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T7918 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
7919
7920 void MakePoolAllocationsLost(
7921 VmaPool hPool,
7922 size_t* pLostAllocationCount);
7923 VkResult CheckPoolCorruption(VmaPool hPool);
7924 VkResult CheckCorruption(uint32_t memoryTypeBits);
7925
7926 void CreateLostAllocation(VmaAllocation* pAllocation);
7927
7928 // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
7929 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
7930 // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
7931 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
7932 // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
7933 VkResult BindVulkanBuffer(
7934 VkDeviceMemory memory,
7935 VkDeviceSize memoryOffset,
7936 VkBuffer buffer,
7937 const void* pNext);
7938 // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
7939 VkResult BindVulkanImage(
7940 VkDeviceMemory memory,
7941 VkDeviceSize memoryOffset,
7942 VkImage image,
7943 const void* pNext);
7944
7945 VkResult Map(VmaAllocation hAllocation, void** ppData);
7946 void Unmap(VmaAllocation hAllocation);
7947
7948 VkResult BindBufferMemory(
7949 VmaAllocation hAllocation,
7950 VkDeviceSize allocationLocalOffset,
7951 VkBuffer hBuffer,
7952 const void* pNext);
7953 VkResult BindImageMemory(
7954 VmaAllocation hAllocation,
7955 VkDeviceSize allocationLocalOffset,
7956 VkImage hImage,
7957 const void* pNext);
7958
7959 VkResult FlushOrInvalidateAllocation(
7960 VmaAllocation hAllocation,
7961 VkDeviceSize offset, VkDeviceSize size,
7962 VMA_CACHE_OPERATION op);
7963 VkResult FlushOrInvalidateAllocations(
7964 uint32_t allocationCount,
7965 const VmaAllocation* allocations,
7966 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
7967 VMA_CACHE_OPERATION op);
7968
7969 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
7970
7971 /*
7972 Returns bit mask of memory types that can support defragmentation on GPU as
7973 they support creation of required buffer for copy operations.
7974 */
7975 uint32_t GetGpuDefragmentationMemoryTypeBits();
7976
7977 private:
7978 VkDeviceSize m_PreferredLargeHeapBlockSize;
7979
7980 VkPhysicalDevice m_PhysicalDevice;
7981 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
7982 VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
7983
7984 VMA_RW_MUTEX m_PoolsMutex;
7985 // Protected by m_PoolsMutex. Sorted by pointer value.
7986 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
7987 uint32_t m_NextPoolId;
7988
7989 VmaVulkanFunctions m_VulkanFunctions;
7990
7991 // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
7992 uint32_t m_GlobalMemoryTypeBits;
7993
7994 #if VMA_RECORDING_ENABLED
7995 VmaRecorder* m_pRecorder;
7996 #endif
7997
7998 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
7999
8000 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
8001 void ImportVulkanFunctions_Static();
8002 #endif
8003
8004 void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
8005
8006 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
8007 void ImportVulkanFunctions_Dynamic();
8008 #endif
8009
8010 void ValidateVulkanFunctions();
8011
8012 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
8013
8014 VkResult AllocateMemoryOfType(
8015 VkDeviceSize size,
8016 VkDeviceSize alignment,
8017 bool dedicatedAllocation,
8018 VkBuffer dedicatedBuffer,
8019 VkBufferUsageFlags dedicatedBufferUsage,
8020 VkImage dedicatedImage,
8021 const VmaAllocationCreateInfo& createInfo,
8022 uint32_t memTypeIndex,
8023 VmaSuballocationType suballocType,
8024 size_t allocationCount,
8025 VmaAllocation* pAllocations);
8026
8027 // Helper function only to be used inside AllocateDedicatedMemory.
8028 VkResult AllocateDedicatedMemoryPage(
8029 VkDeviceSize size,
8030 VmaSuballocationType suballocType,
8031 uint32_t memTypeIndex,
8032 const VkMemoryAllocateInfo& allocInfo,
8033 bool map,
8034 bool isUserDataString,
8035 void* pUserData,
8036 VmaAllocation* pAllocation);
8037
8038 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
8039 VkResult AllocateDedicatedMemory(
8040 VkDeviceSize size,
8041 VmaSuballocationType suballocType,
8042 uint32_t memTypeIndex,
8043 bool withinBudget,
8044 bool map,
8045 bool isUserDataString,
8046 void* pUserData,
8047 VkBuffer dedicatedBuffer,
8048 VkBufferUsageFlags dedicatedBufferUsage,
8049 VkImage dedicatedImage,
8050 size_t allocationCount,
8051 VmaAllocation* pAllocations);
8052
8053 void FreeDedicatedMemory(const VmaAllocation allocation);
8054
8055 /*
8056 Calculates and returns bit mask of memory types that can support defragmentation
8057 on GPU as they support creation of required buffer for copy operations.
8058 */
8059 uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
8060
8061 uint32_t CalculateGlobalMemoryTypeBits() const;
8062
8063 bool GetFlushOrInvalidateRange(
8064 VmaAllocation allocation,
8065 VkDeviceSize offset, VkDeviceSize size,
8066 VkMappedMemoryRange& outRange) const;
8067
8068 #if VMA_MEMORY_BUDGET
8069 void UpdateVulkanBudget();
8070 #endif // #if VMA_MEMORY_BUDGET
8071 };
8072
8073 ////////////////////////////////////////////////////////////////////////////////
8074 // Memory allocation #2 after VmaAllocator_T definition
8075
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)8076 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
8077 {
8078 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
8079 }
8080
VmaFree(VmaAllocator hAllocator,void * ptr)8081 static void VmaFree(VmaAllocator hAllocator, void* ptr)
8082 {
8083 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
8084 }
8085
8086 template<typename T>
VmaAllocate(VmaAllocator hAllocator)8087 static T* VmaAllocate(VmaAllocator hAllocator)
8088 {
8089 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
8090 }
8091
8092 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)8093 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
8094 {
8095 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
8096 }
8097
8098 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)8099 static void vma_delete(VmaAllocator hAllocator, T* ptr)
8100 {
8101 if(ptr != VMA_NULL)
8102 {
8103 ptr->~T();
8104 VmaFree(hAllocator, ptr);
8105 }
8106 }
8107
8108 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)8109 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
8110 {
8111 if(ptr != VMA_NULL)
8112 {
8113 for(size_t i = count; i--; )
8114 ptr[i].~T();
8115 VmaFree(hAllocator, ptr);
8116 }
8117 }
8118
8119 ////////////////////////////////////////////////////////////////////////////////
8120 // VmaStringBuilder
8121
8122 #if VMA_STATS_STRING_ENABLED
8123
8124 class VmaStringBuilder
8125 {
8126 public:
VmaStringBuilder(VmaAllocator alloc)8127 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()8128 size_t GetLength() const { return m_Data.size(); }
GetData()8129 const char* GetData() const { return m_Data.data(); }
8130
Add(char ch)8131 void Add(char ch) { m_Data.push_back(ch); }
8132 void Add(const char* pStr);
AddNewLine()8133 void AddNewLine() { Add('\n'); }
8134 void AddNumber(uint32_t num);
8135 void AddNumber(uint64_t num);
8136 void AddPointer(const void* ptr);
8137
8138 private:
8139 VmaVector< char, VmaStlAllocator<char> > m_Data;
8140 };
8141
Add(const char * pStr)8142 void VmaStringBuilder::Add(const char* pStr)
8143 {
8144 const size_t strLen = strlen(pStr);
8145 if(strLen > 0)
8146 {
8147 const size_t oldCount = m_Data.size();
8148 m_Data.resize(oldCount + strLen);
8149 memcpy(m_Data.data() + oldCount, pStr, strLen);
8150 }
8151 }
8152
AddNumber(uint32_t num)8153 void VmaStringBuilder::AddNumber(uint32_t num)
8154 {
8155 char buf[11];
8156 buf[10] = '\0';
8157 char *p = &buf[10];
8158 do
8159 {
8160 *--p = '0' + (num % 10);
8161 num /= 10;
8162 }
8163 while(num);
8164 Add(p);
8165 }
8166
AddNumber(uint64_t num)8167 void VmaStringBuilder::AddNumber(uint64_t num)
8168 {
8169 char buf[21];
8170 buf[20] = '\0';
8171 char *p = &buf[20];
8172 do
8173 {
8174 *--p = '0' + (num % 10);
8175 num /= 10;
8176 }
8177 while(num);
8178 Add(p);
8179 }
8180
AddPointer(const void * ptr)8181 void VmaStringBuilder::AddPointer(const void* ptr)
8182 {
8183 char buf[21];
8184 VmaPtrToStr(buf, sizeof(buf), ptr);
8185 Add(buf);
8186 }
8187
8188 #endif // #if VMA_STATS_STRING_ENABLED
8189
8190 ////////////////////////////////////////////////////////////////////////////////
8191 // VmaJsonWriter
8192
8193 #if VMA_STATS_STRING_ENABLED
8194
8195 class VmaJsonWriter
8196 {
8197 VMA_CLASS_NO_COPY(VmaJsonWriter)
8198 public:
8199 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
8200 ~VmaJsonWriter();
8201
8202 void BeginObject(bool singleLine = false);
8203 void EndObject();
8204
8205 void BeginArray(bool singleLine = false);
8206 void EndArray();
8207
8208 void WriteString(const char* pStr);
8209 void BeginString(const char* pStr = VMA_NULL);
8210 void ContinueString(const char* pStr);
8211 void ContinueString(uint32_t n);
8212 void ContinueString(uint64_t n);
8213 void ContinueString_Pointer(const void* ptr);
8214 void EndString(const char* pStr = VMA_NULL);
8215
8216 void WriteNumber(uint32_t n);
8217 void WriteNumber(uint64_t n);
8218 void WriteBool(bool b);
8219 void WriteNull();
8220
8221 private:
8222 static const char* const INDENT;
8223
8224 enum COLLECTION_TYPE
8225 {
8226 COLLECTION_TYPE_OBJECT,
8227 COLLECTION_TYPE_ARRAY,
8228 };
8229 struct StackItem
8230 {
8231 COLLECTION_TYPE type;
8232 uint32_t valueCount;
8233 bool singleLineMode;
8234 };
8235
8236 VmaStringBuilder& m_SB;
8237 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
8238 bool m_InsideString;
8239
8240 void BeginValue(bool isString);
8241 void WriteIndent(bool oneLess = false);
8242 };
8243
8244 const char* const VmaJsonWriter::INDENT = " ";
8245
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)8246 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
8247 m_SB(sb),
8248 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
8249 m_InsideString(false)
8250 {
8251 }
8252
~VmaJsonWriter()8253 VmaJsonWriter::~VmaJsonWriter()
8254 {
8255 VMA_ASSERT(!m_InsideString);
8256 VMA_ASSERT(m_Stack.empty());
8257 }
8258
BeginObject(bool singleLine)8259 void VmaJsonWriter::BeginObject(bool singleLine)
8260 {
8261 VMA_ASSERT(!m_InsideString);
8262
8263 BeginValue(false);
8264 m_SB.Add('{');
8265
8266 StackItem item;
8267 item.type = COLLECTION_TYPE_OBJECT;
8268 item.valueCount = 0;
8269 item.singleLineMode = singleLine;
8270 m_Stack.push_back(item);
8271 }
8272
EndObject()8273 void VmaJsonWriter::EndObject()
8274 {
8275 VMA_ASSERT(!m_InsideString);
8276
8277 WriteIndent(true);
8278 m_SB.Add('}');
8279
8280 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
8281 m_Stack.pop_back();
8282 }
8283
BeginArray(bool singleLine)8284 void VmaJsonWriter::BeginArray(bool singleLine)
8285 {
8286 VMA_ASSERT(!m_InsideString);
8287
8288 BeginValue(false);
8289 m_SB.Add('[');
8290
8291 StackItem item;
8292 item.type = COLLECTION_TYPE_ARRAY;
8293 item.valueCount = 0;
8294 item.singleLineMode = singleLine;
8295 m_Stack.push_back(item);
8296 }
8297
EndArray()8298 void VmaJsonWriter::EndArray()
8299 {
8300 VMA_ASSERT(!m_InsideString);
8301
8302 WriteIndent(true);
8303 m_SB.Add(']');
8304
8305 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
8306 m_Stack.pop_back();
8307 }
8308
WriteString(const char * pStr)8309 void VmaJsonWriter::WriteString(const char* pStr)
8310 {
8311 BeginString(pStr);
8312 EndString();
8313 }
8314
BeginString(const char * pStr)8315 void VmaJsonWriter::BeginString(const char* pStr)
8316 {
8317 VMA_ASSERT(!m_InsideString);
8318
8319 BeginValue(true);
8320 m_SB.Add('"');
8321 m_InsideString = true;
8322 if(pStr != VMA_NULL && pStr[0] != '\0')
8323 {
8324 ContinueString(pStr);
8325 }
8326 }
8327
ContinueString(const char * pStr)8328 void VmaJsonWriter::ContinueString(const char* pStr)
8329 {
8330 VMA_ASSERT(m_InsideString);
8331
8332 const size_t strLen = strlen(pStr);
8333 for(size_t i = 0; i < strLen; ++i)
8334 {
8335 char ch = pStr[i];
8336 if(ch == '\\')
8337 {
8338 m_SB.Add("\\\\");
8339 }
8340 else if(ch == '"')
8341 {
8342 m_SB.Add("\\\"");
8343 }
8344 else if(ch >= 32)
8345 {
8346 m_SB.Add(ch);
8347 }
8348 else switch(ch)
8349 {
8350 case '\b':
8351 m_SB.Add("\\b");
8352 break;
8353 case '\f':
8354 m_SB.Add("\\f");
8355 break;
8356 case '\n':
8357 m_SB.Add("\\n");
8358 break;
8359 case '\r':
8360 m_SB.Add("\\r");
8361 break;
8362 case '\t':
8363 m_SB.Add("\\t");
8364 break;
8365 default:
8366 VMA_ASSERT(0 && "Character not currently supported.");
8367 break;
8368 }
8369 }
8370 }
8371
ContinueString(uint32_t n)8372 void VmaJsonWriter::ContinueString(uint32_t n)
8373 {
8374 VMA_ASSERT(m_InsideString);
8375 m_SB.AddNumber(n);
8376 }
8377
ContinueString(uint64_t n)8378 void VmaJsonWriter::ContinueString(uint64_t n)
8379 {
8380 VMA_ASSERT(m_InsideString);
8381 m_SB.AddNumber(n);
8382 }
8383
ContinueString_Pointer(const void * ptr)8384 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
8385 {
8386 VMA_ASSERT(m_InsideString);
8387 m_SB.AddPointer(ptr);
8388 }
8389
EndString(const char * pStr)8390 void VmaJsonWriter::EndString(const char* pStr)
8391 {
8392 VMA_ASSERT(m_InsideString);
8393 if(pStr != VMA_NULL && pStr[0] != '\0')
8394 {
8395 ContinueString(pStr);
8396 }
8397 m_SB.Add('"');
8398 m_InsideString = false;
8399 }
8400
WriteNumber(uint32_t n)8401 void VmaJsonWriter::WriteNumber(uint32_t n)
8402 {
8403 VMA_ASSERT(!m_InsideString);
8404 BeginValue(false);
8405 m_SB.AddNumber(n);
8406 }
8407
WriteNumber(uint64_t n)8408 void VmaJsonWriter::WriteNumber(uint64_t n)
8409 {
8410 VMA_ASSERT(!m_InsideString);
8411 BeginValue(false);
8412 m_SB.AddNumber(n);
8413 }
8414
WriteBool(bool b)8415 void VmaJsonWriter::WriteBool(bool b)
8416 {
8417 VMA_ASSERT(!m_InsideString);
8418 BeginValue(false);
8419 m_SB.Add(b ? "true" : "false");
8420 }
8421
WriteNull()8422 void VmaJsonWriter::WriteNull()
8423 {
8424 VMA_ASSERT(!m_InsideString);
8425 BeginValue(false);
8426 m_SB.Add("null");
8427 }
8428
BeginValue(bool isString)8429 void VmaJsonWriter::BeginValue(bool isString)
8430 {
8431 if(!m_Stack.empty())
8432 {
8433 StackItem& currItem = m_Stack.back();
8434 if(currItem.type == COLLECTION_TYPE_OBJECT &&
8435 currItem.valueCount % 2 == 0)
8436 {
8437 VMA_ASSERT(isString);
8438 }
8439
8440 if(currItem.type == COLLECTION_TYPE_OBJECT &&
8441 currItem.valueCount % 2 != 0)
8442 {
8443 m_SB.Add(": ");
8444 }
8445 else if(currItem.valueCount > 0)
8446 {
8447 m_SB.Add(", ");
8448 WriteIndent();
8449 }
8450 else
8451 {
8452 WriteIndent();
8453 }
8454 ++currItem.valueCount;
8455 }
8456 }
8457
WriteIndent(bool oneLess)8458 void VmaJsonWriter::WriteIndent(bool oneLess)
8459 {
8460 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
8461 {
8462 m_SB.AddNewLine();
8463
8464 size_t count = m_Stack.size();
8465 if(count > 0 && oneLess)
8466 {
8467 --count;
8468 }
8469 for(size_t i = 0; i < count; ++i)
8470 {
8471 m_SB.Add(INDENT);
8472 }
8473 }
8474 }
8475
8476 #endif // #if VMA_STATS_STRING_ENABLED
8477
8478 ////////////////////////////////////////////////////////////////////////////////
8479
SetUserData(VmaAllocator hAllocator,void * pUserData)8480 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
8481 {
8482 if(IsUserDataString())
8483 {
8484 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
8485
8486 FreeUserDataString(hAllocator);
8487
8488 if(pUserData != VMA_NULL)
8489 {
8490 m_pUserData = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), (const char*)pUserData);
8491 }
8492 }
8493 else
8494 {
8495 m_pUserData = pUserData;
8496 }
8497 }
8498
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)8499 void VmaAllocation_T::ChangeBlockAllocation(
8500 VmaAllocator hAllocator,
8501 VmaDeviceMemoryBlock* block,
8502 VkDeviceSize offset)
8503 {
8504 VMA_ASSERT(block != VMA_NULL);
8505 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8506
8507 // Move mapping reference counter from old block to new block.
8508 if(block != m_BlockAllocation.m_Block)
8509 {
8510 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
8511 if(IsPersistentMap())
8512 ++mapRefCount;
8513 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
8514 block->Map(hAllocator, mapRefCount, VMA_NULL);
8515 }
8516
8517 m_BlockAllocation.m_Block = block;
8518 m_BlockAllocation.m_Offset = offset;
8519 }
8520
ChangeOffset(VkDeviceSize newOffset)8521 void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
8522 {
8523 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
8524 m_BlockAllocation.m_Offset = newOffset;
8525 }
8526
GetOffset()8527 VkDeviceSize VmaAllocation_T::GetOffset() const
8528 {
8529 switch(m_Type)
8530 {
8531 case ALLOCATION_TYPE_BLOCK:
8532 return m_BlockAllocation.m_Offset;
8533 case ALLOCATION_TYPE_DEDICATED:
8534 return 0;
8535 default:
8536 VMA_ASSERT(0);
8537 return 0;
8538 }
8539 }
8540
GetMemory()8541 VkDeviceMemory VmaAllocation_T::GetMemory() const
8542 {
8543 switch(m_Type)
8544 {
8545 case ALLOCATION_TYPE_BLOCK:
8546 return m_BlockAllocation.m_Block->GetDeviceMemory();
8547 case ALLOCATION_TYPE_DEDICATED:
8548 return m_DedicatedAllocation.m_hMemory;
8549 default:
8550 VMA_ASSERT(0);
8551 return VK_NULL_HANDLE;
8552 }
8553 }
8554
GetMappedData()8555 void* VmaAllocation_T::GetMappedData() const
8556 {
8557 switch(m_Type)
8558 {
8559 case ALLOCATION_TYPE_BLOCK:
8560 if(m_MapCount != 0)
8561 {
8562 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
8563 VMA_ASSERT(pBlockData != VMA_NULL);
8564 return (char*)pBlockData + m_BlockAllocation.m_Offset;
8565 }
8566 else
8567 {
8568 return VMA_NULL;
8569 }
8570 break;
8571 case ALLOCATION_TYPE_DEDICATED:
8572 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
8573 return m_DedicatedAllocation.m_pMappedData;
8574 default:
8575 VMA_ASSERT(0);
8576 return VMA_NULL;
8577 }
8578 }
8579
CanBecomeLost()8580 bool VmaAllocation_T::CanBecomeLost() const
8581 {
8582 switch(m_Type)
8583 {
8584 case ALLOCATION_TYPE_BLOCK:
8585 return m_BlockAllocation.m_CanBecomeLost;
8586 case ALLOCATION_TYPE_DEDICATED:
8587 return false;
8588 default:
8589 VMA_ASSERT(0);
8590 return false;
8591 }
8592 }
8593
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)8594 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8595 {
8596 VMA_ASSERT(CanBecomeLost());
8597
8598 /*
8599 Warning: This is a carefully designed algorithm.
8600 Do not modify unless you really know what you're doing :)
8601 */
8602 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
8603 for(;;)
8604 {
8605 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8606 {
8607 VMA_ASSERT(0);
8608 return false;
8609 }
8610 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
8611 {
8612 return false;
8613 }
8614 else // Last use time earlier than current time.
8615 {
8616 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
8617 {
8618 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
8619 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
8620 return true;
8621 }
8622 }
8623 }
8624 }
8625
8626 #if VMA_STATS_STRING_ENABLED
8627
8628 // Correspond to values of enum VmaSuballocationType.
8629 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
8630 "FREE",
8631 "UNKNOWN",
8632 "BUFFER",
8633 "IMAGE_UNKNOWN",
8634 "IMAGE_LINEAR",
8635 "IMAGE_OPTIMAL",
8636 };
8637
PrintParameters(class VmaJsonWriter & json)8638 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
8639 {
8640 json.WriteString("Type");
8641 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
8642
8643 json.WriteString("Size");
8644 json.WriteNumber(m_Size);
8645
8646 if(m_pUserData != VMA_NULL)
8647 {
8648 json.WriteString("UserData");
8649 if(IsUserDataString())
8650 {
8651 json.WriteString((const char*)m_pUserData);
8652 }
8653 else
8654 {
8655 json.BeginString();
8656 json.ContinueString_Pointer(m_pUserData);
8657 json.EndString();
8658 }
8659 }
8660
8661 json.WriteString("CreationFrameIndex");
8662 json.WriteNumber(m_CreationFrameIndex);
8663
8664 json.WriteString("LastUseFrameIndex");
8665 json.WriteNumber(GetLastUseFrameIndex());
8666
8667 if(m_BufferImageUsage != 0)
8668 {
8669 json.WriteString("Usage");
8670 json.WriteNumber(m_BufferImageUsage);
8671 }
8672 }
8673
8674 #endif
8675
FreeUserDataString(VmaAllocator hAllocator)8676 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
8677 {
8678 VMA_ASSERT(IsUserDataString());
8679 VmaFreeString(hAllocator->GetAllocationCallbacks(), (char*)m_pUserData);
8680 m_pUserData = VMA_NULL;
8681 }
8682
BlockAllocMap()8683 void VmaAllocation_T::BlockAllocMap()
8684 {
8685 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8686
8687 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8688 {
8689 ++m_MapCount;
8690 }
8691 else
8692 {
8693 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
8694 }
8695 }
8696
BlockAllocUnmap()8697 void VmaAllocation_T::BlockAllocUnmap()
8698 {
8699 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
8700
8701 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8702 {
8703 --m_MapCount;
8704 }
8705 else
8706 {
8707 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
8708 }
8709 }
8710
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)8711 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
8712 {
8713 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8714
8715 if(m_MapCount != 0)
8716 {
8717 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
8718 {
8719 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
8720 *ppData = m_DedicatedAllocation.m_pMappedData;
8721 ++m_MapCount;
8722 return VK_SUCCESS;
8723 }
8724 else
8725 {
8726 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
8727 return VK_ERROR_MEMORY_MAP_FAILED;
8728 }
8729 }
8730 else
8731 {
8732 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
8733 hAllocator->m_hDevice,
8734 m_DedicatedAllocation.m_hMemory,
8735 0, // offset
8736 VK_WHOLE_SIZE,
8737 0, // flags
8738 ppData);
8739 if(result == VK_SUCCESS)
8740 {
8741 m_DedicatedAllocation.m_pMappedData = *ppData;
8742 m_MapCount = 1;
8743 }
8744 return result;
8745 }
8746 }
8747
DedicatedAllocUnmap(VmaAllocator hAllocator)8748 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
8749 {
8750 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
8751
8752 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
8753 {
8754 --m_MapCount;
8755 if(m_MapCount == 0)
8756 {
8757 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
8758 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
8759 hAllocator->m_hDevice,
8760 m_DedicatedAllocation.m_hMemory);
8761 }
8762 }
8763 else
8764 {
8765 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
8766 }
8767 }
8768
8769 #if VMA_STATS_STRING_ENABLED
8770
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)8771 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
8772 {
8773 json.BeginObject();
8774
8775 json.WriteString("Blocks");
8776 json.WriteNumber(stat.blockCount);
8777
8778 json.WriteString("Allocations");
8779 json.WriteNumber(stat.allocationCount);
8780
8781 json.WriteString("UnusedRanges");
8782 json.WriteNumber(stat.unusedRangeCount);
8783
8784 json.WriteString("UsedBytes");
8785 json.WriteNumber(stat.usedBytes);
8786
8787 json.WriteString("UnusedBytes");
8788 json.WriteNumber(stat.unusedBytes);
8789
8790 if(stat.allocationCount > 1)
8791 {
8792 json.WriteString("AllocationSize");
8793 json.BeginObject(true);
8794 json.WriteString("Min");
8795 json.WriteNumber(stat.allocationSizeMin);
8796 json.WriteString("Avg");
8797 json.WriteNumber(stat.allocationSizeAvg);
8798 json.WriteString("Max");
8799 json.WriteNumber(stat.allocationSizeMax);
8800 json.EndObject();
8801 }
8802
8803 if(stat.unusedRangeCount > 1)
8804 {
8805 json.WriteString("UnusedRangeSize");
8806 json.BeginObject(true);
8807 json.WriteString("Min");
8808 json.WriteNumber(stat.unusedRangeSizeMin);
8809 json.WriteString("Avg");
8810 json.WriteNumber(stat.unusedRangeSizeAvg);
8811 json.WriteString("Max");
8812 json.WriteNumber(stat.unusedRangeSizeMax);
8813 json.EndObject();
8814 }
8815
8816 json.EndObject();
8817 }
8818
8819 #endif // #if VMA_STATS_STRING_ENABLED
8820
8821 struct VmaSuballocationItemSizeLess
8822 {
operatorVmaSuballocationItemSizeLess8823 bool operator()(
8824 const VmaSuballocationList::iterator lhs,
8825 const VmaSuballocationList::iterator rhs) const
8826 {
8827 return lhs->size < rhs->size;
8828 }
operatorVmaSuballocationItemSizeLess8829 bool operator()(
8830 const VmaSuballocationList::iterator lhs,
8831 VkDeviceSize rhsSize) const
8832 {
8833 return lhs->size < rhsSize;
8834 }
8835 };
8836
8837
8838 ////////////////////////////////////////////////////////////////////////////////
8839 // class VmaBlockMetadata
8840
VmaBlockMetadata(VmaAllocator hAllocator)8841 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
8842 m_Size(0),
8843 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
8844 {
8845 }
8846
8847 #if VMA_STATS_STRING_ENABLED
8848
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)8849 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
8850 VkDeviceSize unusedBytes,
8851 size_t allocationCount,
8852 size_t unusedRangeCount) const
8853 {
8854 json.BeginObject();
8855
8856 json.WriteString("TotalBytes");
8857 json.WriteNumber(GetSize());
8858
8859 json.WriteString("UnusedBytes");
8860 json.WriteNumber(unusedBytes);
8861
8862 json.WriteString("Allocations");
8863 json.WriteNumber((uint64_t)allocationCount);
8864
8865 json.WriteString("UnusedRanges");
8866 json.WriteNumber((uint64_t)unusedRangeCount);
8867
8868 json.WriteString("Suballocations");
8869 json.BeginArray();
8870 }
8871
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VmaAllocation hAllocation)8872 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
8873 VkDeviceSize offset,
8874 VmaAllocation hAllocation) const
8875 {
8876 json.BeginObject(true);
8877
8878 json.WriteString("Offset");
8879 json.WriteNumber(offset);
8880
8881 hAllocation->PrintParameters(json);
8882
8883 json.EndObject();
8884 }
8885
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)8886 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
8887 VkDeviceSize offset,
8888 VkDeviceSize size) const
8889 {
8890 json.BeginObject(true);
8891
8892 json.WriteString("Offset");
8893 json.WriteNumber(offset);
8894
8895 json.WriteString("Type");
8896 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
8897
8898 json.WriteString("Size");
8899 json.WriteNumber(size);
8900
8901 json.EndObject();
8902 }
8903
PrintDetailedMap_End(class VmaJsonWriter & json)8904 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
8905 {
8906 json.EndArray();
8907 json.EndObject();
8908 }
8909
8910 #endif // #if VMA_STATS_STRING_ENABLED
8911
8912 ////////////////////////////////////////////////////////////////////////////////
8913 // class VmaBlockMetadata_Generic
8914
VmaBlockMetadata_Generic(VmaAllocator hAllocator)8915 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
8916 VmaBlockMetadata(hAllocator),
8917 m_FreeCount(0),
8918 m_SumFreeSize(0),
8919 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8920 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
8921 {
8922 }
8923
~VmaBlockMetadata_Generic()8924 VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
8925 {
8926 }
8927
Init(VkDeviceSize size)8928 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
8929 {
8930 VmaBlockMetadata::Init(size);
8931
8932 m_FreeCount = 1;
8933 m_SumFreeSize = size;
8934
8935 VmaSuballocation suballoc = {};
8936 suballoc.offset = 0;
8937 suballoc.size = size;
8938 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8939 suballoc.hAllocation = VK_NULL_HANDLE;
8940
8941 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8942 m_Suballocations.push_back(suballoc);
8943 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
8944 --suballocItem;
8945 m_FreeSuballocationsBySize.push_back(suballocItem);
8946 }
8947
Validate()8948 bool VmaBlockMetadata_Generic::Validate() const
8949 {
8950 VMA_VALIDATE(!m_Suballocations.empty());
8951
8952 // Expected offset of new suballocation as calculated from previous ones.
8953 VkDeviceSize calculatedOffset = 0;
8954 // Expected number of free suballocations as calculated from traversing their list.
8955 uint32_t calculatedFreeCount = 0;
8956 // Expected sum size of free suballocations as calculated from traversing their list.
8957 VkDeviceSize calculatedSumFreeSize = 0;
8958 // Expected number of free suballocations that should be registered in
8959 // m_FreeSuballocationsBySize calculated from traversing their list.
8960 size_t freeSuballocationsToRegister = 0;
8961 // True if previous visited suballocation was free.
8962 bool prevFree = false;
8963
8964 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
8965 suballocItem != m_Suballocations.cend();
8966 ++suballocItem)
8967 {
8968 const VmaSuballocation& subAlloc = *suballocItem;
8969
8970 // Actual offset of this suballocation doesn't match expected one.
8971 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
8972
8973 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
8974 // Two adjacent free suballocations are invalid. They should be merged.
8975 VMA_VALIDATE(!prevFree || !currFree);
8976
8977 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
8978
8979 if(currFree)
8980 {
8981 calculatedSumFreeSize += subAlloc.size;
8982 ++calculatedFreeCount;
8983 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8984 {
8985 ++freeSuballocationsToRegister;
8986 }
8987
8988 // Margin required between allocations - every free space must be at least that large.
8989 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
8990 }
8991 else
8992 {
8993 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
8994 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
8995
8996 // Margin required between allocations - previous allocation must be free.
8997 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
8998 }
8999
9000 calculatedOffset += subAlloc.size;
9001 prevFree = currFree;
9002 }
9003
9004 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
9005 // match expected one.
9006 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
9007
9008 VkDeviceSize lastSize = 0;
9009 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
9010 {
9011 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
9012
9013 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
9014 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9015 // They must be sorted by size ascending.
9016 VMA_VALIDATE(suballocItem->size >= lastSize);
9017
9018 lastSize = suballocItem->size;
9019 }
9020
9021 // Check if totals match calculacted values.
9022 VMA_VALIDATE(ValidateFreeSuballocationList());
9023 VMA_VALIDATE(calculatedOffset == GetSize());
9024 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
9025 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
9026
9027 return true;
9028 }
9029
GetUnusedRangeSizeMax()9030 VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
9031 {
9032 if(!m_FreeSuballocationsBySize.empty())
9033 {
9034 return m_FreeSuballocationsBySize.back()->size;
9035 }
9036 else
9037 {
9038 return 0;
9039 }
9040 }
9041
IsEmpty()9042 bool VmaBlockMetadata_Generic::IsEmpty() const
9043 {
9044 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
9045 }
9046
CalcAllocationStatInfo(VmaStatInfo & outInfo)9047 void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
9048 {
9049 outInfo.blockCount = 1;
9050
9051 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9052 outInfo.allocationCount = rangeCount - m_FreeCount;
9053 outInfo.unusedRangeCount = m_FreeCount;
9054
9055 outInfo.unusedBytes = m_SumFreeSize;
9056 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
9057
9058 outInfo.allocationSizeMin = UINT64_MAX;
9059 outInfo.allocationSizeMax = 0;
9060 outInfo.unusedRangeSizeMin = UINT64_MAX;
9061 outInfo.unusedRangeSizeMax = 0;
9062
9063 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9064 suballocItem != m_Suballocations.cend();
9065 ++suballocItem)
9066 {
9067 const VmaSuballocation& suballoc = *suballocItem;
9068 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9069 {
9070 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9071 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
9072 }
9073 else
9074 {
9075 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
9076 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
9077 }
9078 }
9079 }
9080
AddPoolStats(VmaPoolStats & inoutStats)9081 void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
9082 {
9083 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
9084
9085 inoutStats.size += GetSize();
9086 inoutStats.unusedSize += m_SumFreeSize;
9087 inoutStats.allocationCount += rangeCount - m_FreeCount;
9088 inoutStats.unusedRangeCount += m_FreeCount;
9089 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
9090 }
9091
9092 #if VMA_STATS_STRING_ENABLED
9093
PrintDetailedMap(class VmaJsonWriter & json)9094 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
9095 {
9096 PrintDetailedMap_Begin(json,
9097 m_SumFreeSize, // unusedBytes
9098 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
9099 m_FreeCount); // unusedRangeCount
9100
9101 size_t i = 0;
9102 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
9103 suballocItem != m_Suballocations.cend();
9104 ++suballocItem, ++i)
9105 {
9106 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9107 {
9108 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
9109 }
9110 else
9111 {
9112 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
9113 }
9114 }
9115
9116 PrintDetailedMap_End(json);
9117 }
9118
9119 #endif // #if VMA_STATS_STRING_ENABLED
9120
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9121 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
9122 uint32_t currentFrameIndex,
9123 uint32_t frameInUseCount,
9124 VkDeviceSize bufferImageGranularity,
9125 VkDeviceSize allocSize,
9126 VkDeviceSize allocAlignment,
9127 bool upperAddress,
9128 VmaSuballocationType allocType,
9129 bool canMakeOtherLost,
9130 uint32_t strategy,
9131 VmaAllocationRequest* pAllocationRequest)
9132 {
9133 VMA_ASSERT(allocSize > 0);
9134 VMA_ASSERT(!upperAddress);
9135 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9136 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9137 VMA_HEAVY_ASSERT(Validate());
9138
9139 pAllocationRequest->type = VmaAllocationRequestType::Normal;
9140
9141 // There is not enough total free space in this block to fullfill the request: Early return.
9142 if(canMakeOtherLost == false &&
9143 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
9144 {
9145 return false;
9146 }
9147
9148 // New algorithm, efficiently searching freeSuballocationsBySize.
9149 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
9150 if(freeSuballocCount > 0)
9151 {
9152 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
9153 {
9154 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
9155 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9156 m_FreeSuballocationsBySize.data(),
9157 m_FreeSuballocationsBySize.data() + freeSuballocCount,
9158 allocSize + 2 * VMA_DEBUG_MARGIN,
9159 VmaSuballocationItemSizeLess());
9160 size_t index = it - m_FreeSuballocationsBySize.data();
9161 for(; index < freeSuballocCount; ++index)
9162 {
9163 if(CheckAllocation(
9164 currentFrameIndex,
9165 frameInUseCount,
9166 bufferImageGranularity,
9167 allocSize,
9168 allocAlignment,
9169 allocType,
9170 m_FreeSuballocationsBySize[index],
9171 false, // canMakeOtherLost
9172 &pAllocationRequest->offset,
9173 &pAllocationRequest->itemsToMakeLostCount,
9174 &pAllocationRequest->sumFreeSize,
9175 &pAllocationRequest->sumItemSize))
9176 {
9177 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9178 return true;
9179 }
9180 }
9181 }
9182 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
9183 {
9184 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9185 it != m_Suballocations.end();
9186 ++it)
9187 {
9188 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
9189 currentFrameIndex,
9190 frameInUseCount,
9191 bufferImageGranularity,
9192 allocSize,
9193 allocAlignment,
9194 allocType,
9195 it,
9196 false, // canMakeOtherLost
9197 &pAllocationRequest->offset,
9198 &pAllocationRequest->itemsToMakeLostCount,
9199 &pAllocationRequest->sumFreeSize,
9200 &pAllocationRequest->sumItemSize))
9201 {
9202 pAllocationRequest->item = it;
9203 return true;
9204 }
9205 }
9206 }
9207 else // WORST_FIT, FIRST_FIT
9208 {
9209 // Search staring from biggest suballocations.
9210 for(size_t index = freeSuballocCount; index--; )
9211 {
9212 if(CheckAllocation(
9213 currentFrameIndex,
9214 frameInUseCount,
9215 bufferImageGranularity,
9216 allocSize,
9217 allocAlignment,
9218 allocType,
9219 m_FreeSuballocationsBySize[index],
9220 false, // canMakeOtherLost
9221 &pAllocationRequest->offset,
9222 &pAllocationRequest->itemsToMakeLostCount,
9223 &pAllocationRequest->sumFreeSize,
9224 &pAllocationRequest->sumItemSize))
9225 {
9226 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
9227 return true;
9228 }
9229 }
9230 }
9231 }
9232
9233 if(canMakeOtherLost)
9234 {
9235 // Brute-force algorithm. TODO: Come up with something better.
9236
9237 bool found = false;
9238 VmaAllocationRequest tmpAllocRequest = {};
9239 tmpAllocRequest.type = VmaAllocationRequestType::Normal;
9240 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
9241 suballocIt != m_Suballocations.end();
9242 ++suballocIt)
9243 {
9244 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
9245 suballocIt->hAllocation->CanBecomeLost())
9246 {
9247 if(CheckAllocation(
9248 currentFrameIndex,
9249 frameInUseCount,
9250 bufferImageGranularity,
9251 allocSize,
9252 allocAlignment,
9253 allocType,
9254 suballocIt,
9255 canMakeOtherLost,
9256 &tmpAllocRequest.offset,
9257 &tmpAllocRequest.itemsToMakeLostCount,
9258 &tmpAllocRequest.sumFreeSize,
9259 &tmpAllocRequest.sumItemSize))
9260 {
9261 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
9262 {
9263 *pAllocationRequest = tmpAllocRequest;
9264 pAllocationRequest->item = suballocIt;
9265 break;
9266 }
9267 if(!found || tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
9268 {
9269 *pAllocationRequest = tmpAllocRequest;
9270 pAllocationRequest->item = suballocIt;
9271 found = true;
9272 }
9273 }
9274 }
9275 }
9276
9277 return found;
9278 }
9279
9280 return false;
9281 }
9282
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)9283 bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
9284 uint32_t currentFrameIndex,
9285 uint32_t frameInUseCount,
9286 VmaAllocationRequest* pAllocationRequest)
9287 {
9288 VMA_ASSERT(pAllocationRequest && pAllocationRequest->type == VmaAllocationRequestType::Normal);
9289
9290 while(pAllocationRequest->itemsToMakeLostCount > 0)
9291 {
9292 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
9293 {
9294 ++pAllocationRequest->item;
9295 }
9296 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9297 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
9298 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
9299 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9300 {
9301 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
9302 --pAllocationRequest->itemsToMakeLostCount;
9303 }
9304 else
9305 {
9306 return false;
9307 }
9308 }
9309
9310 VMA_HEAVY_ASSERT(Validate());
9311 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
9312 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
9313
9314 return true;
9315 }
9316
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)9317 uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
9318 {
9319 uint32_t lostAllocationCount = 0;
9320 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9321 it != m_Suballocations.end();
9322 ++it)
9323 {
9324 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
9325 it->hAllocation->CanBecomeLost() &&
9326 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
9327 {
9328 it = FreeSuballocation(it);
9329 ++lostAllocationCount;
9330 }
9331 }
9332 return lostAllocationCount;
9333 }
9334
CheckCorruption(const void * pBlockData)9335 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
9336 {
9337 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
9338 it != m_Suballocations.end();
9339 ++it)
9340 {
9341 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
9342 {
9343 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
9344 {
9345 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
9346 return VK_ERROR_VALIDATION_FAILED_EXT;
9347 }
9348 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
9349 {
9350 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
9351 return VK_ERROR_VALIDATION_FAILED_EXT;
9352 }
9353 }
9354 }
9355
9356 return VK_SUCCESS;
9357 }
9358
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)9359 void VmaBlockMetadata_Generic::Alloc(
9360 const VmaAllocationRequest& request,
9361 VmaSuballocationType type,
9362 VkDeviceSize allocSize,
9363 VmaAllocation hAllocation)
9364 {
9365 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9366 VMA_ASSERT(request.item != m_Suballocations.end());
9367 VmaSuballocation& suballoc = *request.item;
9368 // Given suballocation is a free block.
9369 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9370 // Given offset is inside this suballocation.
9371 VMA_ASSERT(request.offset >= suballoc.offset);
9372 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
9373 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
9374 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
9375
9376 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
9377 // it to become used.
9378 UnregisterFreeSuballocation(request.item);
9379
9380 suballoc.offset = request.offset;
9381 suballoc.size = allocSize;
9382 suballoc.type = type;
9383 suballoc.hAllocation = hAllocation;
9384
9385 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
9386 if(paddingEnd)
9387 {
9388 VmaSuballocation paddingSuballoc = {};
9389 paddingSuballoc.offset = request.offset + allocSize;
9390 paddingSuballoc.size = paddingEnd;
9391 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9392 VmaSuballocationList::iterator next = request.item;
9393 ++next;
9394 const VmaSuballocationList::iterator paddingEndItem =
9395 m_Suballocations.insert(next, paddingSuballoc);
9396 RegisterFreeSuballocation(paddingEndItem);
9397 }
9398
9399 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
9400 if(paddingBegin)
9401 {
9402 VmaSuballocation paddingSuballoc = {};
9403 paddingSuballoc.offset = request.offset - paddingBegin;
9404 paddingSuballoc.size = paddingBegin;
9405 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9406 const VmaSuballocationList::iterator paddingBeginItem =
9407 m_Suballocations.insert(request.item, paddingSuballoc);
9408 RegisterFreeSuballocation(paddingBeginItem);
9409 }
9410
9411 // Update totals.
9412 m_FreeCount = m_FreeCount - 1;
9413 if(paddingBegin > 0)
9414 {
9415 ++m_FreeCount;
9416 }
9417 if(paddingEnd > 0)
9418 {
9419 ++m_FreeCount;
9420 }
9421 m_SumFreeSize -= allocSize;
9422 }
9423
Free(const VmaAllocation allocation)9424 void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
9425 {
9426 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9427 suballocItem != m_Suballocations.end();
9428 ++suballocItem)
9429 {
9430 VmaSuballocation& suballoc = *suballocItem;
9431 if(suballoc.hAllocation == allocation)
9432 {
9433 FreeSuballocation(suballocItem);
9434 VMA_HEAVY_ASSERT(Validate());
9435 return;
9436 }
9437 }
9438 VMA_ASSERT(0 && "Not found!");
9439 }
9440
FreeAtOffset(VkDeviceSize offset)9441 void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
9442 {
9443 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
9444 suballocItem != m_Suballocations.end();
9445 ++suballocItem)
9446 {
9447 VmaSuballocation& suballoc = *suballocItem;
9448 if(suballoc.offset == offset)
9449 {
9450 FreeSuballocation(suballocItem);
9451 return;
9452 }
9453 }
9454 VMA_ASSERT(0 && "Not found!");
9455 }
9456
ValidateFreeSuballocationList()9457 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
9458 {
9459 VkDeviceSize lastSize = 0;
9460 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
9461 {
9462 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
9463
9464 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
9465 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
9466 VMA_VALIDATE(it->size >= lastSize);
9467 lastSize = it->size;
9468 }
9469 return true;
9470 }
9471
CheckAllocation(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaSuballocationList::const_iterator suballocItem,bool canMakeOtherLost,VkDeviceSize * pOffset,size_t * itemsToMakeLostCount,VkDeviceSize * pSumFreeSize,VkDeviceSize * pSumItemSize)9472 bool VmaBlockMetadata_Generic::CheckAllocation(
9473 uint32_t currentFrameIndex,
9474 uint32_t frameInUseCount,
9475 VkDeviceSize bufferImageGranularity,
9476 VkDeviceSize allocSize,
9477 VkDeviceSize allocAlignment,
9478 VmaSuballocationType allocType,
9479 VmaSuballocationList::const_iterator suballocItem,
9480 bool canMakeOtherLost,
9481 VkDeviceSize* pOffset,
9482 size_t* itemsToMakeLostCount,
9483 VkDeviceSize* pSumFreeSize,
9484 VkDeviceSize* pSumItemSize) const
9485 {
9486 VMA_ASSERT(allocSize > 0);
9487 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9488 VMA_ASSERT(suballocItem != m_Suballocations.cend());
9489 VMA_ASSERT(pOffset != VMA_NULL);
9490
9491 *itemsToMakeLostCount = 0;
9492 *pSumFreeSize = 0;
9493 *pSumItemSize = 0;
9494
9495 if(canMakeOtherLost)
9496 {
9497 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9498 {
9499 *pSumFreeSize = suballocItem->size;
9500 }
9501 else
9502 {
9503 if(suballocItem->hAllocation->CanBecomeLost() &&
9504 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9505 {
9506 ++*itemsToMakeLostCount;
9507 *pSumItemSize = suballocItem->size;
9508 }
9509 else
9510 {
9511 return false;
9512 }
9513 }
9514
9515 // Remaining size is too small for this request: Early return.
9516 if(GetSize() - suballocItem->offset < allocSize)
9517 {
9518 return false;
9519 }
9520
9521 // Start from offset equal to beginning of this suballocation.
9522 *pOffset = suballocItem->offset;
9523
9524 // Apply VMA_DEBUG_MARGIN at the beginning.
9525 if(VMA_DEBUG_MARGIN > 0)
9526 {
9527 *pOffset += VMA_DEBUG_MARGIN;
9528 }
9529
9530 // Apply alignment.
9531 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9532
9533 // Check previous suballocations for BufferImageGranularity conflicts.
9534 // Make bigger alignment if necessary.
9535 if(bufferImageGranularity > 1)
9536 {
9537 bool bufferImageGranularityConflict = false;
9538 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9539 while(prevSuballocItem != m_Suballocations.cbegin())
9540 {
9541 --prevSuballocItem;
9542 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9543 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9544 {
9545 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9546 {
9547 bufferImageGranularityConflict = true;
9548 break;
9549 }
9550 }
9551 else
9552 // Already on previous page.
9553 break;
9554 }
9555 if(bufferImageGranularityConflict)
9556 {
9557 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9558 }
9559 }
9560
9561 // Now that we have final *pOffset, check if we are past suballocItem.
9562 // If yes, return false - this function should be called for another suballocItem as starting point.
9563 if(*pOffset >= suballocItem->offset + suballocItem->size)
9564 {
9565 return false;
9566 }
9567
9568 // Calculate padding at the beginning based on current offset.
9569 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
9570
9571 // Calculate required margin at the end.
9572 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9573
9574 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
9575 // Another early return check.
9576 if(suballocItem->offset + totalSize > GetSize())
9577 {
9578 return false;
9579 }
9580
9581 // Advance lastSuballocItem until desired size is reached.
9582 // Update itemsToMakeLostCount.
9583 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
9584 if(totalSize > suballocItem->size)
9585 {
9586 VkDeviceSize remainingSize = totalSize - suballocItem->size;
9587 while(remainingSize > 0)
9588 {
9589 ++lastSuballocItem;
9590 if(lastSuballocItem == m_Suballocations.cend())
9591 {
9592 return false;
9593 }
9594 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9595 {
9596 *pSumFreeSize += lastSuballocItem->size;
9597 }
9598 else
9599 {
9600 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
9601 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
9602 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9603 {
9604 ++*itemsToMakeLostCount;
9605 *pSumItemSize += lastSuballocItem->size;
9606 }
9607 else
9608 {
9609 return false;
9610 }
9611 }
9612 remainingSize = (lastSuballocItem->size < remainingSize) ?
9613 remainingSize - lastSuballocItem->size : 0;
9614 }
9615 }
9616
9617 // Check next suballocations for BufferImageGranularity conflicts.
9618 // If conflict exists, we must mark more allocations lost or fail.
9619 if(bufferImageGranularity > 1)
9620 {
9621 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
9622 ++nextSuballocItem;
9623 while(nextSuballocItem != m_Suballocations.cend())
9624 {
9625 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9626 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9627 {
9628 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9629 {
9630 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
9631 if(nextSuballoc.hAllocation->CanBecomeLost() &&
9632 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9633 {
9634 ++*itemsToMakeLostCount;
9635 }
9636 else
9637 {
9638 return false;
9639 }
9640 }
9641 }
9642 else
9643 {
9644 // Already on next page.
9645 break;
9646 }
9647 ++nextSuballocItem;
9648 }
9649 }
9650 }
9651 else
9652 {
9653 const VmaSuballocation& suballoc = *suballocItem;
9654 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9655
9656 *pSumFreeSize = suballoc.size;
9657
9658 // Size of this suballocation is too small for this request: Early return.
9659 if(suballoc.size < allocSize)
9660 {
9661 return false;
9662 }
9663
9664 // Start from offset equal to beginning of this suballocation.
9665 *pOffset = suballoc.offset;
9666
9667 // Apply VMA_DEBUG_MARGIN at the beginning.
9668 if(VMA_DEBUG_MARGIN > 0)
9669 {
9670 *pOffset += VMA_DEBUG_MARGIN;
9671 }
9672
9673 // Apply alignment.
9674 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
9675
9676 // Check previous suballocations for BufferImageGranularity conflicts.
9677 // Make bigger alignment if necessary.
9678 if(bufferImageGranularity > 1)
9679 {
9680 bool bufferImageGranularityConflict = false;
9681 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
9682 while(prevSuballocItem != m_Suballocations.cbegin())
9683 {
9684 --prevSuballocItem;
9685 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
9686 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
9687 {
9688 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9689 {
9690 bufferImageGranularityConflict = true;
9691 break;
9692 }
9693 }
9694 else
9695 // Already on previous page.
9696 break;
9697 }
9698 if(bufferImageGranularityConflict)
9699 {
9700 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
9701 }
9702 }
9703
9704 // Calculate padding at the beginning based on current offset.
9705 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
9706
9707 // Calculate required margin at the end.
9708 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
9709
9710 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
9711 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
9712 {
9713 return false;
9714 }
9715
9716 // Check next suballocations for BufferImageGranularity conflicts.
9717 // If conflict exists, allocation cannot be made here.
9718 if(bufferImageGranularity > 1)
9719 {
9720 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
9721 ++nextSuballocItem;
9722 while(nextSuballocItem != m_Suballocations.cend())
9723 {
9724 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
9725 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9726 {
9727 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9728 {
9729 return false;
9730 }
9731 }
9732 else
9733 {
9734 // Already on next page.
9735 break;
9736 }
9737 ++nextSuballocItem;
9738 }
9739 }
9740 }
9741
9742 // All tests passed: Success. pOffset is already filled.
9743 return true;
9744 }
9745
MergeFreeWithNext(VmaSuballocationList::iterator item)9746 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
9747 {
9748 VMA_ASSERT(item != m_Suballocations.end());
9749 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9750
9751 VmaSuballocationList::iterator nextItem = item;
9752 ++nextItem;
9753 VMA_ASSERT(nextItem != m_Suballocations.end());
9754 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
9755
9756 item->size += nextItem->size;
9757 --m_FreeCount;
9758 m_Suballocations.erase(nextItem);
9759 }
9760
FreeSuballocation(VmaSuballocationList::iterator suballocItem)9761 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
9762 {
9763 // Change this suballocation to be marked as free.
9764 VmaSuballocation& suballoc = *suballocItem;
9765 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
9766 suballoc.hAllocation = VK_NULL_HANDLE;
9767
9768 // Update totals.
9769 ++m_FreeCount;
9770 m_SumFreeSize += suballoc.size;
9771
9772 // Merge with previous and/or next suballocation if it's also free.
9773 bool mergeWithNext = false;
9774 bool mergeWithPrev = false;
9775
9776 VmaSuballocationList::iterator nextItem = suballocItem;
9777 ++nextItem;
9778 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
9779 {
9780 mergeWithNext = true;
9781 }
9782
9783 VmaSuballocationList::iterator prevItem = suballocItem;
9784 if(suballocItem != m_Suballocations.begin())
9785 {
9786 --prevItem;
9787 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
9788 {
9789 mergeWithPrev = true;
9790 }
9791 }
9792
9793 if(mergeWithNext)
9794 {
9795 UnregisterFreeSuballocation(nextItem);
9796 MergeFreeWithNext(suballocItem);
9797 }
9798
9799 if(mergeWithPrev)
9800 {
9801 UnregisterFreeSuballocation(prevItem);
9802 MergeFreeWithNext(prevItem);
9803 RegisterFreeSuballocation(prevItem);
9804 return prevItem;
9805 }
9806 else
9807 {
9808 RegisterFreeSuballocation(suballocItem);
9809 return suballocItem;
9810 }
9811 }
9812
RegisterFreeSuballocation(VmaSuballocationList::iterator item)9813 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
9814 {
9815 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9816 VMA_ASSERT(item->size > 0);
9817
9818 // You may want to enable this validation at the beginning or at the end of
9819 // this function, depending on what do you want to check.
9820 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9821
9822 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9823 {
9824 if(m_FreeSuballocationsBySize.empty())
9825 {
9826 m_FreeSuballocationsBySize.push_back(item);
9827 }
9828 else
9829 {
9830 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
9831 }
9832 }
9833
9834 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9835 }
9836
9837
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)9838 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
9839 {
9840 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
9841 VMA_ASSERT(item->size > 0);
9842
9843 // You may want to enable this validation at the beginning or at the end of
9844 // this function, depending on what do you want to check.
9845 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9846
9847 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
9848 {
9849 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
9850 m_FreeSuballocationsBySize.data(),
9851 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
9852 item,
9853 VmaSuballocationItemSizeLess());
9854 for(size_t index = it - m_FreeSuballocationsBySize.data();
9855 index < m_FreeSuballocationsBySize.size();
9856 ++index)
9857 {
9858 if(m_FreeSuballocationsBySize[index] == item)
9859 {
9860 VmaVectorRemove(m_FreeSuballocationsBySize, index);
9861 return;
9862 }
9863 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
9864 }
9865 VMA_ASSERT(0 && "Not found.");
9866 }
9867
9868 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
9869 }
9870
IsBufferImageGranularityConflictPossible(VkDeviceSize bufferImageGranularity,VmaSuballocationType & inOutPrevSuballocType)9871 bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
9872 VkDeviceSize bufferImageGranularity,
9873 VmaSuballocationType& inOutPrevSuballocType) const
9874 {
9875 if(bufferImageGranularity == 1 || IsEmpty())
9876 {
9877 return false;
9878 }
9879
9880 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
9881 bool typeConflictFound = false;
9882 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
9883 it != m_Suballocations.cend();
9884 ++it)
9885 {
9886 const VmaSuballocationType suballocType = it->type;
9887 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
9888 {
9889 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
9890 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
9891 {
9892 typeConflictFound = true;
9893 }
9894 inOutPrevSuballocType = suballocType;
9895 }
9896 }
9897
9898 return typeConflictFound || minAlignment >= bufferImageGranularity;
9899 }
9900
9901 ////////////////////////////////////////////////////////////////////////////////
9902 // class VmaBlockMetadata_Linear
9903
VmaBlockMetadata_Linear(VmaAllocator hAllocator)9904 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
9905 VmaBlockMetadata(hAllocator),
9906 m_SumFreeSize(0),
9907 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9908 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
9909 m_1stVectorIndex(0),
9910 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
9911 m_1stNullItemsBeginCount(0),
9912 m_1stNullItemsMiddleCount(0),
9913 m_2ndNullItemsCount(0)
9914 {
9915 }
9916
~VmaBlockMetadata_Linear()9917 VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
9918 {
9919 }
9920
Init(VkDeviceSize size)9921 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
9922 {
9923 VmaBlockMetadata::Init(size);
9924 m_SumFreeSize = size;
9925 }
9926
Validate()9927 bool VmaBlockMetadata_Linear::Validate() const
9928 {
9929 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9930 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9931
9932 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
9933 VMA_VALIDATE(!suballocations1st.empty() ||
9934 suballocations2nd.empty() ||
9935 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
9936
9937 if(!suballocations1st.empty())
9938 {
9939 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
9940 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
9941 // Null item at the end should be just pop_back().
9942 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
9943 }
9944 if(!suballocations2nd.empty())
9945 {
9946 // Null item at the end should be just pop_back().
9947 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
9948 }
9949
9950 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
9951 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
9952
9953 VkDeviceSize sumUsedSize = 0;
9954 const size_t suballoc1stCount = suballocations1st.size();
9955 VkDeviceSize offset = VMA_DEBUG_MARGIN;
9956
9957 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9958 {
9959 const size_t suballoc2ndCount = suballocations2nd.size();
9960 size_t nullItem2ndCount = 0;
9961 for(size_t i = 0; i < suballoc2ndCount; ++i)
9962 {
9963 const VmaSuballocation& suballoc = suballocations2nd[i];
9964 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9965
9966 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
9967 VMA_VALIDATE(suballoc.offset >= offset);
9968
9969 if(!currFree)
9970 {
9971 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
9972 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
9973 sumUsedSize += suballoc.size;
9974 }
9975 else
9976 {
9977 ++nullItem2ndCount;
9978 }
9979
9980 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
9981 }
9982
9983 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
9984 }
9985
9986 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
9987 {
9988 const VmaSuballocation& suballoc = suballocations1st[i];
9989 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
9990 suballoc.hAllocation == VK_NULL_HANDLE);
9991 }
9992
9993 size_t nullItem1stCount = m_1stNullItemsBeginCount;
9994
9995 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
9996 {
9997 const VmaSuballocation& suballoc = suballocations1st[i];
9998 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
9999
10000 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10001 VMA_VALIDATE(suballoc.offset >= offset);
10002 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
10003
10004 if(!currFree)
10005 {
10006 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10007 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10008 sumUsedSize += suballoc.size;
10009 }
10010 else
10011 {
10012 ++nullItem1stCount;
10013 }
10014
10015 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10016 }
10017 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
10018
10019 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10020 {
10021 const size_t suballoc2ndCount = suballocations2nd.size();
10022 size_t nullItem2ndCount = 0;
10023 for(size_t i = suballoc2ndCount; i--; )
10024 {
10025 const VmaSuballocation& suballoc = suballocations2nd[i];
10026 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
10027
10028 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
10029 VMA_VALIDATE(suballoc.offset >= offset);
10030
10031 if(!currFree)
10032 {
10033 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
10034 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
10035 sumUsedSize += suballoc.size;
10036 }
10037 else
10038 {
10039 ++nullItem2ndCount;
10040 }
10041
10042 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
10043 }
10044
10045 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
10046 }
10047
10048 VMA_VALIDATE(offset <= GetSize());
10049 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
10050
10051 return true;
10052 }
10053
GetAllocationCount()10054 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
10055 {
10056 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
10057 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
10058 }
10059
GetUnusedRangeSizeMax()10060 VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
10061 {
10062 const VkDeviceSize size = GetSize();
10063
10064 /*
10065 We don't consider gaps inside allocation vectors with freed allocations because
10066 they are not suitable for reuse in linear allocator. We consider only space that
10067 is available for new allocations.
10068 */
10069 if(IsEmpty())
10070 {
10071 return size;
10072 }
10073
10074 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10075
10076 switch(m_2ndVectorMode)
10077 {
10078 case SECOND_VECTOR_EMPTY:
10079 /*
10080 Available space is after end of 1st, as well as before beginning of 1st (which
10081 whould make it a ring buffer).
10082 */
10083 {
10084 const size_t suballocations1stCount = suballocations1st.size();
10085 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
10086 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10087 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
10088 return VMA_MAX(
10089 firstSuballoc.offset,
10090 size - (lastSuballoc.offset + lastSuballoc.size));
10091 }
10092 break;
10093
10094 case SECOND_VECTOR_RING_BUFFER:
10095 /*
10096 Available space is only between end of 2nd and beginning of 1st.
10097 */
10098 {
10099 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10100 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
10101 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
10102 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
10103 }
10104 break;
10105
10106 case SECOND_VECTOR_DOUBLE_STACK:
10107 /*
10108 Available space is only between end of 1st and top of 2nd.
10109 */
10110 {
10111 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10112 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
10113 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
10114 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
10115 }
10116 break;
10117
10118 default:
10119 VMA_ASSERT(0);
10120 return 0;
10121 }
10122 }
10123
CalcAllocationStatInfo(VmaStatInfo & outInfo)10124 void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10125 {
10126 const VkDeviceSize size = GetSize();
10127 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10128 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10129 const size_t suballoc1stCount = suballocations1st.size();
10130 const size_t suballoc2ndCount = suballocations2nd.size();
10131
10132 outInfo.blockCount = 1;
10133 outInfo.allocationCount = (uint32_t)GetAllocationCount();
10134 outInfo.unusedRangeCount = 0;
10135 outInfo.usedBytes = 0;
10136 outInfo.allocationSizeMin = UINT64_MAX;
10137 outInfo.allocationSizeMax = 0;
10138 outInfo.unusedRangeSizeMin = UINT64_MAX;
10139 outInfo.unusedRangeSizeMax = 0;
10140
10141 VkDeviceSize lastOffset = 0;
10142
10143 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10144 {
10145 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10146 size_t nextAlloc2ndIndex = 0;
10147 while(lastOffset < freeSpace2ndTo1stEnd)
10148 {
10149 // Find next non-null allocation or move nextAllocIndex to the end.
10150 while(nextAlloc2ndIndex < suballoc2ndCount &&
10151 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10152 {
10153 ++nextAlloc2ndIndex;
10154 }
10155
10156 // Found non-null allocation.
10157 if(nextAlloc2ndIndex < suballoc2ndCount)
10158 {
10159 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10160
10161 // 1. Process free space before this allocation.
10162 if(lastOffset < suballoc.offset)
10163 {
10164 // There is free space from lastOffset to suballoc.offset.
10165 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10166 ++outInfo.unusedRangeCount;
10167 outInfo.unusedBytes += unusedRangeSize;
10168 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10169 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10170 }
10171
10172 // 2. Process this allocation.
10173 // There is allocation with suballoc.offset, suballoc.size.
10174 outInfo.usedBytes += suballoc.size;
10175 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10176 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10177
10178 // 3. Prepare for next iteration.
10179 lastOffset = suballoc.offset + suballoc.size;
10180 ++nextAlloc2ndIndex;
10181 }
10182 // We are at the end.
10183 else
10184 {
10185 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10186 if(lastOffset < freeSpace2ndTo1stEnd)
10187 {
10188 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10189 ++outInfo.unusedRangeCount;
10190 outInfo.unusedBytes += unusedRangeSize;
10191 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10192 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10193 }
10194
10195 // End of loop.
10196 lastOffset = freeSpace2ndTo1stEnd;
10197 }
10198 }
10199 }
10200
10201 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10202 const VkDeviceSize freeSpace1stTo2ndEnd =
10203 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10204 while(lastOffset < freeSpace1stTo2ndEnd)
10205 {
10206 // Find next non-null allocation or move nextAllocIndex to the end.
10207 while(nextAlloc1stIndex < suballoc1stCount &&
10208 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10209 {
10210 ++nextAlloc1stIndex;
10211 }
10212
10213 // Found non-null allocation.
10214 if(nextAlloc1stIndex < suballoc1stCount)
10215 {
10216 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10217
10218 // 1. Process free space before this allocation.
10219 if(lastOffset < suballoc.offset)
10220 {
10221 // There is free space from lastOffset to suballoc.offset.
10222 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10223 ++outInfo.unusedRangeCount;
10224 outInfo.unusedBytes += unusedRangeSize;
10225 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10226 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10227 }
10228
10229 // 2. Process this allocation.
10230 // There is allocation with suballoc.offset, suballoc.size.
10231 outInfo.usedBytes += suballoc.size;
10232 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10233 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10234
10235 // 3. Prepare for next iteration.
10236 lastOffset = suballoc.offset + suballoc.size;
10237 ++nextAlloc1stIndex;
10238 }
10239 // We are at the end.
10240 else
10241 {
10242 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10243 if(lastOffset < freeSpace1stTo2ndEnd)
10244 {
10245 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10246 ++outInfo.unusedRangeCount;
10247 outInfo.unusedBytes += unusedRangeSize;
10248 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10249 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10250 }
10251
10252 // End of loop.
10253 lastOffset = freeSpace1stTo2ndEnd;
10254 }
10255 }
10256
10257 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10258 {
10259 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10260 while(lastOffset < size)
10261 {
10262 // Find next non-null allocation or move nextAllocIndex to the end.
10263 while(nextAlloc2ndIndex != SIZE_MAX &&
10264 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10265 {
10266 --nextAlloc2ndIndex;
10267 }
10268
10269 // Found non-null allocation.
10270 if(nextAlloc2ndIndex != SIZE_MAX)
10271 {
10272 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10273
10274 // 1. Process free space before this allocation.
10275 if(lastOffset < suballoc.offset)
10276 {
10277 // There is free space from lastOffset to suballoc.offset.
10278 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10279 ++outInfo.unusedRangeCount;
10280 outInfo.unusedBytes += unusedRangeSize;
10281 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10282 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10283 }
10284
10285 // 2. Process this allocation.
10286 // There is allocation with suballoc.offset, suballoc.size.
10287 outInfo.usedBytes += suballoc.size;
10288 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
10289 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
10290
10291 // 3. Prepare for next iteration.
10292 lastOffset = suballoc.offset + suballoc.size;
10293 --nextAlloc2ndIndex;
10294 }
10295 // We are at the end.
10296 else
10297 {
10298 // There is free space from lastOffset to size.
10299 if(lastOffset < size)
10300 {
10301 const VkDeviceSize unusedRangeSize = size - lastOffset;
10302 ++outInfo.unusedRangeCount;
10303 outInfo.unusedBytes += unusedRangeSize;
10304 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
10305 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
10306 }
10307
10308 // End of loop.
10309 lastOffset = size;
10310 }
10311 }
10312 }
10313
10314 outInfo.unusedBytes = size - outInfo.usedBytes;
10315 }
10316
AddPoolStats(VmaPoolStats & inoutStats)10317 void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
10318 {
10319 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10320 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10321 const VkDeviceSize size = GetSize();
10322 const size_t suballoc1stCount = suballocations1st.size();
10323 const size_t suballoc2ndCount = suballocations2nd.size();
10324
10325 inoutStats.size += size;
10326
10327 VkDeviceSize lastOffset = 0;
10328
10329 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10330 {
10331 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10332 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
10333 while(lastOffset < freeSpace2ndTo1stEnd)
10334 {
10335 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10336 while(nextAlloc2ndIndex < suballoc2ndCount &&
10337 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10338 {
10339 ++nextAlloc2ndIndex;
10340 }
10341
10342 // Found non-null allocation.
10343 if(nextAlloc2ndIndex < suballoc2ndCount)
10344 {
10345 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10346
10347 // 1. Process free space before this allocation.
10348 if(lastOffset < suballoc.offset)
10349 {
10350 // There is free space from lastOffset to suballoc.offset.
10351 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10352 inoutStats.unusedSize += unusedRangeSize;
10353 ++inoutStats.unusedRangeCount;
10354 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10355 }
10356
10357 // 2. Process this allocation.
10358 // There is allocation with suballoc.offset, suballoc.size.
10359 ++inoutStats.allocationCount;
10360
10361 // 3. Prepare for next iteration.
10362 lastOffset = suballoc.offset + suballoc.size;
10363 ++nextAlloc2ndIndex;
10364 }
10365 // We are at the end.
10366 else
10367 {
10368 if(lastOffset < freeSpace2ndTo1stEnd)
10369 {
10370 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10371 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10372 inoutStats.unusedSize += unusedRangeSize;
10373 ++inoutStats.unusedRangeCount;
10374 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10375 }
10376
10377 // End of loop.
10378 lastOffset = freeSpace2ndTo1stEnd;
10379 }
10380 }
10381 }
10382
10383 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10384 const VkDeviceSize freeSpace1stTo2ndEnd =
10385 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10386 while(lastOffset < freeSpace1stTo2ndEnd)
10387 {
10388 // Find next non-null allocation or move nextAllocIndex to the end.
10389 while(nextAlloc1stIndex < suballoc1stCount &&
10390 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10391 {
10392 ++nextAlloc1stIndex;
10393 }
10394
10395 // Found non-null allocation.
10396 if(nextAlloc1stIndex < suballoc1stCount)
10397 {
10398 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10399
10400 // 1. Process free space before this allocation.
10401 if(lastOffset < suballoc.offset)
10402 {
10403 // There is free space from lastOffset to suballoc.offset.
10404 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10405 inoutStats.unusedSize += unusedRangeSize;
10406 ++inoutStats.unusedRangeCount;
10407 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10408 }
10409
10410 // 2. Process this allocation.
10411 // There is allocation with suballoc.offset, suballoc.size.
10412 ++inoutStats.allocationCount;
10413
10414 // 3. Prepare for next iteration.
10415 lastOffset = suballoc.offset + suballoc.size;
10416 ++nextAlloc1stIndex;
10417 }
10418 // We are at the end.
10419 else
10420 {
10421 if(lastOffset < freeSpace1stTo2ndEnd)
10422 {
10423 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10424 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10425 inoutStats.unusedSize += unusedRangeSize;
10426 ++inoutStats.unusedRangeCount;
10427 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10428 }
10429
10430 // End of loop.
10431 lastOffset = freeSpace1stTo2ndEnd;
10432 }
10433 }
10434
10435 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10436 {
10437 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10438 while(lastOffset < size)
10439 {
10440 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10441 while(nextAlloc2ndIndex != SIZE_MAX &&
10442 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10443 {
10444 --nextAlloc2ndIndex;
10445 }
10446
10447 // Found non-null allocation.
10448 if(nextAlloc2ndIndex != SIZE_MAX)
10449 {
10450 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10451
10452 // 1. Process free space before this allocation.
10453 if(lastOffset < suballoc.offset)
10454 {
10455 // There is free space from lastOffset to suballoc.offset.
10456 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10457 inoutStats.unusedSize += unusedRangeSize;
10458 ++inoutStats.unusedRangeCount;
10459 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10460 }
10461
10462 // 2. Process this allocation.
10463 // There is allocation with suballoc.offset, suballoc.size.
10464 ++inoutStats.allocationCount;
10465
10466 // 3. Prepare for next iteration.
10467 lastOffset = suballoc.offset + suballoc.size;
10468 --nextAlloc2ndIndex;
10469 }
10470 // We are at the end.
10471 else
10472 {
10473 if(lastOffset < size)
10474 {
10475 // There is free space from lastOffset to size.
10476 const VkDeviceSize unusedRangeSize = size - lastOffset;
10477 inoutStats.unusedSize += unusedRangeSize;
10478 ++inoutStats.unusedRangeCount;
10479 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
10480 }
10481
10482 // End of loop.
10483 lastOffset = size;
10484 }
10485 }
10486 }
10487 }
10488
10489 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10490 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
10491 {
10492 const VkDeviceSize size = GetSize();
10493 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10494 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10495 const size_t suballoc1stCount = suballocations1st.size();
10496 const size_t suballoc2ndCount = suballocations2nd.size();
10497
10498 // FIRST PASS
10499
10500 size_t unusedRangeCount = 0;
10501 VkDeviceSize usedBytes = 0;
10502
10503 VkDeviceSize lastOffset = 0;
10504
10505 size_t alloc2ndCount = 0;
10506 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10507 {
10508 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10509 size_t nextAlloc2ndIndex = 0;
10510 while(lastOffset < freeSpace2ndTo1stEnd)
10511 {
10512 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10513 while(nextAlloc2ndIndex < suballoc2ndCount &&
10514 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10515 {
10516 ++nextAlloc2ndIndex;
10517 }
10518
10519 // Found non-null allocation.
10520 if(nextAlloc2ndIndex < suballoc2ndCount)
10521 {
10522 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10523
10524 // 1. Process free space before this allocation.
10525 if(lastOffset < suballoc.offset)
10526 {
10527 // There is free space from lastOffset to suballoc.offset.
10528 ++unusedRangeCount;
10529 }
10530
10531 // 2. Process this allocation.
10532 // There is allocation with suballoc.offset, suballoc.size.
10533 ++alloc2ndCount;
10534 usedBytes += suballoc.size;
10535
10536 // 3. Prepare for next iteration.
10537 lastOffset = suballoc.offset + suballoc.size;
10538 ++nextAlloc2ndIndex;
10539 }
10540 // We are at the end.
10541 else
10542 {
10543 if(lastOffset < freeSpace2ndTo1stEnd)
10544 {
10545 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10546 ++unusedRangeCount;
10547 }
10548
10549 // End of loop.
10550 lastOffset = freeSpace2ndTo1stEnd;
10551 }
10552 }
10553 }
10554
10555 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
10556 size_t alloc1stCount = 0;
10557 const VkDeviceSize freeSpace1stTo2ndEnd =
10558 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
10559 while(lastOffset < freeSpace1stTo2ndEnd)
10560 {
10561 // Find next non-null allocation or move nextAllocIndex to the end.
10562 while(nextAlloc1stIndex < suballoc1stCount &&
10563 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10564 {
10565 ++nextAlloc1stIndex;
10566 }
10567
10568 // Found non-null allocation.
10569 if(nextAlloc1stIndex < suballoc1stCount)
10570 {
10571 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10572
10573 // 1. Process free space before this allocation.
10574 if(lastOffset < suballoc.offset)
10575 {
10576 // There is free space from lastOffset to suballoc.offset.
10577 ++unusedRangeCount;
10578 }
10579
10580 // 2. Process this allocation.
10581 // There is allocation with suballoc.offset, suballoc.size.
10582 ++alloc1stCount;
10583 usedBytes += suballoc.size;
10584
10585 // 3. Prepare for next iteration.
10586 lastOffset = suballoc.offset + suballoc.size;
10587 ++nextAlloc1stIndex;
10588 }
10589 // We are at the end.
10590 else
10591 {
10592 if(lastOffset < size)
10593 {
10594 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10595 ++unusedRangeCount;
10596 }
10597
10598 // End of loop.
10599 lastOffset = freeSpace1stTo2ndEnd;
10600 }
10601 }
10602
10603 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10604 {
10605 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10606 while(lastOffset < size)
10607 {
10608 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10609 while(nextAlloc2ndIndex != SIZE_MAX &&
10610 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10611 {
10612 --nextAlloc2ndIndex;
10613 }
10614
10615 // Found non-null allocation.
10616 if(nextAlloc2ndIndex != SIZE_MAX)
10617 {
10618 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10619
10620 // 1. Process free space before this allocation.
10621 if(lastOffset < suballoc.offset)
10622 {
10623 // There is free space from lastOffset to suballoc.offset.
10624 ++unusedRangeCount;
10625 }
10626
10627 // 2. Process this allocation.
10628 // There is allocation with suballoc.offset, suballoc.size.
10629 ++alloc2ndCount;
10630 usedBytes += suballoc.size;
10631
10632 // 3. Prepare for next iteration.
10633 lastOffset = suballoc.offset + suballoc.size;
10634 --nextAlloc2ndIndex;
10635 }
10636 // We are at the end.
10637 else
10638 {
10639 if(lastOffset < size)
10640 {
10641 // There is free space from lastOffset to size.
10642 ++unusedRangeCount;
10643 }
10644
10645 // End of loop.
10646 lastOffset = size;
10647 }
10648 }
10649 }
10650
10651 const VkDeviceSize unusedBytes = size - usedBytes;
10652 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
10653
10654 // SECOND PASS
10655 lastOffset = 0;
10656
10657 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10658 {
10659 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
10660 size_t nextAlloc2ndIndex = 0;
10661 while(lastOffset < freeSpace2ndTo1stEnd)
10662 {
10663 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10664 while(nextAlloc2ndIndex < suballoc2ndCount &&
10665 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10666 {
10667 ++nextAlloc2ndIndex;
10668 }
10669
10670 // Found non-null allocation.
10671 if(nextAlloc2ndIndex < suballoc2ndCount)
10672 {
10673 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10674
10675 // 1. Process free space before this allocation.
10676 if(lastOffset < suballoc.offset)
10677 {
10678 // There is free space from lastOffset to suballoc.offset.
10679 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10680 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10681 }
10682
10683 // 2. Process this allocation.
10684 // There is allocation with suballoc.offset, suballoc.size.
10685 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10686
10687 // 3. Prepare for next iteration.
10688 lastOffset = suballoc.offset + suballoc.size;
10689 ++nextAlloc2ndIndex;
10690 }
10691 // We are at the end.
10692 else
10693 {
10694 if(lastOffset < freeSpace2ndTo1stEnd)
10695 {
10696 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
10697 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
10698 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10699 }
10700
10701 // End of loop.
10702 lastOffset = freeSpace2ndTo1stEnd;
10703 }
10704 }
10705 }
10706
10707 nextAlloc1stIndex = m_1stNullItemsBeginCount;
10708 while(lastOffset < freeSpace1stTo2ndEnd)
10709 {
10710 // Find next non-null allocation or move nextAllocIndex to the end.
10711 while(nextAlloc1stIndex < suballoc1stCount &&
10712 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
10713 {
10714 ++nextAlloc1stIndex;
10715 }
10716
10717 // Found non-null allocation.
10718 if(nextAlloc1stIndex < suballoc1stCount)
10719 {
10720 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
10721
10722 // 1. Process free space before this allocation.
10723 if(lastOffset < suballoc.offset)
10724 {
10725 // There is free space from lastOffset to suballoc.offset.
10726 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10727 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10728 }
10729
10730 // 2. Process this allocation.
10731 // There is allocation with suballoc.offset, suballoc.size.
10732 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10733
10734 // 3. Prepare for next iteration.
10735 lastOffset = suballoc.offset + suballoc.size;
10736 ++nextAlloc1stIndex;
10737 }
10738 // We are at the end.
10739 else
10740 {
10741 if(lastOffset < freeSpace1stTo2ndEnd)
10742 {
10743 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
10744 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
10745 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10746 }
10747
10748 // End of loop.
10749 lastOffset = freeSpace1stTo2ndEnd;
10750 }
10751 }
10752
10753 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10754 {
10755 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
10756 while(lastOffset < size)
10757 {
10758 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
10759 while(nextAlloc2ndIndex != SIZE_MAX &&
10760 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
10761 {
10762 --nextAlloc2ndIndex;
10763 }
10764
10765 // Found non-null allocation.
10766 if(nextAlloc2ndIndex != SIZE_MAX)
10767 {
10768 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
10769
10770 // 1. Process free space before this allocation.
10771 if(lastOffset < suballoc.offset)
10772 {
10773 // There is free space from lastOffset to suballoc.offset.
10774 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
10775 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10776 }
10777
10778 // 2. Process this allocation.
10779 // There is allocation with suballoc.offset, suballoc.size.
10780 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
10781
10782 // 3. Prepare for next iteration.
10783 lastOffset = suballoc.offset + suballoc.size;
10784 --nextAlloc2ndIndex;
10785 }
10786 // We are at the end.
10787 else
10788 {
10789 if(lastOffset < size)
10790 {
10791 // There is free space from lastOffset to size.
10792 const VkDeviceSize unusedRangeSize = size - lastOffset;
10793 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
10794 }
10795
10796 // End of loop.
10797 lastOffset = size;
10798 }
10799 }
10800 }
10801
10802 PrintDetailedMap_End(json);
10803 }
10804 #endif // #if VMA_STATS_STRING_ENABLED
10805
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10806 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
10807 uint32_t currentFrameIndex,
10808 uint32_t frameInUseCount,
10809 VkDeviceSize bufferImageGranularity,
10810 VkDeviceSize allocSize,
10811 VkDeviceSize allocAlignment,
10812 bool upperAddress,
10813 VmaSuballocationType allocType,
10814 bool canMakeOtherLost,
10815 uint32_t strategy,
10816 VmaAllocationRequest* pAllocationRequest)
10817 {
10818 VMA_ASSERT(allocSize > 0);
10819 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
10820 VMA_ASSERT(pAllocationRequest != VMA_NULL);
10821 VMA_HEAVY_ASSERT(Validate());
10822 return upperAddress ?
10823 CreateAllocationRequest_UpperAddress(
10824 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10825 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest) :
10826 CreateAllocationRequest_LowerAddress(
10827 currentFrameIndex, frameInUseCount, bufferImageGranularity,
10828 allocSize, allocAlignment, allocType, canMakeOtherLost, strategy, pAllocationRequest);
10829 }
10830
CreateAllocationRequest_UpperAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10831 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
10832 uint32_t currentFrameIndex,
10833 uint32_t frameInUseCount,
10834 VkDeviceSize bufferImageGranularity,
10835 VkDeviceSize allocSize,
10836 VkDeviceSize allocAlignment,
10837 VmaSuballocationType allocType,
10838 bool canMakeOtherLost,
10839 uint32_t strategy,
10840 VmaAllocationRequest* pAllocationRequest)
10841 {
10842 const VkDeviceSize size = GetSize();
10843 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10844 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10845
10846 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10847 {
10848 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
10849 return false;
10850 }
10851
10852 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
10853 if(allocSize > size)
10854 {
10855 return false;
10856 }
10857 VkDeviceSize resultBaseOffset = size - allocSize;
10858 if(!suballocations2nd.empty())
10859 {
10860 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
10861 resultBaseOffset = lastSuballoc.offset - allocSize;
10862 if(allocSize > lastSuballoc.offset)
10863 {
10864 return false;
10865 }
10866 }
10867
10868 // Start from offset equal to end of free space.
10869 VkDeviceSize resultOffset = resultBaseOffset;
10870
10871 // Apply VMA_DEBUG_MARGIN at the end.
10872 if(VMA_DEBUG_MARGIN > 0)
10873 {
10874 if(resultOffset < VMA_DEBUG_MARGIN)
10875 {
10876 return false;
10877 }
10878 resultOffset -= VMA_DEBUG_MARGIN;
10879 }
10880
10881 // Apply alignment.
10882 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
10883
10884 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
10885 // Make bigger alignment if necessary.
10886 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
10887 {
10888 bool bufferImageGranularityConflict = false;
10889 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
10890 {
10891 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
10892 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
10893 {
10894 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
10895 {
10896 bufferImageGranularityConflict = true;
10897 break;
10898 }
10899 }
10900 else
10901 // Already on previous page.
10902 break;
10903 }
10904 if(bufferImageGranularityConflict)
10905 {
10906 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
10907 }
10908 }
10909
10910 // There is enough free space.
10911 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
10912 suballocations1st.back().offset + suballocations1st.back().size :
10913 0;
10914 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
10915 {
10916 // Check previous suballocations for BufferImageGranularity conflicts.
10917 // If conflict exists, allocation cannot be made here.
10918 if(bufferImageGranularity > 1)
10919 {
10920 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10921 {
10922 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10923 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10924 {
10925 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
10926 {
10927 return false;
10928 }
10929 }
10930 else
10931 {
10932 // Already on next page.
10933 break;
10934 }
10935 }
10936 }
10937
10938 // All tests passed: Success.
10939 pAllocationRequest->offset = resultOffset;
10940 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
10941 pAllocationRequest->sumItemSize = 0;
10942 // pAllocationRequest->item unused.
10943 pAllocationRequest->itemsToMakeLostCount = 0;
10944 pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
10945 return true;
10946 }
10947
10948 return false;
10949 }
10950
CreateAllocationRequest_LowerAddress(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10951 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
10952 uint32_t currentFrameIndex,
10953 uint32_t frameInUseCount,
10954 VkDeviceSize bufferImageGranularity,
10955 VkDeviceSize allocSize,
10956 VkDeviceSize allocAlignment,
10957 VmaSuballocationType allocType,
10958 bool canMakeOtherLost,
10959 uint32_t strategy,
10960 VmaAllocationRequest* pAllocationRequest)
10961 {
10962 const VkDeviceSize size = GetSize();
10963 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10964 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10965
10966 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10967 {
10968 // Try to allocate at the end of 1st vector.
10969
10970 VkDeviceSize resultBaseOffset = 0;
10971 if(!suballocations1st.empty())
10972 {
10973 const VmaSuballocation& lastSuballoc = suballocations1st.back();
10974 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
10975 }
10976
10977 // Start from offset equal to beginning of free space.
10978 VkDeviceSize resultOffset = resultBaseOffset;
10979
10980 // Apply VMA_DEBUG_MARGIN at the beginning.
10981 if(VMA_DEBUG_MARGIN > 0)
10982 {
10983 resultOffset += VMA_DEBUG_MARGIN;
10984 }
10985
10986 // Apply alignment.
10987 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
10988
10989 // Check previous suballocations for BufferImageGranularity conflicts.
10990 // Make bigger alignment if necessary.
10991 if(bufferImageGranularity > 1 && !suballocations1st.empty())
10992 {
10993 bool bufferImageGranularityConflict = false;
10994 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
10995 {
10996 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
10997 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
10998 {
10999 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11000 {
11001 bufferImageGranularityConflict = true;
11002 break;
11003 }
11004 }
11005 else
11006 // Already on previous page.
11007 break;
11008 }
11009 if(bufferImageGranularityConflict)
11010 {
11011 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11012 }
11013 }
11014
11015 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
11016 suballocations2nd.back().offset : size;
11017
11018 // There is enough free space at the end after alignment.
11019 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
11020 {
11021 // Check next suballocations for BufferImageGranularity conflicts.
11022 // If conflict exists, allocation cannot be made here.
11023 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11024 {
11025 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
11026 {
11027 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
11028 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11029 {
11030 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11031 {
11032 return false;
11033 }
11034 }
11035 else
11036 {
11037 // Already on previous page.
11038 break;
11039 }
11040 }
11041 }
11042
11043 // All tests passed: Success.
11044 pAllocationRequest->offset = resultOffset;
11045 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
11046 pAllocationRequest->sumItemSize = 0;
11047 // pAllocationRequest->item, customData unused.
11048 pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
11049 pAllocationRequest->itemsToMakeLostCount = 0;
11050 return true;
11051 }
11052 }
11053
11054 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
11055 // beginning of 1st vector as the end of free space.
11056 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11057 {
11058 VMA_ASSERT(!suballocations1st.empty());
11059
11060 VkDeviceSize resultBaseOffset = 0;
11061 if(!suballocations2nd.empty())
11062 {
11063 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
11064 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
11065 }
11066
11067 // Start from offset equal to beginning of free space.
11068 VkDeviceSize resultOffset = resultBaseOffset;
11069
11070 // Apply VMA_DEBUG_MARGIN at the beginning.
11071 if(VMA_DEBUG_MARGIN > 0)
11072 {
11073 resultOffset += VMA_DEBUG_MARGIN;
11074 }
11075
11076 // Apply alignment.
11077 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
11078
11079 // Check previous suballocations for BufferImageGranularity conflicts.
11080 // Make bigger alignment if necessary.
11081 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
11082 {
11083 bool bufferImageGranularityConflict = false;
11084 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
11085 {
11086 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
11087 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
11088 {
11089 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
11090 {
11091 bufferImageGranularityConflict = true;
11092 break;
11093 }
11094 }
11095 else
11096 // Already on previous page.
11097 break;
11098 }
11099 if(bufferImageGranularityConflict)
11100 {
11101 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
11102 }
11103 }
11104
11105 pAllocationRequest->itemsToMakeLostCount = 0;
11106 pAllocationRequest->sumItemSize = 0;
11107 size_t index1st = m_1stNullItemsBeginCount;
11108
11109 if(canMakeOtherLost)
11110 {
11111 while(index1st < suballocations1st.size() &&
11112 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
11113 {
11114 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
11115 const VmaSuballocation& suballoc = suballocations1st[index1st];
11116 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
11117 {
11118 // No problem.
11119 }
11120 else
11121 {
11122 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11123 if(suballoc.hAllocation->CanBecomeLost() &&
11124 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11125 {
11126 ++pAllocationRequest->itemsToMakeLostCount;
11127 pAllocationRequest->sumItemSize += suballoc.size;
11128 }
11129 else
11130 {
11131 return false;
11132 }
11133 }
11134 ++index1st;
11135 }
11136
11137 // Check next suballocations for BufferImageGranularity conflicts.
11138 // If conflict exists, we must mark more allocations lost or fail.
11139 if(bufferImageGranularity > 1)
11140 {
11141 while(index1st < suballocations1st.size())
11142 {
11143 const VmaSuballocation& suballoc = suballocations1st[index1st];
11144 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
11145 {
11146 if(suballoc.hAllocation != VK_NULL_HANDLE)
11147 {
11148 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
11149 if(suballoc.hAllocation->CanBecomeLost() &&
11150 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
11151 {
11152 ++pAllocationRequest->itemsToMakeLostCount;
11153 pAllocationRequest->sumItemSize += suballoc.size;
11154 }
11155 else
11156 {
11157 return false;
11158 }
11159 }
11160 }
11161 else
11162 {
11163 // Already on next page.
11164 break;
11165 }
11166 ++index1st;
11167 }
11168 }
11169
11170 // Special case: There is not enough room at the end for this allocation, even after making all from the 1st lost.
11171 if(index1st == suballocations1st.size() &&
11172 resultOffset + allocSize + VMA_DEBUG_MARGIN > size)
11173 {
11174 // TODO: This is a known bug that it's not yet implemented and the allocation is failing.
11175 VMA_DEBUG_LOG("Unsupported special case in custom pool with linear allocation algorithm used as ring buffer with allocations that can be lost.");
11176 }
11177 }
11178
11179 // There is enough free space at the end after alignment.
11180 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= size) ||
11181 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
11182 {
11183 // Check next suballocations for BufferImageGranularity conflicts.
11184 // If conflict exists, allocation cannot be made here.
11185 if(bufferImageGranularity > 1)
11186 {
11187 for(size_t nextSuballocIndex = index1st;
11188 nextSuballocIndex < suballocations1st.size();
11189 nextSuballocIndex++)
11190 {
11191 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
11192 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
11193 {
11194 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
11195 {
11196 return false;
11197 }
11198 }
11199 else
11200 {
11201 // Already on next page.
11202 break;
11203 }
11204 }
11205 }
11206
11207 // All tests passed: Success.
11208 pAllocationRequest->offset = resultOffset;
11209 pAllocationRequest->sumFreeSize =
11210 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
11211 - resultBaseOffset
11212 - pAllocationRequest->sumItemSize;
11213 pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
11214 // pAllocationRequest->item, customData unused.
11215 return true;
11216 }
11217 }
11218
11219 return false;
11220 }
11221
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11222 bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
11223 uint32_t currentFrameIndex,
11224 uint32_t frameInUseCount,
11225 VmaAllocationRequest* pAllocationRequest)
11226 {
11227 if(pAllocationRequest->itemsToMakeLostCount == 0)
11228 {
11229 return true;
11230 }
11231
11232 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
11233
11234 // We always start from 1st.
11235 SuballocationVectorType* suballocations = &AccessSuballocations1st();
11236 size_t index = m_1stNullItemsBeginCount;
11237 size_t madeLostCount = 0;
11238 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
11239 {
11240 if(index == suballocations->size())
11241 {
11242 index = 0;
11243 // If we get to the end of 1st, we wrap around to beginning of 2nd of 1st.
11244 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11245 {
11246 suballocations = &AccessSuballocations2nd();
11247 }
11248 // else: m_2ndVectorMode == SECOND_VECTOR_EMPTY:
11249 // suballocations continues pointing at AccessSuballocations1st().
11250 VMA_ASSERT(!suballocations->empty());
11251 }
11252 VmaSuballocation& suballoc = (*suballocations)[index];
11253 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11254 {
11255 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
11256 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
11257 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11258 {
11259 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11260 suballoc.hAllocation = VK_NULL_HANDLE;
11261 m_SumFreeSize += suballoc.size;
11262 if(suballocations == &AccessSuballocations1st())
11263 {
11264 ++m_1stNullItemsMiddleCount;
11265 }
11266 else
11267 {
11268 ++m_2ndNullItemsCount;
11269 }
11270 ++madeLostCount;
11271 }
11272 else
11273 {
11274 return false;
11275 }
11276 }
11277 ++index;
11278 }
11279
11280 CleanupAfterFree();
11281 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
11282
11283 return true;
11284 }
11285
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11286 uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11287 {
11288 uint32_t lostAllocationCount = 0;
11289
11290 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11291 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11292 {
11293 VmaSuballocation& suballoc = suballocations1st[i];
11294 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11295 suballoc.hAllocation->CanBecomeLost() &&
11296 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11297 {
11298 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11299 suballoc.hAllocation = VK_NULL_HANDLE;
11300 ++m_1stNullItemsMiddleCount;
11301 m_SumFreeSize += suballoc.size;
11302 ++lostAllocationCount;
11303 }
11304 }
11305
11306 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11307 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11308 {
11309 VmaSuballocation& suballoc = suballocations2nd[i];
11310 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
11311 suballoc.hAllocation->CanBecomeLost() &&
11312 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
11313 {
11314 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11315 suballoc.hAllocation = VK_NULL_HANDLE;
11316 ++m_2ndNullItemsCount;
11317 m_SumFreeSize += suballoc.size;
11318 ++lostAllocationCount;
11319 }
11320 }
11321
11322 if(lostAllocationCount)
11323 {
11324 CleanupAfterFree();
11325 }
11326
11327 return lostAllocationCount;
11328 }
11329
CheckCorruption(const void * pBlockData)11330 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
11331 {
11332 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11333 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
11334 {
11335 const VmaSuballocation& suballoc = suballocations1st[i];
11336 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11337 {
11338 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11339 {
11340 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11341 return VK_ERROR_VALIDATION_FAILED_EXT;
11342 }
11343 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11344 {
11345 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11346 return VK_ERROR_VALIDATION_FAILED_EXT;
11347 }
11348 }
11349 }
11350
11351 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11352 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
11353 {
11354 const VmaSuballocation& suballoc = suballocations2nd[i];
11355 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
11356 {
11357 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
11358 {
11359 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
11360 return VK_ERROR_VALIDATION_FAILED_EXT;
11361 }
11362 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
11363 {
11364 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
11365 return VK_ERROR_VALIDATION_FAILED_EXT;
11366 }
11367 }
11368 }
11369
11370 return VK_SUCCESS;
11371 }
11372
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11373 void VmaBlockMetadata_Linear::Alloc(
11374 const VmaAllocationRequest& request,
11375 VmaSuballocationType type,
11376 VkDeviceSize allocSize,
11377 VmaAllocation hAllocation)
11378 {
11379 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
11380
11381 switch(request.type)
11382 {
11383 case VmaAllocationRequestType::UpperAddress:
11384 {
11385 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
11386 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
11387 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11388 suballocations2nd.push_back(newSuballoc);
11389 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
11390 }
11391 break;
11392 case VmaAllocationRequestType::EndOf1st:
11393 {
11394 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11395
11396 VMA_ASSERT(suballocations1st.empty() ||
11397 request.offset >= suballocations1st.back().offset + suballocations1st.back().size);
11398 // Check if it fits before the end of the block.
11399 VMA_ASSERT(request.offset + allocSize <= GetSize());
11400
11401 suballocations1st.push_back(newSuballoc);
11402 }
11403 break;
11404 case VmaAllocationRequestType::EndOf2nd:
11405 {
11406 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11407 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
11408 VMA_ASSERT(!suballocations1st.empty() &&
11409 request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset);
11410 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11411
11412 switch(m_2ndVectorMode)
11413 {
11414 case SECOND_VECTOR_EMPTY:
11415 // First allocation from second part ring buffer.
11416 VMA_ASSERT(suballocations2nd.empty());
11417 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
11418 break;
11419 case SECOND_VECTOR_RING_BUFFER:
11420 // 2-part ring buffer is already started.
11421 VMA_ASSERT(!suballocations2nd.empty());
11422 break;
11423 case SECOND_VECTOR_DOUBLE_STACK:
11424 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
11425 break;
11426 default:
11427 VMA_ASSERT(0);
11428 }
11429
11430 suballocations2nd.push_back(newSuballoc);
11431 }
11432 break;
11433 default:
11434 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
11435 }
11436
11437 m_SumFreeSize -= newSuballoc.size;
11438 }
11439
Free(const VmaAllocation allocation)11440 void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
11441 {
11442 FreeAtOffset(allocation->GetOffset());
11443 }
11444
FreeAtOffset(VkDeviceSize offset)11445 void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
11446 {
11447 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11448 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11449
11450 if(!suballocations1st.empty())
11451 {
11452 // First allocation: Mark it as next empty at the beginning.
11453 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
11454 if(firstSuballoc.offset == offset)
11455 {
11456 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
11457 firstSuballoc.hAllocation = VK_NULL_HANDLE;
11458 m_SumFreeSize += firstSuballoc.size;
11459 ++m_1stNullItemsBeginCount;
11460 CleanupAfterFree();
11461 return;
11462 }
11463 }
11464
11465 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
11466 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
11467 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
11468 {
11469 VmaSuballocation& lastSuballoc = suballocations2nd.back();
11470 if(lastSuballoc.offset == offset)
11471 {
11472 m_SumFreeSize += lastSuballoc.size;
11473 suballocations2nd.pop_back();
11474 CleanupAfterFree();
11475 return;
11476 }
11477 }
11478 // Last allocation in 1st vector.
11479 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
11480 {
11481 VmaSuballocation& lastSuballoc = suballocations1st.back();
11482 if(lastSuballoc.offset == offset)
11483 {
11484 m_SumFreeSize += lastSuballoc.size;
11485 suballocations1st.pop_back();
11486 CleanupAfterFree();
11487 return;
11488 }
11489 }
11490
11491 // Item from the middle of 1st vector.
11492 {
11493 VmaSuballocation refSuballoc;
11494 refSuballoc.offset = offset;
11495 // Rest of members stays uninitialized intentionally for better performance.
11496 SuballocationVectorType::iterator it = VmaBinaryFindSorted(
11497 suballocations1st.begin() + m_1stNullItemsBeginCount,
11498 suballocations1st.end(),
11499 refSuballoc,
11500 VmaSuballocationOffsetLess());
11501 if(it != suballocations1st.end())
11502 {
11503 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11504 it->hAllocation = VK_NULL_HANDLE;
11505 ++m_1stNullItemsMiddleCount;
11506 m_SumFreeSize += it->size;
11507 CleanupAfterFree();
11508 return;
11509 }
11510 }
11511
11512 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
11513 {
11514 // Item from the middle of 2nd vector.
11515 VmaSuballocation refSuballoc;
11516 refSuballoc.offset = offset;
11517 // Rest of members stays uninitialized intentionally for better performance.
11518 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
11519 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
11520 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
11521 if(it != suballocations2nd.end())
11522 {
11523 it->type = VMA_SUBALLOCATION_TYPE_FREE;
11524 it->hAllocation = VK_NULL_HANDLE;
11525 ++m_2ndNullItemsCount;
11526 m_SumFreeSize += it->size;
11527 CleanupAfterFree();
11528 return;
11529 }
11530 }
11531
11532 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
11533 }
11534
ShouldCompact1st()11535 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
11536 {
11537 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11538 const size_t suballocCount = AccessSuballocations1st().size();
11539 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
11540 }
11541
CleanupAfterFree()11542 void VmaBlockMetadata_Linear::CleanupAfterFree()
11543 {
11544 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
11545 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
11546
11547 if(IsEmpty())
11548 {
11549 suballocations1st.clear();
11550 suballocations2nd.clear();
11551 m_1stNullItemsBeginCount = 0;
11552 m_1stNullItemsMiddleCount = 0;
11553 m_2ndNullItemsCount = 0;
11554 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11555 }
11556 else
11557 {
11558 const size_t suballoc1stCount = suballocations1st.size();
11559 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
11560 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
11561
11562 // Find more null items at the beginning of 1st vector.
11563 while(m_1stNullItemsBeginCount < suballoc1stCount &&
11564 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11565 {
11566 ++m_1stNullItemsBeginCount;
11567 --m_1stNullItemsMiddleCount;
11568 }
11569
11570 // Find more null items at the end of 1st vector.
11571 while(m_1stNullItemsMiddleCount > 0 &&
11572 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
11573 {
11574 --m_1stNullItemsMiddleCount;
11575 suballocations1st.pop_back();
11576 }
11577
11578 // Find more null items at the end of 2nd vector.
11579 while(m_2ndNullItemsCount > 0 &&
11580 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
11581 {
11582 --m_2ndNullItemsCount;
11583 suballocations2nd.pop_back();
11584 }
11585
11586 // Find more null items at the beginning of 2nd vector.
11587 while(m_2ndNullItemsCount > 0 &&
11588 suballocations2nd[0].hAllocation == VK_NULL_HANDLE)
11589 {
11590 --m_2ndNullItemsCount;
11591 VmaVectorRemove(suballocations2nd, 0);
11592 }
11593
11594 if(ShouldCompact1st())
11595 {
11596 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
11597 size_t srcIndex = m_1stNullItemsBeginCount;
11598 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
11599 {
11600 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
11601 {
11602 ++srcIndex;
11603 }
11604 if(dstIndex != srcIndex)
11605 {
11606 suballocations1st[dstIndex] = suballocations1st[srcIndex];
11607 }
11608 ++srcIndex;
11609 }
11610 suballocations1st.resize(nonNullItemCount);
11611 m_1stNullItemsBeginCount = 0;
11612 m_1stNullItemsMiddleCount = 0;
11613 }
11614
11615 // 2nd vector became empty.
11616 if(suballocations2nd.empty())
11617 {
11618 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11619 }
11620
11621 // 1st vector became empty.
11622 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
11623 {
11624 suballocations1st.clear();
11625 m_1stNullItemsBeginCount = 0;
11626
11627 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
11628 {
11629 // Swap 1st with 2nd. Now 2nd is empty.
11630 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
11631 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
11632 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
11633 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
11634 {
11635 ++m_1stNullItemsBeginCount;
11636 --m_1stNullItemsMiddleCount;
11637 }
11638 m_2ndNullItemsCount = 0;
11639 m_1stVectorIndex ^= 1;
11640 }
11641 }
11642 }
11643
11644 VMA_HEAVY_ASSERT(Validate());
11645 }
11646
11647
11648 ////////////////////////////////////////////////////////////////////////////////
11649 // class VmaBlockMetadata_Buddy
11650
VmaBlockMetadata_Buddy(VmaAllocator hAllocator)11651 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
11652 VmaBlockMetadata(hAllocator),
11653 m_Root(VMA_NULL),
11654 m_AllocationCount(0),
11655 m_FreeCount(1),
11656 m_SumFreeSize(0)
11657 {
11658 memset(m_FreeList, 0, sizeof(m_FreeList));
11659 }
11660
~VmaBlockMetadata_Buddy()11661 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
11662 {
11663 DeleteNode(m_Root);
11664 }
11665
Init(VkDeviceSize size)11666 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
11667 {
11668 VmaBlockMetadata::Init(size);
11669
11670 m_UsableSize = VmaPrevPow2(size);
11671 m_SumFreeSize = m_UsableSize;
11672
11673 // Calculate m_LevelCount.
11674 m_LevelCount = 1;
11675 while(m_LevelCount < MAX_LEVELS &&
11676 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
11677 {
11678 ++m_LevelCount;
11679 }
11680
11681 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
11682 rootNode->offset = 0;
11683 rootNode->type = Node::TYPE_FREE;
11684 rootNode->parent = VMA_NULL;
11685 rootNode->buddy = VMA_NULL;
11686
11687 m_Root = rootNode;
11688 AddToFreeListFront(0, rootNode);
11689 }
11690
Validate()11691 bool VmaBlockMetadata_Buddy::Validate() const
11692 {
11693 // Validate tree.
11694 ValidationContext ctx;
11695 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
11696 {
11697 VMA_VALIDATE(false && "ValidateNode failed.");
11698 }
11699 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
11700 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
11701
11702 // Validate free node lists.
11703 for(uint32_t level = 0; level < m_LevelCount; ++level)
11704 {
11705 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
11706 m_FreeList[level].front->free.prev == VMA_NULL);
11707
11708 for(Node* node = m_FreeList[level].front;
11709 node != VMA_NULL;
11710 node = node->free.next)
11711 {
11712 VMA_VALIDATE(node->type == Node::TYPE_FREE);
11713
11714 if(node->free.next == VMA_NULL)
11715 {
11716 VMA_VALIDATE(m_FreeList[level].back == node);
11717 }
11718 else
11719 {
11720 VMA_VALIDATE(node->free.next->free.prev == node);
11721 }
11722 }
11723 }
11724
11725 // Validate that free lists ar higher levels are empty.
11726 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
11727 {
11728 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
11729 }
11730
11731 return true;
11732 }
11733
GetUnusedRangeSizeMax()11734 VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
11735 {
11736 for(uint32_t level = 0; level < m_LevelCount; ++level)
11737 {
11738 if(m_FreeList[level].front != VMA_NULL)
11739 {
11740 return LevelToNodeSize(level);
11741 }
11742 }
11743 return 0;
11744 }
11745
CalcAllocationStatInfo(VmaStatInfo & outInfo)11746 void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
11747 {
11748 const VkDeviceSize unusableSize = GetUnusableSize();
11749
11750 outInfo.blockCount = 1;
11751
11752 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
11753 outInfo.usedBytes = outInfo.unusedBytes = 0;
11754
11755 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
11756 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
11757 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
11758
11759 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
11760
11761 if(unusableSize > 0)
11762 {
11763 ++outInfo.unusedRangeCount;
11764 outInfo.unusedBytes += unusableSize;
11765 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
11766 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
11767 }
11768 }
11769
AddPoolStats(VmaPoolStats & inoutStats)11770 void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
11771 {
11772 const VkDeviceSize unusableSize = GetUnusableSize();
11773
11774 inoutStats.size += GetSize();
11775 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
11776 inoutStats.allocationCount += m_AllocationCount;
11777 inoutStats.unusedRangeCount += m_FreeCount;
11778 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
11779
11780 if(unusableSize > 0)
11781 {
11782 ++inoutStats.unusedRangeCount;
11783 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
11784 }
11785 }
11786
11787 #if VMA_STATS_STRING_ENABLED
11788
PrintDetailedMap(class VmaJsonWriter & json)11789 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
11790 {
11791 // TODO optimize
11792 VmaStatInfo stat;
11793 CalcAllocationStatInfo(stat);
11794
11795 PrintDetailedMap_Begin(
11796 json,
11797 stat.unusedBytes,
11798 stat.allocationCount,
11799 stat.unusedRangeCount);
11800
11801 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
11802
11803 const VkDeviceSize unusableSize = GetUnusableSize();
11804 if(unusableSize > 0)
11805 {
11806 PrintDetailedMap_UnusedRange(json,
11807 m_UsableSize, // offset
11808 unusableSize); // size
11809 }
11810
11811 PrintDetailedMap_End(json);
11812 }
11813
11814 #endif // #if VMA_STATS_STRING_ENABLED
11815
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,bool canMakeOtherLost,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)11816 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
11817 uint32_t currentFrameIndex,
11818 uint32_t frameInUseCount,
11819 VkDeviceSize bufferImageGranularity,
11820 VkDeviceSize allocSize,
11821 VkDeviceSize allocAlignment,
11822 bool upperAddress,
11823 VmaSuballocationType allocType,
11824 bool canMakeOtherLost,
11825 uint32_t strategy,
11826 VmaAllocationRequest* pAllocationRequest)
11827 {
11828 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
11829
11830 // Simple way to respect bufferImageGranularity. May be optimized some day.
11831 // Whenever it might be an OPTIMAL image...
11832 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
11833 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
11834 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
11835 {
11836 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
11837 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
11838 }
11839
11840 if(allocSize > m_UsableSize)
11841 {
11842 return false;
11843 }
11844
11845 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11846 for(uint32_t level = targetLevel + 1; level--; )
11847 {
11848 for(Node* freeNode = m_FreeList[level].front;
11849 freeNode != VMA_NULL;
11850 freeNode = freeNode->free.next)
11851 {
11852 if(freeNode->offset % allocAlignment == 0)
11853 {
11854 pAllocationRequest->type = VmaAllocationRequestType::Normal;
11855 pAllocationRequest->offset = freeNode->offset;
11856 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
11857 pAllocationRequest->sumItemSize = 0;
11858 pAllocationRequest->itemsToMakeLostCount = 0;
11859 pAllocationRequest->customData = (void*)(uintptr_t)level;
11860 return true;
11861 }
11862 }
11863 }
11864
11865 return false;
11866 }
11867
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)11868 bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
11869 uint32_t currentFrameIndex,
11870 uint32_t frameInUseCount,
11871 VmaAllocationRequest* pAllocationRequest)
11872 {
11873 /*
11874 Lost allocations are not supported in buddy allocator at the moment.
11875 Support might be added in the future.
11876 */
11877 return pAllocationRequest->itemsToMakeLostCount == 0;
11878 }
11879
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)11880 uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
11881 {
11882 /*
11883 Lost allocations are not supported in buddy allocator at the moment.
11884 Support might be added in the future.
11885 */
11886 return 0;
11887 }
11888
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)11889 void VmaBlockMetadata_Buddy::Alloc(
11890 const VmaAllocationRequest& request,
11891 VmaSuballocationType type,
11892 VkDeviceSize allocSize,
11893 VmaAllocation hAllocation)
11894 {
11895 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
11896
11897 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
11898 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
11899
11900 Node* currNode = m_FreeList[currLevel].front;
11901 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11902 while(currNode->offset != request.offset)
11903 {
11904 currNode = currNode->free.next;
11905 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
11906 }
11907
11908 // Go down, splitting free nodes.
11909 while(currLevel < targetLevel)
11910 {
11911 // currNode is already first free node at currLevel.
11912 // Remove it from list of free nodes at this currLevel.
11913 RemoveFromFreeList(currLevel, currNode);
11914
11915 const uint32_t childrenLevel = currLevel + 1;
11916
11917 // Create two free sub-nodes.
11918 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
11919 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
11920
11921 leftChild->offset = currNode->offset;
11922 leftChild->type = Node::TYPE_FREE;
11923 leftChild->parent = currNode;
11924 leftChild->buddy = rightChild;
11925
11926 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
11927 rightChild->type = Node::TYPE_FREE;
11928 rightChild->parent = currNode;
11929 rightChild->buddy = leftChild;
11930
11931 // Convert current currNode to split type.
11932 currNode->type = Node::TYPE_SPLIT;
11933 currNode->split.leftChild = leftChild;
11934
11935 // Add child nodes to free list. Order is important!
11936 AddToFreeListFront(childrenLevel, rightChild);
11937 AddToFreeListFront(childrenLevel, leftChild);
11938
11939 ++m_FreeCount;
11940 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
11941 ++currLevel;
11942 currNode = m_FreeList[currLevel].front;
11943
11944 /*
11945 We can be sure that currNode, as left child of node previously split,
11946 also fullfills the alignment requirement.
11947 */
11948 }
11949
11950 // Remove from free list.
11951 VMA_ASSERT(currLevel == targetLevel &&
11952 currNode != VMA_NULL &&
11953 currNode->type == Node::TYPE_FREE);
11954 RemoveFromFreeList(currLevel, currNode);
11955
11956 // Convert to allocation node.
11957 currNode->type = Node::TYPE_ALLOCATION;
11958 currNode->allocation.alloc = hAllocation;
11959
11960 ++m_AllocationCount;
11961 --m_FreeCount;
11962 m_SumFreeSize -= allocSize;
11963 }
11964
DeleteNode(Node * node)11965 void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
11966 {
11967 if(node->type == Node::TYPE_SPLIT)
11968 {
11969 DeleteNode(node->split.leftChild->buddy);
11970 DeleteNode(node->split.leftChild);
11971 }
11972
11973 vma_delete(GetAllocationCallbacks(), node);
11974 }
11975
ValidateNode(ValidationContext & ctx,const Node * parent,const Node * curr,uint32_t level,VkDeviceSize levelNodeSize)11976 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
11977 {
11978 VMA_VALIDATE(level < m_LevelCount);
11979 VMA_VALIDATE(curr->parent == parent);
11980 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
11981 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
11982 switch(curr->type)
11983 {
11984 case Node::TYPE_FREE:
11985 // curr->free.prev, next are validated separately.
11986 ctx.calculatedSumFreeSize += levelNodeSize;
11987 ++ctx.calculatedFreeCount;
11988 break;
11989 case Node::TYPE_ALLOCATION:
11990 ++ctx.calculatedAllocationCount;
11991 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
11992 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
11993 break;
11994 case Node::TYPE_SPLIT:
11995 {
11996 const uint32_t childrenLevel = level + 1;
11997 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
11998 const Node* const leftChild = curr->split.leftChild;
11999 VMA_VALIDATE(leftChild != VMA_NULL);
12000 VMA_VALIDATE(leftChild->offset == curr->offset);
12001 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
12002 {
12003 VMA_VALIDATE(false && "ValidateNode for left child failed.");
12004 }
12005 const Node* const rightChild = leftChild->buddy;
12006 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
12007 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
12008 {
12009 VMA_VALIDATE(false && "ValidateNode for right child failed.");
12010 }
12011 }
12012 break;
12013 default:
12014 return false;
12015 }
12016
12017 return true;
12018 }
12019
AllocSizeToLevel(VkDeviceSize allocSize)12020 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
12021 {
12022 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
12023 uint32_t level = 0;
12024 VkDeviceSize currLevelNodeSize = m_UsableSize;
12025 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
12026 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
12027 {
12028 ++level;
12029 currLevelNodeSize = nextLevelNodeSize;
12030 nextLevelNodeSize = currLevelNodeSize >> 1;
12031 }
12032 return level;
12033 }
12034
FreeAtOffset(VmaAllocation alloc,VkDeviceSize offset)12035 void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
12036 {
12037 // Find node and level.
12038 Node* node = m_Root;
12039 VkDeviceSize nodeOffset = 0;
12040 uint32_t level = 0;
12041 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
12042 while(node->type == Node::TYPE_SPLIT)
12043 {
12044 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
12045 if(offset < nodeOffset + nextLevelSize)
12046 {
12047 node = node->split.leftChild;
12048 }
12049 else
12050 {
12051 node = node->split.leftChild->buddy;
12052 nodeOffset += nextLevelSize;
12053 }
12054 ++level;
12055 levelNodeSize = nextLevelSize;
12056 }
12057
12058 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
12059 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
12060
12061 ++m_FreeCount;
12062 --m_AllocationCount;
12063 m_SumFreeSize += alloc->GetSize();
12064
12065 node->type = Node::TYPE_FREE;
12066
12067 // Join free nodes if possible.
12068 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
12069 {
12070 RemoveFromFreeList(level, node->buddy);
12071 Node* const parent = node->parent;
12072
12073 vma_delete(GetAllocationCallbacks(), node->buddy);
12074 vma_delete(GetAllocationCallbacks(), node);
12075 parent->type = Node::TYPE_FREE;
12076
12077 node = parent;
12078 --level;
12079 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
12080 --m_FreeCount;
12081 }
12082
12083 AddToFreeListFront(level, node);
12084 }
12085
CalcAllocationStatInfoNode(VmaStatInfo & outInfo,const Node * node,VkDeviceSize levelNodeSize)12086 void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
12087 {
12088 switch(node->type)
12089 {
12090 case Node::TYPE_FREE:
12091 ++outInfo.unusedRangeCount;
12092 outInfo.unusedBytes += levelNodeSize;
12093 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
12094 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
12095 break;
12096 case Node::TYPE_ALLOCATION:
12097 {
12098 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12099 ++outInfo.allocationCount;
12100 outInfo.usedBytes += allocSize;
12101 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
12102 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
12103
12104 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
12105 if(unusedRangeSize > 0)
12106 {
12107 ++outInfo.unusedRangeCount;
12108 outInfo.unusedBytes += unusedRangeSize;
12109 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
12110 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
12111 }
12112 }
12113 break;
12114 case Node::TYPE_SPLIT:
12115 {
12116 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12117 const Node* const leftChild = node->split.leftChild;
12118 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
12119 const Node* const rightChild = leftChild->buddy;
12120 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
12121 }
12122 break;
12123 default:
12124 VMA_ASSERT(0);
12125 }
12126 }
12127
AddToFreeListFront(uint32_t level,Node * node)12128 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
12129 {
12130 VMA_ASSERT(node->type == Node::TYPE_FREE);
12131
12132 // List is empty.
12133 Node* const frontNode = m_FreeList[level].front;
12134 if(frontNode == VMA_NULL)
12135 {
12136 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
12137 node->free.prev = node->free.next = VMA_NULL;
12138 m_FreeList[level].front = m_FreeList[level].back = node;
12139 }
12140 else
12141 {
12142 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
12143 node->free.prev = VMA_NULL;
12144 node->free.next = frontNode;
12145 frontNode->free.prev = node;
12146 m_FreeList[level].front = node;
12147 }
12148 }
12149
RemoveFromFreeList(uint32_t level,Node * node)12150 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
12151 {
12152 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
12153
12154 // It is at the front.
12155 if(node->free.prev == VMA_NULL)
12156 {
12157 VMA_ASSERT(m_FreeList[level].front == node);
12158 m_FreeList[level].front = node->free.next;
12159 }
12160 else
12161 {
12162 Node* const prevFreeNode = node->free.prev;
12163 VMA_ASSERT(prevFreeNode->free.next == node);
12164 prevFreeNode->free.next = node->free.next;
12165 }
12166
12167 // It is at the back.
12168 if(node->free.next == VMA_NULL)
12169 {
12170 VMA_ASSERT(m_FreeList[level].back == node);
12171 m_FreeList[level].back = node->free.prev;
12172 }
12173 else
12174 {
12175 Node* const nextFreeNode = node->free.next;
12176 VMA_ASSERT(nextFreeNode->free.prev == node);
12177 nextFreeNode->free.prev = node->free.prev;
12178 }
12179 }
12180
12181 #if VMA_STATS_STRING_ENABLED
PrintDetailedMapNode(class VmaJsonWriter & json,const Node * node,VkDeviceSize levelNodeSize)12182 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
12183 {
12184 switch(node->type)
12185 {
12186 case Node::TYPE_FREE:
12187 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
12188 break;
12189 case Node::TYPE_ALLOCATION:
12190 {
12191 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
12192 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
12193 if(allocSize < levelNodeSize)
12194 {
12195 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
12196 }
12197 }
12198 break;
12199 case Node::TYPE_SPLIT:
12200 {
12201 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
12202 const Node* const leftChild = node->split.leftChild;
12203 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
12204 const Node* const rightChild = leftChild->buddy;
12205 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
12206 }
12207 break;
12208 default:
12209 VMA_ASSERT(0);
12210 }
12211 }
12212 #endif // #if VMA_STATS_STRING_ENABLED
12213
12214
12215 ////////////////////////////////////////////////////////////////////////////////
12216 // class VmaDeviceMemoryBlock
12217
VmaDeviceMemoryBlock(VmaAllocator hAllocator)12218 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
12219 m_pMetadata(VMA_NULL),
12220 m_MemoryTypeIndex(UINT32_MAX),
12221 m_Id(0),
12222 m_hMemory(VK_NULL_HANDLE),
12223 m_MapCount(0),
12224 m_pMappedData(VMA_NULL),
12225 m_BindComplete(false)
12226 {
12227 }
12228
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm)12229 void VmaDeviceMemoryBlock::Init(
12230 VmaAllocator hAllocator,
12231 VmaPool hParentPool,
12232 uint32_t newMemoryTypeIndex,
12233 VkDeviceMemory newMemory,
12234 VkDeviceSize newSize,
12235 uint32_t id,
12236 uint32_t algorithm)
12237 {
12238 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
12239
12240 m_hParentPool = hParentPool;
12241 m_MemoryTypeIndex = newMemoryTypeIndex;
12242 m_Id = id;
12243 m_hMemory = newMemory;
12244
12245 switch(algorithm)
12246 {
12247 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
12248 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
12249 break;
12250 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
12251 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
12252 break;
12253 default:
12254 VMA_ASSERT(0);
12255 // Fall-through.
12256 case 0:
12257 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
12258 }
12259 m_pMetadata->Init(newSize);
12260 }
12261
Destroy(VmaAllocator allocator)12262 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
12263 {
12264 // This is the most important assert in the entire library.
12265 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
12266 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
12267
12268 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
12269 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
12270 m_hMemory = VK_NULL_HANDLE;
12271
12272 vma_delete(allocator, m_pMetadata);
12273 m_pMetadata = VMA_NULL;
12274 }
12275
Validate()12276 bool VmaDeviceMemoryBlock::Validate() const
12277 {
12278 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
12279 (m_pMetadata->GetSize() != 0));
12280
12281 return m_pMetadata->Validate();
12282 }
12283
CheckCorruption(VmaAllocator hAllocator)12284 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
12285 {
12286 void* pData = nullptr;
12287 VkResult res = Map(hAllocator, 1, &pData);
12288 if(res != VK_SUCCESS)
12289 {
12290 return res;
12291 }
12292
12293 res = m_pMetadata->CheckCorruption(pData);
12294
12295 Unmap(hAllocator, 1);
12296
12297 return res;
12298 }
12299
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)12300 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
12301 {
12302 if(count == 0)
12303 {
12304 return VK_SUCCESS;
12305 }
12306
12307 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12308 if(m_MapCount != 0)
12309 {
12310 m_MapCount += count;
12311 VMA_ASSERT(m_pMappedData != VMA_NULL);
12312 if(ppData != VMA_NULL)
12313 {
12314 *ppData = m_pMappedData;
12315 }
12316 return VK_SUCCESS;
12317 }
12318 else
12319 {
12320 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12321 hAllocator->m_hDevice,
12322 m_hMemory,
12323 0, // offset
12324 VK_WHOLE_SIZE,
12325 0, // flags
12326 &m_pMappedData);
12327 if(result == VK_SUCCESS)
12328 {
12329 if(ppData != VMA_NULL)
12330 {
12331 *ppData = m_pMappedData;
12332 }
12333 m_MapCount = count;
12334 }
12335 return result;
12336 }
12337 }
12338
Unmap(VmaAllocator hAllocator,uint32_t count)12339 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
12340 {
12341 if(count == 0)
12342 {
12343 return;
12344 }
12345
12346 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12347 if(m_MapCount >= count)
12348 {
12349 m_MapCount -= count;
12350 if(m_MapCount == 0)
12351 {
12352 m_pMappedData = VMA_NULL;
12353 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
12354 }
12355 }
12356 else
12357 {
12358 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
12359 }
12360 }
12361
WriteMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12362 VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12363 {
12364 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12365 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12366
12367 void* pData;
12368 VkResult res = Map(hAllocator, 1, &pData);
12369 if(res != VK_SUCCESS)
12370 {
12371 return res;
12372 }
12373
12374 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
12375 VmaWriteMagicValue(pData, allocOffset + allocSize);
12376
12377 Unmap(hAllocator, 1);
12378
12379 return VK_SUCCESS;
12380 }
12381
ValidateMagicValueAroundAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)12382 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
12383 {
12384 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
12385 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
12386
12387 void* pData;
12388 VkResult res = Map(hAllocator, 1, &pData);
12389 if(res != VK_SUCCESS)
12390 {
12391 return res;
12392 }
12393
12394 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
12395 {
12396 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
12397 }
12398 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
12399 {
12400 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
12401 }
12402
12403 Unmap(hAllocator, 1);
12404
12405 return VK_SUCCESS;
12406 }
12407
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)12408 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
12409 const VmaAllocator hAllocator,
12410 const VmaAllocation hAllocation,
12411 VkDeviceSize allocationLocalOffset,
12412 VkBuffer hBuffer,
12413 const void* pNext)
12414 {
12415 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12416 hAllocation->GetBlock() == this);
12417 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12418 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12419 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12420 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12421 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12422 return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
12423 }
12424
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)12425 VkResult VmaDeviceMemoryBlock::BindImageMemory(
12426 const VmaAllocator hAllocator,
12427 const VmaAllocation hAllocation,
12428 VkDeviceSize allocationLocalOffset,
12429 VkImage hImage,
12430 const void* pNext)
12431 {
12432 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
12433 hAllocation->GetBlock() == this);
12434 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
12435 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
12436 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
12437 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
12438 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
12439 return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
12440 }
12441
InitStatInfo(VmaStatInfo & outInfo)12442 static void InitStatInfo(VmaStatInfo& outInfo)
12443 {
12444 memset(&outInfo, 0, sizeof(outInfo));
12445 outInfo.allocationSizeMin = UINT64_MAX;
12446 outInfo.unusedRangeSizeMin = UINT64_MAX;
12447 }
12448
12449 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)12450 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
12451 {
12452 inoutInfo.blockCount += srcInfo.blockCount;
12453 inoutInfo.allocationCount += srcInfo.allocationCount;
12454 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
12455 inoutInfo.usedBytes += srcInfo.usedBytes;
12456 inoutInfo.unusedBytes += srcInfo.unusedBytes;
12457 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
12458 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
12459 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
12460 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
12461 }
12462
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)12463 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
12464 {
12465 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
12466 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
12467 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
12468 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
12469 }
12470
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)12471 VmaPool_T::VmaPool_T(
12472 VmaAllocator hAllocator,
12473 const VmaPoolCreateInfo& createInfo,
12474 VkDeviceSize preferredBlockSize) :
12475 m_BlockVector(
12476 hAllocator,
12477 this, // hParentPool
12478 createInfo.memoryTypeIndex,
12479 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
12480 createInfo.minBlockCount,
12481 createInfo.maxBlockCount,
12482 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
12483 createInfo.frameInUseCount,
12484 createInfo.blockSize != 0, // explicitBlockSize
12485 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
12486 m_Id(0),
12487 m_Name(VMA_NULL)
12488 {
12489 }
12490
~VmaPool_T()12491 VmaPool_T::~VmaPool_T()
12492 {
12493 }
12494
SetName(const char * pName)12495 void VmaPool_T::SetName(const char* pName)
12496 {
12497 const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
12498 VmaFreeString(allocs, m_Name);
12499
12500 if(pName != VMA_NULL)
12501 {
12502 m_Name = VmaCreateStringCopy(allocs, pName);
12503 }
12504 else
12505 {
12506 m_Name = VMA_NULL;
12507 }
12508 }
12509
12510 #if VMA_STATS_STRING_ENABLED
12511
12512 #endif // #if VMA_STATS_STRING_ENABLED
12513
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool explicitBlockSize,uint32_t algorithm)12514 VmaBlockVector::VmaBlockVector(
12515 VmaAllocator hAllocator,
12516 VmaPool hParentPool,
12517 uint32_t memoryTypeIndex,
12518 VkDeviceSize preferredBlockSize,
12519 size_t minBlockCount,
12520 size_t maxBlockCount,
12521 VkDeviceSize bufferImageGranularity,
12522 uint32_t frameInUseCount,
12523 bool explicitBlockSize,
12524 uint32_t algorithm) :
12525 m_hAllocator(hAllocator),
12526 m_hParentPool(hParentPool),
12527 m_MemoryTypeIndex(memoryTypeIndex),
12528 m_PreferredBlockSize(preferredBlockSize),
12529 m_MinBlockCount(minBlockCount),
12530 m_MaxBlockCount(maxBlockCount),
12531 m_BufferImageGranularity(bufferImageGranularity),
12532 m_FrameInUseCount(frameInUseCount),
12533 m_ExplicitBlockSize(explicitBlockSize),
12534 m_Algorithm(algorithm),
12535 m_HasEmptyBlock(false),
12536 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12537 m_NextBlockId(0)
12538 {
12539 }
12540
~VmaBlockVector()12541 VmaBlockVector::~VmaBlockVector()
12542 {
12543 for(size_t i = m_Blocks.size(); i--; )
12544 {
12545 m_Blocks[i]->Destroy(m_hAllocator);
12546 vma_delete(m_hAllocator, m_Blocks[i]);
12547 }
12548 }
12549
CreateMinBlocks()12550 VkResult VmaBlockVector::CreateMinBlocks()
12551 {
12552 for(size_t i = 0; i < m_MinBlockCount; ++i)
12553 {
12554 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12555 if(res != VK_SUCCESS)
12556 {
12557 return res;
12558 }
12559 }
12560 return VK_SUCCESS;
12561 }
12562
GetPoolStats(VmaPoolStats * pStats)12563 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
12564 {
12565 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12566
12567 const size_t blockCount = m_Blocks.size();
12568
12569 pStats->size = 0;
12570 pStats->unusedSize = 0;
12571 pStats->allocationCount = 0;
12572 pStats->unusedRangeCount = 0;
12573 pStats->unusedRangeSizeMax = 0;
12574 pStats->blockCount = blockCount;
12575
12576 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12577 {
12578 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12579 VMA_ASSERT(pBlock);
12580 VMA_HEAVY_ASSERT(pBlock->Validate());
12581 pBlock->m_pMetadata->AddPoolStats(*pStats);
12582 }
12583 }
12584
IsEmpty()12585 bool VmaBlockVector::IsEmpty()
12586 {
12587 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12588 return m_Blocks.empty();
12589 }
12590
IsLastBlockBindComplete()12591 bool VmaBlockVector::IsLastBlockBindComplete()
12592 {
12593 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12594 if (m_Blocks.empty()) {
12595 return false;
12596 }
12597 VmaDeviceMemoryBlock* lastBlock = m_Blocks.back();
12598 if (!lastBlock) {
12599 return false;
12600 }
12601 return lastBlock->GetBindCompleteFlag();
12602 }
12603
IsCorruptionDetectionEnabled()12604 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12605 {
12606 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12607 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12608 (VMA_DEBUG_MARGIN > 0) &&
12609 (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12610 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12611 }
12612
12613 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
12614
Allocate(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12615 VkResult VmaBlockVector::Allocate(
12616 uint32_t currentFrameIndex,
12617 VkDeviceSize size,
12618 VkDeviceSize alignment,
12619 const VmaAllocationCreateInfo& createInfo,
12620 VmaSuballocationType suballocType,
12621 size_t allocationCount,
12622 VmaAllocation* pAllocations)
12623 {
12624 size_t allocIndex;
12625 VkResult res = VK_SUCCESS;
12626
12627 if(IsCorruptionDetectionEnabled())
12628 {
12629 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12630 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12631 }
12632
12633 {
12634 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12635 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12636 {
12637 res = AllocatePage(
12638 currentFrameIndex,
12639 size,
12640 alignment,
12641 createInfo,
12642 suballocType,
12643 pAllocations + allocIndex);
12644 if(res != VK_SUCCESS)
12645 {
12646 break;
12647 }
12648 }
12649 }
12650
12651 if(res != VK_SUCCESS)
12652 {
12653 // Free all already created allocations.
12654 while(allocIndex--)
12655 {
12656 Free(pAllocations[allocIndex]);
12657 }
12658 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12659 }
12660
12661 return res;
12662 }
12663
12664 // OH ISSUE: VMA preAlloc
AllocateReserved(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12665 VkResult VmaBlockVector::AllocateReserved(
12666 uint32_t currentFrameIndex,
12667 VkDeviceSize size,
12668 VkDeviceSize alignment,
12669 const VmaAllocationCreateInfo& createInfo,
12670 VmaSuballocationType suballocType,
12671 VmaAllocation* pAllocation)
12672 {
12673 size_t allocIndex;
12674 VkResult res = VK_SUCCESS;
12675
12676 if(IsCorruptionDetectionEnabled())
12677 {
12678 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12679 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12680 }
12681 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12682
12683 // Validate strategy.
12684 switch(strategy)
12685 {
12686 case 0:
12687 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12688 break;
12689 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12690 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12691 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12692 break;
12693 default:
12694 return VK_ERROR_FEATURE_NOT_PRESENT;
12695 }
12696
12697 {
12698 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12699 size_t newBlockIndex = 0;
12700 res = CreateBlock(m_PreferredBlockSize, &newBlockIndex);
12701 if(res != VK_SUCCESS)
12702 {
12703 return res;
12704 }
12705 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12706 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12707
12708 res = AllocateFromBlock(
12709 pBlock,
12710 currentFrameIndex,
12711 size,
12712 alignment,
12713 createInfo.flags,
12714 createInfo.pUserData,
12715 suballocType,
12716 strategy,
12717 pAllocation);
12718 }
12719 return res;
12720 }
12721
SwapLastBlock(VmaBlockVector * blockVector)12722 bool VmaBlockVector::SwapLastBlock(VmaBlockVector* blockVector)
12723 {
12724 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12725
12726 VmaDeviceMemoryBlock* lastBlock1 = m_Blocks.back();
12727 VmaDeviceMemoryBlock* lastBlock2 = blockVector->m_Blocks.back();
12728 if (lastBlock1 == nullptr || lastBlock2 == nullptr) {
12729 return false;
12730 }
12731 m_Blocks.pop_back();
12732 blockVector->m_Blocks.pop_back();
12733
12734 m_Blocks.push_back(lastBlock2);
12735 blockVector->m_Blocks.push_back(lastBlock1);
12736 return true;
12737 }
12738
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12739 VkResult VmaBlockVector::AllocatePage(
12740 uint32_t currentFrameIndex,
12741 VkDeviceSize size,
12742 VkDeviceSize alignment,
12743 const VmaAllocationCreateInfo& createInfo,
12744 VmaSuballocationType suballocType,
12745 VmaAllocation* pAllocation)
12746 {
12747 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12748 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12749 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12750 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12751
12752 VkDeviceSize freeMemory;
12753 {
12754 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12755 VmaBudget heapBudget = {};
12756 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12757 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12758 }
12759
12760 const bool canFallbackToDedicated = !IsCustomPool();
12761 const bool canCreateNewBlock =
12762 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12763 (m_Blocks.size() < m_MaxBlockCount) &&
12764 (freeMemory >= size || !canFallbackToDedicated);
12765 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12766
12767 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12768 // Which in turn is available only when maxBlockCount = 1.
12769 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12770 {
12771 canMakeOtherLost = false;
12772 }
12773
12774 // Upper address can only be used with linear allocator and within single memory block.
12775 if(isUpperAddress &&
12776 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12777 {
12778 return VK_ERROR_FEATURE_NOT_PRESENT;
12779 }
12780
12781 // Validate strategy.
12782 switch(strategy)
12783 {
12784 case 0:
12785 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12786 break;
12787 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12788 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12789 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12790 break;
12791 default:
12792 return VK_ERROR_FEATURE_NOT_PRESENT;
12793 }
12794
12795 // Early reject: requested allocation size is larger that maximum block size for this block vector.
12796 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12797 {
12798 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12799 }
12800
12801 /*
12802 Under certain condition, this whole section can be skipped for optimization, so
12803 we move on directly to trying to allocate with canMakeOtherLost. That's the case
12804 e.g. for custom pools with linear algorithm.
12805 */
12806 if(!canMakeOtherLost || canCreateNewBlock)
12807 {
12808 // 1. Search existing allocations. Try to allocate without making other allocations lost.
12809 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12810 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12811
12812 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12813 {
12814 // Use only last block.
12815 if(!m_Blocks.empty())
12816 {
12817 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12818 VMA_ASSERT(pCurrBlock);
12819 VkResult res = AllocateFromBlock(
12820 pCurrBlock,
12821 currentFrameIndex,
12822 size,
12823 alignment,
12824 allocFlagsCopy,
12825 createInfo.pUserData,
12826 suballocType,
12827 strategy,
12828 pAllocation);
12829 if(res == VK_SUCCESS)
12830 {
12831 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12832 return VK_SUCCESS;
12833 }
12834 }
12835 }
12836 else
12837 {
12838 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12839 {
12840 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12841 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12842 {
12843 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12844 VMA_ASSERT(pCurrBlock);
12845 VkResult res = AllocateFromBlock(
12846 pCurrBlock,
12847 currentFrameIndex,
12848 size,
12849 alignment,
12850 allocFlagsCopy,
12851 createInfo.pUserData,
12852 suballocType,
12853 strategy,
12854 pAllocation);
12855 if(res == VK_SUCCESS)
12856 {
12857 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12858 return VK_SUCCESS;
12859 }
12860 }
12861 }
12862 else // WORST_FIT, FIRST_FIT
12863 {
12864 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12865 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12866 {
12867 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12868 VMA_ASSERT(pCurrBlock);
12869 VkResult res = AllocateFromBlock(
12870 pCurrBlock,
12871 currentFrameIndex,
12872 size,
12873 alignment,
12874 allocFlagsCopy,
12875 createInfo.pUserData,
12876 suballocType,
12877 strategy,
12878 pAllocation);
12879 if(res == VK_SUCCESS)
12880 {
12881 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12882 return VK_SUCCESS;
12883 }
12884 }
12885 }
12886 }
12887
12888 // 2. Try to create new block.
12889 if(canCreateNewBlock)
12890 {
12891 // Calculate optimal size for new block.
12892 VkDeviceSize newBlockSize = m_PreferredBlockSize;
12893 uint32_t newBlockSizeShift = 0;
12894 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12895
12896 if(!m_ExplicitBlockSize)
12897 {
12898 // Allocate 1/8, 1/4, 1/2 as first blocks.
12899 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12900 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12901 {
12902 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12903 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12904 {
12905 newBlockSize = smallerNewBlockSize;
12906 ++newBlockSizeShift;
12907 }
12908 else
12909 {
12910 break;
12911 }
12912 }
12913 }
12914
12915 size_t newBlockIndex = 0;
12916 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12917 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12918 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12919 if(!m_ExplicitBlockSize)
12920 {
12921 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12922 {
12923 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12924 if(smallerNewBlockSize >= size)
12925 {
12926 newBlockSize = smallerNewBlockSize;
12927 ++newBlockSizeShift;
12928 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12929 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12930 }
12931 else
12932 {
12933 break;
12934 }
12935 }
12936 }
12937
12938 if(res == VK_SUCCESS)
12939 {
12940 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12941 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12942
12943 res = AllocateFromBlock(
12944 pBlock,
12945 currentFrameIndex,
12946 size,
12947 alignment,
12948 allocFlagsCopy,
12949 createInfo.pUserData,
12950 suballocType,
12951 strategy,
12952 pAllocation);
12953 if(res == VK_SUCCESS)
12954 {
12955 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12956 return VK_SUCCESS;
12957 }
12958 else
12959 {
12960 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12961 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12962 }
12963 }
12964 }
12965 }
12966
12967 // 3. Try to allocate from existing blocks with making other allocations lost.
12968 if(canMakeOtherLost)
12969 {
12970 uint32_t tryIndex = 0;
12971 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12972 {
12973 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12974 VmaAllocationRequest bestRequest = {};
12975 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12976
12977 // 1. Search existing allocations.
12978 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12979 {
12980 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12981 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12982 {
12983 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12984 VMA_ASSERT(pCurrBlock);
12985 VmaAllocationRequest currRequest = {};
12986 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12987 currentFrameIndex,
12988 m_FrameInUseCount,
12989 m_BufferImageGranularity,
12990 size,
12991 alignment,
12992 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12993 suballocType,
12994 canMakeOtherLost,
12995 strategy,
12996 &currRequest))
12997 {
12998 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12999 if(pBestRequestBlock == VMA_NULL ||
13000 currRequestCost < bestRequestCost)
13001 {
13002 pBestRequestBlock = pCurrBlock;
13003 bestRequest = currRequest;
13004 bestRequestCost = currRequestCost;
13005
13006 if(bestRequestCost == 0)
13007 {
13008 break;
13009 }
13010 }
13011 }
13012 }
13013 }
13014 else // WORST_FIT, FIRST_FIT
13015 {
13016 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13017 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13018 {
13019 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13020 VMA_ASSERT(pCurrBlock);
13021 VmaAllocationRequest currRequest = {};
13022 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13023 currentFrameIndex,
13024 m_FrameInUseCount,
13025 m_BufferImageGranularity,
13026 size,
13027 alignment,
13028 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13029 suballocType,
13030 canMakeOtherLost,
13031 strategy,
13032 &currRequest))
13033 {
13034 const VkDeviceSize currRequestCost = currRequest.CalcCost();
13035 if(pBestRequestBlock == VMA_NULL ||
13036 currRequestCost < bestRequestCost ||
13037 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13038 {
13039 pBestRequestBlock = pCurrBlock;
13040 bestRequest = currRequest;
13041 bestRequestCost = currRequestCost;
13042
13043 if(bestRequestCost == 0 ||
13044 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13045 {
13046 break;
13047 }
13048 }
13049 }
13050 }
13051 }
13052
13053 if(pBestRequestBlock != VMA_NULL)
13054 {
13055 if(mapped)
13056 {
13057 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13058 if(res != VK_SUCCESS)
13059 {
13060 return res;
13061 }
13062 }
13063
13064 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13065 currentFrameIndex,
13066 m_FrameInUseCount,
13067 &bestRequest))
13068 {
13069 // Allocate from this pBlock.
13070 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13071 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13072 UpdateHasEmptyBlock();
13073 (*pAllocation)->InitBlockAllocation(
13074 pBestRequestBlock,
13075 bestRequest.offset,
13076 alignment,
13077 size,
13078 m_MemoryTypeIndex,
13079 suballocType,
13080 mapped,
13081 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13082 IsNewBlockFlag());
13083 ClearNewBlockFlag();
13084 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13085 VMA_DEBUG_LOG(" Returned from existing block");
13086 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13087 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13088 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13089 {
13090 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13091 }
13092 if(IsCorruptionDetectionEnabled())
13093 {
13094 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13095 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13096 }
13097 return VK_SUCCESS;
13098 }
13099 // else: Some allocations must have been touched while we are here. Next try.
13100 }
13101 else
13102 {
13103 // Could not find place in any of the blocks - break outer loop.
13104 break;
13105 }
13106 }
13107 /* Maximum number of tries exceeded - a very unlike event when many other
13108 threads are simultaneously touching allocations making it impossible to make
13109 lost at the same time as we try to allocate. */
13110 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13111 {
13112 return VK_ERROR_TOO_MANY_OBJECTS;
13113 }
13114 }
13115
13116 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13117 }
13118
Free(const VmaAllocation hAllocation)13119 void VmaBlockVector::Free(
13120 const VmaAllocation hAllocation)
13121 {
13122 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13123
13124 bool budgetExceeded = false;
13125 {
13126 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13127 VmaBudget heapBudget = {};
13128 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13129 budgetExceeded = heapBudget.usage >= heapBudget.budget;
13130 }
13131
13132 // Scope for lock.
13133 {
13134 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13135
13136 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13137
13138 if(IsCorruptionDetectionEnabled())
13139 {
13140 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13141 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13142 }
13143
13144 if(hAllocation->IsPersistentMap())
13145 {
13146 pBlock->Unmap(m_hAllocator, 1);
13147 }
13148
13149 pBlock->m_pMetadata->Free(hAllocation);
13150 VMA_HEAVY_ASSERT(pBlock->Validate());
13151
13152 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13153
13154 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13155 // pBlock became empty after this deallocation.
13156 if(pBlock->m_pMetadata->IsEmpty())
13157 {
13158 // Already has empty block. We don't want to have two, so delete this one.
13159 if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13160 {
13161 pBlockToDelete = pBlock;
13162 Remove(pBlock);
13163 }
13164 // else: We now have an empty block - leave it.
13165 }
13166 // pBlock didn't become empty, but we have another empty block - find and free that one.
13167 // (This is optional, heuristics.)
13168 else if(m_HasEmptyBlock && canDeleteBlock)
13169 {
13170 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13171 if(pLastBlock->m_pMetadata->IsEmpty())
13172 {
13173 pBlockToDelete = pLastBlock;
13174 m_Blocks.pop_back();
13175 }
13176 }
13177
13178 UpdateHasEmptyBlock();
13179 IncrementallySortBlocks();
13180 }
13181
13182 // Destruction of a free block. Deferred until this point, outside of mutex
13183 // lock, for performance reason.
13184 if(pBlockToDelete != VMA_NULL)
13185 {
13186 VMA_DEBUG_LOG(" Deleted empty block");
13187 pBlockToDelete->Destroy(m_hAllocator);
13188 vma_delete(m_hAllocator, pBlockToDelete);
13189 }
13190 }
13191
13192 // OH ISSUE: VMA preAlloc
FreeReserved(const VmaAllocation hAllocation)13193 void VmaBlockVector::FreeReserved(
13194 const VmaAllocation hAllocation)
13195 {
13196 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13197
13198 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13199
13200 if(IsCorruptionDetectionEnabled())
13201 {
13202 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13203 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13204 }
13205
13206 if(hAllocation->IsPersistentMap())
13207 {
13208 pBlock->Unmap(m_hAllocator, 1);
13209 }
13210
13211 pBlock->m_pMetadata->Free(hAllocation);
13212 VMA_HEAVY_ASSERT(pBlock->Validate());
13213
13214 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13215 IncrementallySortBlocks();
13216 }
13217
CalcMaxBlockSize()13218 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13219 {
13220 VkDeviceSize result = 0;
13221 for(size_t i = m_Blocks.size(); i--; )
13222 {
13223 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13224 if(result >= m_PreferredBlockSize)
13225 {
13226 break;
13227 }
13228 }
13229 return result;
13230 }
13231
Remove(VmaDeviceMemoryBlock * pBlock)13232 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13233 {
13234 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13235 {
13236 if(m_Blocks[blockIndex] == pBlock)
13237 {
13238 VmaVectorRemove(m_Blocks, blockIndex);
13239 return;
13240 }
13241 }
13242 VMA_ASSERT(0);
13243 }
13244
IncrementallySortBlocks()13245 void VmaBlockVector::IncrementallySortBlocks()
13246 {
13247 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13248 {
13249 // Bubble sort only until first swap.
13250 for(size_t i = 1; i < m_Blocks.size(); ++i)
13251 {
13252 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13253 {
13254 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13255 return;
13256 }
13257 }
13258 }
13259 }
13260
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13261 VkResult VmaBlockVector::AllocateFromBlock(
13262 VmaDeviceMemoryBlock* pBlock,
13263 uint32_t currentFrameIndex,
13264 VkDeviceSize size,
13265 VkDeviceSize alignment,
13266 VmaAllocationCreateFlags allocFlags,
13267 void* pUserData,
13268 VmaSuballocationType suballocType,
13269 uint32_t strategy,
13270 VmaAllocation* pAllocation)
13271 {
13272 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13273 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13274 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13275 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13276
13277 VmaAllocationRequest currRequest = {};
13278 if(pBlock->m_pMetadata->CreateAllocationRequest(
13279 currentFrameIndex,
13280 m_FrameInUseCount,
13281 m_BufferImageGranularity,
13282 size,
13283 alignment,
13284 isUpperAddress,
13285 suballocType,
13286 false, // canMakeOtherLost
13287 strategy,
13288 &currRequest))
13289 {
13290 // Allocate from pCurrBlock.
13291 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13292
13293 if(mapped)
13294 {
13295 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13296 if(res != VK_SUCCESS)
13297 {
13298 return res;
13299 }
13300 }
13301
13302 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13303 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13304 UpdateHasEmptyBlock();
13305 (*pAllocation)->InitBlockAllocation(
13306 pBlock,
13307 currRequest.offset,
13308 alignment,
13309 size,
13310 m_MemoryTypeIndex,
13311 suballocType,
13312 mapped,
13313 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13314 IsNewBlockFlag());
13315 ClearNewBlockFlag();
13316 VMA_HEAVY_ASSERT(pBlock->Validate());
13317 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13318 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13319 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13320 {
13321 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13322 }
13323 if(IsCorruptionDetectionEnabled())
13324 {
13325 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13326 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13327 }
13328 return VK_SUCCESS;
13329 }
13330 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13331 }
13332
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13333 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13334 {
13335 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13336 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13337 allocInfo.allocationSize = blockSize;
13338
13339 #if VMA_BUFFER_DEVICE_ADDRESS
13340 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13341 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13342 if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13343 {
13344 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13345 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13346 }
13347 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13348
13349 VkDeviceMemory mem = VK_NULL_HANDLE;
13350 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13351 if(res < 0)
13352 {
13353 return res;
13354 }
13355
13356 // New VkDeviceMemory successfully created.
13357
13358 // Create new Allocation for it.
13359 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13360 pBlock->Init(
13361 m_hAllocator,
13362 m_hParentPool,
13363 m_MemoryTypeIndex,
13364 mem,
13365 allocInfo.allocationSize,
13366 m_NextBlockId++,
13367 m_Algorithm);
13368 m_NewBlockFlag = true;
13369
13370 m_Blocks.push_back(pBlock);
13371 if(pNewBlockIndex != VMA_NULL)
13372 {
13373 *pNewBlockIndex = m_Blocks.size() - 1;
13374 }
13375
13376 return VK_SUCCESS;
13377 }
13378
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13379 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13380 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13381 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13382 {
13383 const size_t blockCount = m_Blocks.size();
13384 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13385
13386 enum BLOCK_FLAG
13387 {
13388 BLOCK_FLAG_USED = 0x00000001,
13389 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13390 };
13391
13392 struct BlockInfo
13393 {
13394 uint32_t flags;
13395 void* pMappedData;
13396 };
13397 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13398 blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13399 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13400
13401 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13402 const size_t moveCount = moves.size();
13403 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13404 {
13405 const VmaDefragmentationMove& move = moves[moveIndex];
13406 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13407 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13408 }
13409
13410 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13411
13412 // Go over all blocks. Get mapped pointer or map if necessary.
13413 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13414 {
13415 BlockInfo& currBlockInfo = blockInfo[blockIndex];
13416 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13417 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13418 {
13419 currBlockInfo.pMappedData = pBlock->GetMappedData();
13420 // It is not originally mapped - map it.
13421 if(currBlockInfo.pMappedData == VMA_NULL)
13422 {
13423 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13424 if(pDefragCtx->res == VK_SUCCESS)
13425 {
13426 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13427 }
13428 }
13429 }
13430 }
13431
13432 // Go over all moves. Do actual data transfer.
13433 if(pDefragCtx->res == VK_SUCCESS)
13434 {
13435 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13436 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13437
13438 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13439 {
13440 const VmaDefragmentationMove& move = moves[moveIndex];
13441
13442 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13443 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13444
13445 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13446
13447 // Invalidate source.
13448 if(isNonCoherent)
13449 {
13450 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13451 memRange.memory = pSrcBlock->GetDeviceMemory();
13452 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13453 memRange.size = VMA_MIN(
13454 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13455 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13456 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13457 }
13458
13459 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13460 memmove(
13461 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13462 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13463 static_cast<size_t>(move.size));
13464
13465 if(IsCorruptionDetectionEnabled())
13466 {
13467 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13468 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13469 }
13470
13471 // Flush destination.
13472 if(isNonCoherent)
13473 {
13474 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13475 memRange.memory = pDstBlock->GetDeviceMemory();
13476 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13477 memRange.size = VMA_MIN(
13478 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13479 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13480 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13481 }
13482 }
13483 }
13484
13485 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13486 // Regardless of pCtx->res == VK_SUCCESS.
13487 for(size_t blockIndex = blockCount; blockIndex--; )
13488 {
13489 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13490 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13491 {
13492 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13493 pBlock->Unmap(m_hAllocator, 1);
13494 }
13495 }
13496 }
13497
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13498 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13499 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13500 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13501 VkCommandBuffer commandBuffer)
13502 {
13503 const size_t blockCount = m_Blocks.size();
13504
13505 pDefragCtx->blockContexts.resize(blockCount);
13506 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13507
13508 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13509 const size_t moveCount = moves.size();
13510 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13511 {
13512 const VmaDefragmentationMove& move = moves[moveIndex];
13513
13514 //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13515 {
13516 // Old school move still require us to map the whole block
13517 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13518 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13519 }
13520 }
13521
13522 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13523
13524 // Go over all blocks. Create and bind buffer for whole block if necessary.
13525 {
13526 VkBufferCreateInfo bufCreateInfo;
13527 VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13528
13529 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13530 {
13531 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13532 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13533 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13534 {
13535 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13536 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13537 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13538 if(pDefragCtx->res == VK_SUCCESS)
13539 {
13540 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13541 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13542 }
13543 }
13544 }
13545 }
13546
13547 // Go over all moves. Post data transfer commands to command buffer.
13548 if(pDefragCtx->res == VK_SUCCESS)
13549 {
13550 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13551 {
13552 const VmaDefragmentationMove& move = moves[moveIndex];
13553
13554 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13555 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13556
13557 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13558
13559 VkBufferCopy region = {
13560 move.srcOffset,
13561 move.dstOffset,
13562 move.size };
13563 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13564 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion);
13565 }
13566 }
13567
13568 // Save buffers to defrag context for later destruction.
13569 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13570 {
13571 pDefragCtx->res = VK_NOT_READY;
13572 }
13573 }
13574
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13575 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13576 {
13577 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13578 {
13579 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13580 if(pBlock->m_pMetadata->IsEmpty())
13581 {
13582 if(m_Blocks.size() > m_MinBlockCount)
13583 {
13584 if(pDefragmentationStats != VMA_NULL)
13585 {
13586 ++pDefragmentationStats->deviceMemoryBlocksFreed;
13587 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13588 }
13589
13590 VmaVectorRemove(m_Blocks, blockIndex);
13591 pBlock->Destroy(m_hAllocator);
13592 vma_delete(m_hAllocator, pBlock);
13593 }
13594 else
13595 {
13596 break;
13597 }
13598 }
13599 }
13600 UpdateHasEmptyBlock();
13601 }
13602
UpdateHasEmptyBlock()13603 void VmaBlockVector::UpdateHasEmptyBlock()
13604 {
13605 m_HasEmptyBlock = false;
13606 for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13607 {
13608 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13609 if(pBlock->m_pMetadata->IsEmpty())
13610 {
13611 m_HasEmptyBlock = true;
13612 break;
13613 }
13614 }
13615 }
13616
13617 #if VMA_STATS_STRING_ENABLED
13618
PrintDetailedMap(class VmaJsonWriter & json)13619 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13620 {
13621 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13622
13623 json.BeginObject();
13624
13625 if(IsCustomPool())
13626 {
13627 const char* poolName = m_hParentPool->GetName();
13628 if(poolName != VMA_NULL && poolName[0] != '\0')
13629 {
13630 json.WriteString("Name");
13631 json.WriteString(poolName);
13632 }
13633
13634 json.WriteString("MemoryTypeIndex");
13635 json.WriteNumber(m_MemoryTypeIndex);
13636
13637 json.WriteString("BlockSize");
13638 json.WriteNumber(m_PreferredBlockSize);
13639
13640 json.WriteString("BlockCount");
13641 json.BeginObject(true);
13642 if(m_MinBlockCount > 0)
13643 {
13644 json.WriteString("Min");
13645 json.WriteNumber((uint64_t)m_MinBlockCount);
13646 }
13647 if(m_MaxBlockCount < SIZE_MAX)
13648 {
13649 json.WriteString("Max");
13650 json.WriteNumber((uint64_t)m_MaxBlockCount);
13651 }
13652 json.WriteString("Cur");
13653 json.WriteNumber((uint64_t)m_Blocks.size());
13654 json.EndObject();
13655
13656 if(m_FrameInUseCount > 0)
13657 {
13658 json.WriteString("FrameInUseCount");
13659 json.WriteNumber(m_FrameInUseCount);
13660 }
13661
13662 if(m_Algorithm != 0)
13663 {
13664 json.WriteString("Algorithm");
13665 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13666 }
13667 }
13668 else
13669 {
13670 json.WriteString("PreferredBlockSize");
13671 json.WriteNumber(m_PreferredBlockSize);
13672 }
13673
13674 json.WriteString("Blocks");
13675 json.BeginObject();
13676 for(size_t i = 0; i < m_Blocks.size(); ++i)
13677 {
13678 json.BeginString();
13679 json.ContinueString(m_Blocks[i]->GetId());
13680 json.EndString();
13681
13682 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13683 }
13684 json.EndObject();
13685
13686 json.EndObject();
13687 }
13688
13689 #endif // #if VMA_STATS_STRING_ENABLED
13690
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13691 void VmaBlockVector::Defragment(
13692 class VmaBlockVectorDefragmentationContext* pCtx,
13693 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13694 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13695 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13696 VkCommandBuffer commandBuffer)
13697 {
13698 pCtx->res = VK_SUCCESS;
13699
13700 const VkMemoryPropertyFlags memPropFlags =
13701 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13702 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13703
13704 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13705 isHostVisible;
13706 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13707 !IsCorruptionDetectionEnabled() &&
13708 ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13709
13710 // There are options to defragment this memory type.
13711 if(canDefragmentOnCpu || canDefragmentOnGpu)
13712 {
13713 bool defragmentOnGpu;
13714 // There is only one option to defragment this memory type.
13715 if(canDefragmentOnGpu != canDefragmentOnCpu)
13716 {
13717 defragmentOnGpu = canDefragmentOnGpu;
13718 }
13719 // Both options are available: Heuristics to choose the best one.
13720 else
13721 {
13722 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13723 m_hAllocator->IsIntegratedGpu();
13724 }
13725
13726 bool overlappingMoveSupported = !defragmentOnGpu;
13727
13728 if(m_hAllocator->m_UseMutex)
13729 {
13730 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13731 {
13732 if(!m_Mutex.TryLockWrite())
13733 {
13734 pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13735 return;
13736 }
13737 }
13738 else
13739 {
13740 m_Mutex.LockWrite();
13741 pCtx->mutexLocked = true;
13742 }
13743 }
13744
13745 pCtx->Begin(overlappingMoveSupported, flags);
13746
13747 // Defragment.
13748
13749 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13750 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13751 pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13752
13753 // Accumulate statistics.
13754 if(pStats != VMA_NULL)
13755 {
13756 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13757 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13758 pStats->bytesMoved += bytesMoved;
13759 pStats->allocationsMoved += allocationsMoved;
13760 VMA_ASSERT(bytesMoved <= maxBytesToMove);
13761 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13762 if(defragmentOnGpu)
13763 {
13764 maxGpuBytesToMove -= bytesMoved;
13765 maxGpuAllocationsToMove -= allocationsMoved;
13766 }
13767 else
13768 {
13769 maxCpuBytesToMove -= bytesMoved;
13770 maxCpuAllocationsToMove -= allocationsMoved;
13771 }
13772 }
13773
13774 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13775 {
13776 if(m_hAllocator->m_UseMutex)
13777 m_Mutex.UnlockWrite();
13778
13779 if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13780 pCtx->res = VK_NOT_READY;
13781
13782 return;
13783 }
13784
13785 if(pCtx->res >= VK_SUCCESS)
13786 {
13787 if(defragmentOnGpu)
13788 {
13789 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13790 }
13791 else
13792 {
13793 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13794 }
13795 }
13796 }
13797 }
13798
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13799 void VmaBlockVector::DefragmentationEnd(
13800 class VmaBlockVectorDefragmentationContext* pCtx,
13801 uint32_t flags,
13802 VmaDefragmentationStats* pStats)
13803 {
13804 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13805 {
13806 VMA_ASSERT(pCtx->mutexLocked == false);
13807
13808 // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13809 // lock protecting us. Since we mutate state here, we have to take the lock out now
13810 m_Mutex.LockWrite();
13811 pCtx->mutexLocked = true;
13812 }
13813
13814 // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13815 if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13816 {
13817 // Destroy buffers.
13818 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13819 {
13820 VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13821 if(blockCtx.hBuffer)
13822 {
13823 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13824 }
13825 }
13826
13827 if(pCtx->res >= VK_SUCCESS)
13828 {
13829 FreeEmptyBlocks(pStats);
13830 }
13831 }
13832
13833 if(pCtx->mutexLocked)
13834 {
13835 VMA_ASSERT(m_hAllocator->m_UseMutex);
13836 m_Mutex.UnlockWrite();
13837 }
13838 }
13839
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13840 uint32_t VmaBlockVector::ProcessDefragmentations(
13841 class VmaBlockVectorDefragmentationContext *pCtx,
13842 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13843 {
13844 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13845
13846 const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13847
13848 for(uint32_t i = 0; i < moveCount; ++ i)
13849 {
13850 VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13851
13852 pMove->allocation = move.hAllocation;
13853 pMove->memory = move.pDstBlock->GetDeviceMemory();
13854 pMove->offset = move.dstOffset;
13855
13856 ++ pMove;
13857 }
13858
13859 pCtx->defragmentationMovesProcessed += moveCount;
13860
13861 return moveCount;
13862 }
13863
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13864 void VmaBlockVector::CommitDefragmentations(
13865 class VmaBlockVectorDefragmentationContext *pCtx,
13866 VmaDefragmentationStats* pStats)
13867 {
13868 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13869
13870 for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13871 {
13872 const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13873
13874 move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13875 move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13876 }
13877
13878 pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13879 FreeEmptyBlocks(pStats);
13880 }
13881
CalcAllocationCount()13882 size_t VmaBlockVector::CalcAllocationCount() const
13883 {
13884 size_t result = 0;
13885 for(size_t i = 0; i < m_Blocks.size(); ++i)
13886 {
13887 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13888 }
13889 return result;
13890 }
13891
IsBufferImageGranularityConflictPossible()13892 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13893 {
13894 if(m_BufferImageGranularity == 1)
13895 {
13896 return false;
13897 }
13898 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13899 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13900 {
13901 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13902 VMA_ASSERT(m_Algorithm == 0);
13903 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13904 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13905 {
13906 return true;
13907 }
13908 }
13909 return false;
13910 }
13911
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13912 void VmaBlockVector::MakePoolAllocationsLost(
13913 uint32_t currentFrameIndex,
13914 size_t* pLostAllocationCount)
13915 {
13916 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13917 size_t lostAllocationCount = 0;
13918 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13919 {
13920 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13921 VMA_ASSERT(pBlock);
13922 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13923 }
13924 if(pLostAllocationCount != VMA_NULL)
13925 {
13926 *pLostAllocationCount = lostAllocationCount;
13927 }
13928 }
13929
CheckCorruption()13930 VkResult VmaBlockVector::CheckCorruption()
13931 {
13932 if(!IsCorruptionDetectionEnabled())
13933 {
13934 return VK_ERROR_FEATURE_NOT_PRESENT;
13935 }
13936
13937 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13938 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13939 {
13940 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13941 VMA_ASSERT(pBlock);
13942 VkResult res = pBlock->CheckCorruption(m_hAllocator);
13943 if(res != VK_SUCCESS)
13944 {
13945 return res;
13946 }
13947 }
13948 return VK_SUCCESS;
13949 }
13950
AddStats(VmaStats * pStats)13951 void VmaBlockVector::AddStats(VmaStats* pStats)
13952 {
13953 const uint32_t memTypeIndex = m_MemoryTypeIndex;
13954 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13955
13956 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13957
13958 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13959 {
13960 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13961 VMA_ASSERT(pBlock);
13962 VMA_HEAVY_ASSERT(pBlock->Validate());
13963 VmaStatInfo allocationStatInfo;
13964 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13965 VmaAddStatInfo(pStats->total, allocationStatInfo);
13966 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13967 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13968 }
13969 }
13970
FreeEmptyBlock()13971 void VmaBlockVector::FreeEmptyBlock()
13972 {
13973 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13974 FreeEmptyBlocks(VMA_NULL);
13975 }
13976
13977 ////////////////////////////////////////////////////////////////////////////////
13978 // VmaDefragmentationAlgorithm_Generic members definition
13979
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13980 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13981 VmaAllocator hAllocator,
13982 VmaBlockVector* pBlockVector,
13983 uint32_t currentFrameIndex,
13984 bool overlappingMoveSupported) :
13985 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13986 m_AllocationCount(0),
13987 m_AllAllocations(false),
13988 m_BytesMoved(0),
13989 m_AllocationsMoved(0),
13990 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13991 {
13992 // Create block info for each block.
13993 const size_t blockCount = m_pBlockVector->m_Blocks.size();
13994 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13995 {
13996 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13997 pBlockInfo->m_OriginalBlockIndex = blockIndex;
13998 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13999 m_Blocks.push_back(pBlockInfo);
14000 }
14001
14002 // Sort them by m_pBlock pointer value.
14003 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
14004 }
14005
~VmaDefragmentationAlgorithm_Generic()14006 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14007 {
14008 for(size_t i = m_Blocks.size(); i--; )
14009 {
14010 vma_delete(m_hAllocator, m_Blocks[i]);
14011 }
14012 }
14013
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14014 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14015 {
14016 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14017 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14018 {
14019 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14020 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14021 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14022 {
14023 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14024 (*it)->m_Allocations.push_back(allocInfo);
14025 }
14026 else
14027 {
14028 VMA_ASSERT(0);
14029 }
14030
14031 ++m_AllocationCount;
14032 }
14033 }
14034
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)14035 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14036 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14037 VkDeviceSize maxBytesToMove,
14038 uint32_t maxAllocationsToMove,
14039 bool freeOldAllocations)
14040 {
14041 if(m_Blocks.empty())
14042 {
14043 return VK_SUCCESS;
14044 }
14045
14046 // This is a choice based on research.
14047 // Option 1:
14048 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14049 // Option 2:
14050 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14051 // Option 3:
14052 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14053
14054 size_t srcBlockMinIndex = 0;
14055 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14056 /*
14057 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14058 {
14059 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14060 if(blocksWithNonMovableCount > 0)
14061 {
14062 srcBlockMinIndex = blocksWithNonMovableCount - 1;
14063 }
14064 }
14065 */
14066
14067 size_t srcBlockIndex = m_Blocks.size() - 1;
14068 size_t srcAllocIndex = SIZE_MAX;
14069 for(;;)
14070 {
14071 // 1. Find next allocation to move.
14072 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14073 // 1.2. Then start from last to first m_Allocations.
14074 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14075 {
14076 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14077 {
14078 // Finished: no more allocations to process.
14079 if(srcBlockIndex == srcBlockMinIndex)
14080 {
14081 return VK_SUCCESS;
14082 }
14083 else
14084 {
14085 --srcBlockIndex;
14086 srcAllocIndex = SIZE_MAX;
14087 }
14088 }
14089 else
14090 {
14091 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14092 }
14093 }
14094
14095 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14096 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14097
14098 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14099 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14100 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14101 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14102
14103 // 2. Try to find new place for this allocation in preceding or current block.
14104 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14105 {
14106 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14107 VmaAllocationRequest dstAllocRequest;
14108 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14109 m_CurrentFrameIndex,
14110 m_pBlockVector->GetFrameInUseCount(),
14111 m_pBlockVector->GetBufferImageGranularity(),
14112 size,
14113 alignment,
14114 false, // upperAddress
14115 suballocType,
14116 false, // canMakeOtherLost
14117 strategy,
14118 &dstAllocRequest) &&
14119 MoveMakesSense(
14120 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14121 {
14122 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14123
14124 // Reached limit on number of allocations or bytes to move.
14125 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14126 (m_BytesMoved + size > maxBytesToMove))
14127 {
14128 return VK_SUCCESS;
14129 }
14130
14131 VmaDefragmentationMove move = {};
14132 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14133 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14134 move.srcOffset = srcOffset;
14135 move.dstOffset = dstAllocRequest.offset;
14136 move.size = size;
14137 move.hAllocation = allocInfo.m_hAllocation;
14138 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14139 move.pDstBlock = pDstBlockInfo->m_pBlock;
14140
14141 moves.push_back(move);
14142
14143 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14144 dstAllocRequest,
14145 suballocType,
14146 size,
14147 allocInfo.m_hAllocation);
14148
14149 if(freeOldAllocations)
14150 {
14151 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14152 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14153 }
14154
14155 if(allocInfo.m_pChanged != VMA_NULL)
14156 {
14157 *allocInfo.m_pChanged = VK_TRUE;
14158 }
14159
14160 ++m_AllocationsMoved;
14161 m_BytesMoved += size;
14162
14163 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14164
14165 break;
14166 }
14167 }
14168
14169 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14170
14171 if(srcAllocIndex > 0)
14172 {
14173 --srcAllocIndex;
14174 }
14175 else
14176 {
14177 if(srcBlockIndex > 0)
14178 {
14179 --srcBlockIndex;
14180 srcAllocIndex = SIZE_MAX;
14181 }
14182 else
14183 {
14184 return VK_SUCCESS;
14185 }
14186 }
14187 }
14188 }
14189
CalcBlocksWithNonMovableCount()14190 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14191 {
14192 size_t result = 0;
14193 for(size_t i = 0; i < m_Blocks.size(); ++i)
14194 {
14195 if(m_Blocks[i]->m_HasNonMovableAllocations)
14196 {
14197 ++result;
14198 }
14199 }
14200 return result;
14201 }
14202
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14203 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14204 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14205 VkDeviceSize maxBytesToMove,
14206 uint32_t maxAllocationsToMove,
14207 VmaDefragmentationFlags flags)
14208 {
14209 if(!m_AllAllocations && m_AllocationCount == 0)
14210 {
14211 return VK_SUCCESS;
14212 }
14213
14214 const size_t blockCount = m_Blocks.size();
14215 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14216 {
14217 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14218
14219 if(m_AllAllocations)
14220 {
14221 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14222 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14223 it != pMetadata->m_Suballocations.end();
14224 ++it)
14225 {
14226 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14227 {
14228 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14229 pBlockInfo->m_Allocations.push_back(allocInfo);
14230 }
14231 }
14232 }
14233
14234 pBlockInfo->CalcHasNonMovableAllocations();
14235
14236 // This is a choice based on research.
14237 // Option 1:
14238 pBlockInfo->SortAllocationsByOffsetDescending();
14239 // Option 2:
14240 //pBlockInfo->SortAllocationsBySizeDescending();
14241 }
14242
14243 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14244 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14245
14246 // This is a choice based on research.
14247 const uint32_t roundCount = 2;
14248
14249 // Execute defragmentation rounds (the main part).
14250 VkResult result = VK_SUCCESS;
14251 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14252 {
14253 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14254 }
14255
14256 return result;
14257 }
14258
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14259 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14260 size_t dstBlockIndex, VkDeviceSize dstOffset,
14261 size_t srcBlockIndex, VkDeviceSize srcOffset)
14262 {
14263 if(dstBlockIndex < srcBlockIndex)
14264 {
14265 return true;
14266 }
14267 if(dstBlockIndex > srcBlockIndex)
14268 {
14269 return false;
14270 }
14271 if(dstOffset < srcOffset)
14272 {
14273 return true;
14274 }
14275 return false;
14276 }
14277
14278 ////////////////////////////////////////////////////////////////////////////////
14279 // VmaDefragmentationAlgorithm_Fast
14280
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14281 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14282 VmaAllocator hAllocator,
14283 VmaBlockVector* pBlockVector,
14284 uint32_t currentFrameIndex,
14285 bool overlappingMoveSupported) :
14286 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14287 m_OverlappingMoveSupported(overlappingMoveSupported),
14288 m_AllocationCount(0),
14289 m_AllAllocations(false),
14290 m_BytesMoved(0),
14291 m_AllocationsMoved(0),
14292 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14293 {
14294 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14295
14296 }
14297
~VmaDefragmentationAlgorithm_Fast()14298 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14299 {
14300 }
14301
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14302 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14303 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14304 VkDeviceSize maxBytesToMove,
14305 uint32_t maxAllocationsToMove,
14306 VmaDefragmentationFlags flags)
14307 {
14308 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14309
14310 const size_t blockCount = m_pBlockVector->GetBlockCount();
14311 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14312 {
14313 return VK_SUCCESS;
14314 }
14315
14316 PreprocessMetadata();
14317
14318 // Sort blocks in order from most destination.
14319
14320 m_BlockInfos.resize(blockCount);
14321 for(size_t i = 0; i < blockCount; ++i)
14322 {
14323 m_BlockInfos[i].origBlockIndex = i;
14324 }
14325
14326 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14327 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14328 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14329 });
14330
14331 // THE MAIN ALGORITHM
14332
14333 FreeSpaceDatabase freeSpaceDb;
14334
14335 size_t dstBlockInfoIndex = 0;
14336 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14337 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14338 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14339 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14340 VkDeviceSize dstOffset = 0;
14341
14342 bool end = false;
14343 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14344 {
14345 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14346 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14347 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14348 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14349 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14350 {
14351 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14352 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14353 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14354 if(m_AllocationsMoved == maxAllocationsToMove ||
14355 m_BytesMoved + srcAllocSize > maxBytesToMove)
14356 {
14357 end = true;
14358 break;
14359 }
14360 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14361
14362 VmaDefragmentationMove move = {};
14363 // Try to place it in one of free spaces from the database.
14364 size_t freeSpaceInfoIndex;
14365 VkDeviceSize dstAllocOffset;
14366 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14367 freeSpaceInfoIndex, dstAllocOffset))
14368 {
14369 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14370 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14371 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14372
14373 // Same block
14374 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14375 {
14376 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14377
14378 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14379
14380 VmaSuballocation suballoc = *srcSuballocIt;
14381 suballoc.offset = dstAllocOffset;
14382 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14383 m_BytesMoved += srcAllocSize;
14384 ++m_AllocationsMoved;
14385
14386 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14387 ++nextSuballocIt;
14388 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14389 srcSuballocIt = nextSuballocIt;
14390
14391 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14392
14393 move.srcBlockIndex = srcOrigBlockIndex;
14394 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14395 move.srcOffset = srcAllocOffset;
14396 move.dstOffset = dstAllocOffset;
14397 move.size = srcAllocSize;
14398
14399 moves.push_back(move);
14400 }
14401 // Different block
14402 else
14403 {
14404 // MOVE OPTION 2: Move the allocation to a different block.
14405
14406 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14407
14408 VmaSuballocation suballoc = *srcSuballocIt;
14409 suballoc.offset = dstAllocOffset;
14410 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14411 m_BytesMoved += srcAllocSize;
14412 ++m_AllocationsMoved;
14413
14414 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14415 ++nextSuballocIt;
14416 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14417 srcSuballocIt = nextSuballocIt;
14418
14419 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14420
14421 move.srcBlockIndex = srcOrigBlockIndex;
14422 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14423 move.srcOffset = srcAllocOffset;
14424 move.dstOffset = dstAllocOffset;
14425 move.size = srcAllocSize;
14426
14427 moves.push_back(move);
14428 }
14429 }
14430 else
14431 {
14432 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14433
14434 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14435 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14436 dstAllocOffset + srcAllocSize > dstBlockSize)
14437 {
14438 // But before that, register remaining free space at the end of dst block.
14439 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14440
14441 ++dstBlockInfoIndex;
14442 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14443 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14444 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14445 dstBlockSize = pDstMetadata->GetSize();
14446 dstOffset = 0;
14447 dstAllocOffset = 0;
14448 }
14449
14450 // Same block
14451 if(dstBlockInfoIndex == srcBlockInfoIndex)
14452 {
14453 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14454
14455 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14456
14457 bool skipOver = overlap;
14458 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14459 {
14460 // If destination and source place overlap, skip if it would move it
14461 // by only < 1/64 of its size.
14462 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14463 }
14464
14465 if(skipOver)
14466 {
14467 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14468
14469 dstOffset = srcAllocOffset + srcAllocSize;
14470 ++srcSuballocIt;
14471 }
14472 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14473 else
14474 {
14475 srcSuballocIt->offset = dstAllocOffset;
14476 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14477 dstOffset = dstAllocOffset + srcAllocSize;
14478 m_BytesMoved += srcAllocSize;
14479 ++m_AllocationsMoved;
14480 ++srcSuballocIt;
14481
14482 move.srcBlockIndex = srcOrigBlockIndex;
14483 move.dstBlockIndex = dstOrigBlockIndex;
14484 move.srcOffset = srcAllocOffset;
14485 move.dstOffset = dstAllocOffset;
14486 move.size = srcAllocSize;
14487
14488 moves.push_back(move);
14489 }
14490 }
14491 // Different block
14492 else
14493 {
14494 // MOVE OPTION 2: Move the allocation to a different block.
14495
14496 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14497 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14498
14499 VmaSuballocation suballoc = *srcSuballocIt;
14500 suballoc.offset = dstAllocOffset;
14501 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14502 dstOffset = dstAllocOffset + srcAllocSize;
14503 m_BytesMoved += srcAllocSize;
14504 ++m_AllocationsMoved;
14505
14506 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14507 ++nextSuballocIt;
14508 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14509 srcSuballocIt = nextSuballocIt;
14510
14511 pDstMetadata->m_Suballocations.push_back(suballoc);
14512
14513 move.srcBlockIndex = srcOrigBlockIndex;
14514 move.dstBlockIndex = dstOrigBlockIndex;
14515 move.srcOffset = srcAllocOffset;
14516 move.dstOffset = dstAllocOffset;
14517 move.size = srcAllocSize;
14518
14519 moves.push_back(move);
14520 }
14521 }
14522 }
14523 }
14524
14525 m_BlockInfos.clear();
14526
14527 PostprocessMetadata();
14528
14529 return VK_SUCCESS;
14530 }
14531
PreprocessMetadata()14532 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14533 {
14534 const size_t blockCount = m_pBlockVector->GetBlockCount();
14535 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14536 {
14537 VmaBlockMetadata_Generic* const pMetadata =
14538 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14539 pMetadata->m_FreeCount = 0;
14540 pMetadata->m_SumFreeSize = pMetadata->GetSize();
14541 pMetadata->m_FreeSuballocationsBySize.clear();
14542 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14543 it != pMetadata->m_Suballocations.end(); )
14544 {
14545 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14546 {
14547 VmaSuballocationList::iterator nextIt = it;
14548 ++nextIt;
14549 pMetadata->m_Suballocations.erase(it);
14550 it = nextIt;
14551 }
14552 else
14553 {
14554 ++it;
14555 }
14556 }
14557 }
14558 }
14559
PostprocessMetadata()14560 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14561 {
14562 const size_t blockCount = m_pBlockVector->GetBlockCount();
14563 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14564 {
14565 VmaBlockMetadata_Generic* const pMetadata =
14566 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14567 const VkDeviceSize blockSize = pMetadata->GetSize();
14568
14569 // No allocations in this block - entire area is free.
14570 if(pMetadata->m_Suballocations.empty())
14571 {
14572 pMetadata->m_FreeCount = 1;
14573 //pMetadata->m_SumFreeSize is already set to blockSize.
14574 VmaSuballocation suballoc = {
14575 0, // offset
14576 blockSize, // size
14577 VMA_NULL, // hAllocation
14578 VMA_SUBALLOCATION_TYPE_FREE };
14579 pMetadata->m_Suballocations.push_back(suballoc);
14580 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14581 }
14582 // There are some allocations in this block.
14583 else
14584 {
14585 VkDeviceSize offset = 0;
14586 VmaSuballocationList::iterator it;
14587 for(it = pMetadata->m_Suballocations.begin();
14588 it != pMetadata->m_Suballocations.end();
14589 ++it)
14590 {
14591 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14592 VMA_ASSERT(it->offset >= offset);
14593
14594 // Need to insert preceding free space.
14595 if(it->offset > offset)
14596 {
14597 ++pMetadata->m_FreeCount;
14598 const VkDeviceSize freeSize = it->offset - offset;
14599 VmaSuballocation suballoc = {
14600 offset, // offset
14601 freeSize, // size
14602 VMA_NULL, // hAllocation
14603 VMA_SUBALLOCATION_TYPE_FREE };
14604 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14605 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14606 {
14607 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14608 }
14609 }
14610
14611 pMetadata->m_SumFreeSize -= it->size;
14612 offset = it->offset + it->size;
14613 }
14614
14615 // Need to insert trailing free space.
14616 if(offset < blockSize)
14617 {
14618 ++pMetadata->m_FreeCount;
14619 const VkDeviceSize freeSize = blockSize - offset;
14620 VmaSuballocation suballoc = {
14621 offset, // offset
14622 freeSize, // size
14623 VMA_NULL, // hAllocation
14624 VMA_SUBALLOCATION_TYPE_FREE };
14625 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14626 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14627 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14628 {
14629 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14630 }
14631 }
14632
14633 VMA_SORT(
14634 pMetadata->m_FreeSuballocationsBySize.begin(),
14635 pMetadata->m_FreeSuballocationsBySize.end(),
14636 VmaSuballocationItemSizeLess());
14637 }
14638
14639 VMA_HEAVY_ASSERT(pMetadata->Validate());
14640 }
14641 }
14642
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14643 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14644 {
14645 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14646 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14647 while(it != pMetadata->m_Suballocations.end())
14648 {
14649 if(it->offset < suballoc.offset)
14650 {
14651 ++it;
14652 }
14653 }
14654 pMetadata->m_Suballocations.insert(it, suballoc);
14655 }
14656
14657 ////////////////////////////////////////////////////////////////////////////////
14658 // VmaBlockVectorDefragmentationContext
14659
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14660 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14661 VmaAllocator hAllocator,
14662 VmaPool hCustomPool,
14663 VmaBlockVector* pBlockVector,
14664 uint32_t currFrameIndex) :
14665 res(VK_SUCCESS),
14666 mutexLocked(false),
14667 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14668 defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14669 defragmentationMovesProcessed(0),
14670 defragmentationMovesCommitted(0),
14671 hasDefragmentationPlan(0),
14672 m_hAllocator(hAllocator),
14673 m_hCustomPool(hCustomPool),
14674 m_pBlockVector(pBlockVector),
14675 m_CurrFrameIndex(currFrameIndex),
14676 m_pAlgorithm(VMA_NULL),
14677 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14678 m_AllAllocations(false)
14679 {
14680 }
14681
~VmaBlockVectorDefragmentationContext()14682 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14683 {
14684 vma_delete(m_hAllocator, m_pAlgorithm);
14685 }
14686
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14687 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14688 {
14689 AllocInfo info = { hAlloc, pChanged };
14690 m_Allocations.push_back(info);
14691 }
14692
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14693 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14694 {
14695 const bool allAllocations = m_AllAllocations ||
14696 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14697
14698 /********************************
14699 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14700 ********************************/
14701
14702 /*
14703 Fast algorithm is supported only when certain criteria are met:
14704 - VMA_DEBUG_MARGIN is 0.
14705 - All allocations in this block vector are moveable.
14706 - There is no possibility of image/buffer granularity conflict.
14707 - The defragmentation is not incremental
14708 */
14709 if(VMA_DEBUG_MARGIN == 0 &&
14710 allAllocations &&
14711 !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14712 !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14713 {
14714 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14715 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14716 }
14717 else
14718 {
14719 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14720 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14721 }
14722
14723 if(allAllocations)
14724 {
14725 m_pAlgorithm->AddAll();
14726 }
14727 else
14728 {
14729 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14730 {
14731 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14732 }
14733 }
14734 }
14735
14736 ////////////////////////////////////////////////////////////////////////////////
14737 // VmaDefragmentationContext
14738
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14739 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14740 VmaAllocator hAllocator,
14741 uint32_t currFrameIndex,
14742 uint32_t flags,
14743 VmaDefragmentationStats* pStats) :
14744 m_hAllocator(hAllocator),
14745 m_CurrFrameIndex(currFrameIndex),
14746 m_Flags(flags),
14747 m_pStats(pStats),
14748 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14749 {
14750 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14751 }
14752
~VmaDefragmentationContext_T()14753 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14754 {
14755 for(size_t i = m_CustomPoolContexts.size(); i--; )
14756 {
14757 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14758 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14759 vma_delete(m_hAllocator, pBlockVectorCtx);
14760 }
14761 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14762 {
14763 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14764 if(pBlockVectorCtx)
14765 {
14766 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14767 vma_delete(m_hAllocator, pBlockVectorCtx);
14768 }
14769 }
14770 }
14771
AddPools(uint32_t poolCount,const VmaPool * pPools)14772 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14773 {
14774 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14775 {
14776 VmaPool pool = pPools[poolIndex];
14777 VMA_ASSERT(pool);
14778 // Pools with algorithm other than default are not defragmented.
14779 if(pool->m_BlockVector.GetAlgorithm() == 0)
14780 {
14781 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14782
14783 for(size_t i = m_CustomPoolContexts.size(); i--; )
14784 {
14785 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14786 {
14787 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14788 break;
14789 }
14790 }
14791
14792 if(!pBlockVectorDefragCtx)
14793 {
14794 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14795 m_hAllocator,
14796 pool,
14797 &pool->m_BlockVector,
14798 m_CurrFrameIndex);
14799 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14800 }
14801
14802 pBlockVectorDefragCtx->AddAll();
14803 }
14804 }
14805 }
14806
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14807 void VmaDefragmentationContext_T::AddAllocations(
14808 uint32_t allocationCount,
14809 const VmaAllocation* pAllocations,
14810 VkBool32* pAllocationsChanged)
14811 {
14812 // Dispatch pAllocations among defragmentators. Create them when necessary.
14813 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14814 {
14815 const VmaAllocation hAlloc = pAllocations[allocIndex];
14816 VMA_ASSERT(hAlloc);
14817 // DedicatedAlloc cannot be defragmented.
14818 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14819 // Lost allocation cannot be defragmented.
14820 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14821 {
14822 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14823
14824 const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14825 // This allocation belongs to custom pool.
14826 if(hAllocPool != VK_NULL_HANDLE)
14827 {
14828 // Pools with algorithm other than default are not defragmented.
14829 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14830 {
14831 for(size_t i = m_CustomPoolContexts.size(); i--; )
14832 {
14833 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14834 {
14835 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14836 break;
14837 }
14838 }
14839 if(!pBlockVectorDefragCtx)
14840 {
14841 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14842 m_hAllocator,
14843 hAllocPool,
14844 &hAllocPool->m_BlockVector,
14845 m_CurrFrameIndex);
14846 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14847 }
14848 }
14849 }
14850 // This allocation belongs to default pool.
14851 else
14852 {
14853 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14854 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14855 if(!pBlockVectorDefragCtx)
14856 {
14857 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14858 m_hAllocator,
14859 VMA_NULL, // hCustomPool
14860 m_hAllocator->m_pBlockVectors[memTypeIndex],
14861 m_CurrFrameIndex);
14862 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14863 }
14864 }
14865
14866 if(pBlockVectorDefragCtx)
14867 {
14868 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14869 &pAllocationsChanged[allocIndex] : VMA_NULL;
14870 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14871 }
14872 }
14873 }
14874 }
14875
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14876 VkResult VmaDefragmentationContext_T::Defragment(
14877 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14878 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14879 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14880 {
14881 if(pStats)
14882 {
14883 memset(pStats, 0, sizeof(VmaDefragmentationStats));
14884 }
14885
14886 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14887 {
14888 // For incremental defragmetnations, we just earmark how much we can move
14889 // The real meat is in the defragmentation steps
14890 m_MaxCpuBytesToMove = maxCpuBytesToMove;
14891 m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14892
14893 m_MaxGpuBytesToMove = maxGpuBytesToMove;
14894 m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14895
14896 if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14897 m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14898 return VK_SUCCESS;
14899
14900 return VK_NOT_READY;
14901 }
14902
14903 if(commandBuffer == VK_NULL_HANDLE)
14904 {
14905 maxGpuBytesToMove = 0;
14906 maxGpuAllocationsToMove = 0;
14907 }
14908
14909 VkResult res = VK_SUCCESS;
14910
14911 // Process default pools.
14912 for(uint32_t memTypeIndex = 0;
14913 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14914 ++memTypeIndex)
14915 {
14916 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14917 if(pBlockVectorCtx)
14918 {
14919 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14920 pBlockVectorCtx->GetBlockVector()->Defragment(
14921 pBlockVectorCtx,
14922 pStats, flags,
14923 maxCpuBytesToMove, maxCpuAllocationsToMove,
14924 maxGpuBytesToMove, maxGpuAllocationsToMove,
14925 commandBuffer);
14926 if(pBlockVectorCtx->res != VK_SUCCESS)
14927 {
14928 res = pBlockVectorCtx->res;
14929 }
14930 }
14931 }
14932
14933 // Process custom pools.
14934 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14935 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14936 ++customCtxIndex)
14937 {
14938 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14939 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14940 pBlockVectorCtx->GetBlockVector()->Defragment(
14941 pBlockVectorCtx,
14942 pStats, flags,
14943 maxCpuBytesToMove, maxCpuAllocationsToMove,
14944 maxGpuBytesToMove, maxGpuAllocationsToMove,
14945 commandBuffer);
14946 if(pBlockVectorCtx->res != VK_SUCCESS)
14947 {
14948 res = pBlockVectorCtx->res;
14949 }
14950 }
14951
14952 return res;
14953 }
14954
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14955 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14956 {
14957 VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14958 uint32_t movesLeft = pInfo->moveCount;
14959
14960 // Process default pools.
14961 for(uint32_t memTypeIndex = 0;
14962 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14963 ++memTypeIndex)
14964 {
14965 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14966 if(pBlockVectorCtx)
14967 {
14968 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14969
14970 if(!pBlockVectorCtx->hasDefragmentationPlan)
14971 {
14972 pBlockVectorCtx->GetBlockVector()->Defragment(
14973 pBlockVectorCtx,
14974 m_pStats, m_Flags,
14975 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14976 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14977 VK_NULL_HANDLE);
14978
14979 if(pBlockVectorCtx->res < VK_SUCCESS)
14980 continue;
14981
14982 pBlockVectorCtx->hasDefragmentationPlan = true;
14983 }
14984
14985 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14986 pBlockVectorCtx,
14987 pCurrentMove, movesLeft);
14988
14989 movesLeft -= processed;
14990 pCurrentMove += processed;
14991 }
14992 }
14993
14994 // Process custom pools.
14995 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14996 customCtxIndex < customCtxCount;
14997 ++customCtxIndex)
14998 {
14999 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15000 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15001
15002 if(!pBlockVectorCtx->hasDefragmentationPlan)
15003 {
15004 pBlockVectorCtx->GetBlockVector()->Defragment(
15005 pBlockVectorCtx,
15006 m_pStats, m_Flags,
15007 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15008 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15009 VK_NULL_HANDLE);
15010
15011 if(pBlockVectorCtx->res < VK_SUCCESS)
15012 continue;
15013
15014 pBlockVectorCtx->hasDefragmentationPlan = true;
15015 }
15016
15017 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15018 pBlockVectorCtx,
15019 pCurrentMove, movesLeft);
15020
15021 movesLeft -= processed;
15022 pCurrentMove += processed;
15023 }
15024
15025 pInfo->moveCount = pInfo->moveCount - movesLeft;
15026
15027 return VK_SUCCESS;
15028 }
DefragmentPassEnd()15029 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15030 {
15031 VkResult res = VK_SUCCESS;
15032
15033 // Process default pools.
15034 for(uint32_t memTypeIndex = 0;
15035 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15036 ++memTypeIndex)
15037 {
15038 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15039 if(pBlockVectorCtx)
15040 {
15041 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15042
15043 if(!pBlockVectorCtx->hasDefragmentationPlan)
15044 {
15045 res = VK_NOT_READY;
15046 continue;
15047 }
15048
15049 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15050 pBlockVectorCtx, m_pStats);
15051
15052 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15053 res = VK_NOT_READY;
15054 }
15055 }
15056
15057 // Process custom pools.
15058 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15059 customCtxIndex < customCtxCount;
15060 ++customCtxIndex)
15061 {
15062 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15063 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15064
15065 if(!pBlockVectorCtx->hasDefragmentationPlan)
15066 {
15067 res = VK_NOT_READY;
15068 continue;
15069 }
15070
15071 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15072 pBlockVectorCtx, m_pStats);
15073
15074 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15075 res = VK_NOT_READY;
15076 }
15077
15078 return res;
15079 }
15080
15081 ////////////////////////////////////////////////////////////////////////////////
15082 // VmaRecorder
15083
15084 #if VMA_RECORDING_ENABLED
15085
VmaRecorder()15086 VmaRecorder::VmaRecorder() :
15087 m_UseMutex(true),
15088 m_Flags(0),
15089 m_File(VMA_NULL),
15090 m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15091 {
15092 }
15093
Init(const VmaRecordSettings & settings,bool useMutex)15094 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15095 {
15096 m_UseMutex = useMutex;
15097 m_Flags = settings.flags;
15098
15099 #if defined(_WIN32)
15100 // Open file for writing.
15101 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15102
15103 if(err != 0)
15104 {
15105 return VK_ERROR_INITIALIZATION_FAILED;
15106 }
15107 #else
15108 // Open file for writing.
15109 m_File = fopen(settings.pFilePath, "wb");
15110
15111 if(m_File == 0)
15112 {
15113 return VK_ERROR_INITIALIZATION_FAILED;
15114 }
15115 #endif
15116
15117 // Write header.
15118 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15119 fprintf(m_File, "%s\n", "1,8");
15120
15121 return VK_SUCCESS;
15122 }
15123
~VmaRecorder()15124 VmaRecorder::~VmaRecorder()
15125 {
15126 if(m_File != VMA_NULL)
15127 {
15128 fclose(m_File);
15129 }
15130 }
15131
RecordCreateAllocator(uint32_t frameIndex)15132 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15133 {
15134 CallParams callParams;
15135 GetBasicParams(callParams);
15136
15137 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15138 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15139 Flush();
15140 }
15141
RecordDestroyAllocator(uint32_t frameIndex)15142 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15143 {
15144 CallParams callParams;
15145 GetBasicParams(callParams);
15146
15147 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15148 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15149 Flush();
15150 }
15151
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)15152 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15153 {
15154 CallParams callParams;
15155 GetBasicParams(callParams);
15156
15157 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15158 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15159 createInfo.memoryTypeIndex,
15160 createInfo.flags,
15161 createInfo.blockSize,
15162 (uint64_t)createInfo.minBlockCount,
15163 (uint64_t)createInfo.maxBlockCount,
15164 createInfo.frameInUseCount,
15165 pool);
15166 Flush();
15167 }
15168
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)15169 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15170 {
15171 CallParams callParams;
15172 GetBasicParams(callParams);
15173
15174 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15175 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15176 pool);
15177 Flush();
15178 }
15179
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15180 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15181 const VkMemoryRequirements& vkMemReq,
15182 const VmaAllocationCreateInfo& createInfo,
15183 VmaAllocation allocation)
15184 {
15185 CallParams callParams;
15186 GetBasicParams(callParams);
15187
15188 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15189 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15190 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15191 vkMemReq.size,
15192 vkMemReq.alignment,
15193 vkMemReq.memoryTypeBits,
15194 createInfo.flags,
15195 createInfo.usage,
15196 createInfo.requiredFlags,
15197 createInfo.preferredFlags,
15198 createInfo.memoryTypeBits,
15199 createInfo.pool,
15200 allocation,
15201 userDataStr.GetString());
15202 Flush();
15203 }
15204
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)15205 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15206 const VkMemoryRequirements& vkMemReq,
15207 const VmaAllocationCreateInfo& createInfo,
15208 uint64_t allocationCount,
15209 const VmaAllocation* pAllocations)
15210 {
15211 CallParams callParams;
15212 GetBasicParams(callParams);
15213
15214 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15215 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15216 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15217 vkMemReq.size,
15218 vkMemReq.alignment,
15219 vkMemReq.memoryTypeBits,
15220 createInfo.flags,
15221 createInfo.usage,
15222 createInfo.requiredFlags,
15223 createInfo.preferredFlags,
15224 createInfo.memoryTypeBits,
15225 createInfo.pool);
15226 PrintPointerList(allocationCount, pAllocations);
15227 fprintf(m_File, ",%s\n", userDataStr.GetString());
15228 Flush();
15229 }
15230
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15231 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15232 const VkMemoryRequirements& vkMemReq,
15233 bool requiresDedicatedAllocation,
15234 bool prefersDedicatedAllocation,
15235 const VmaAllocationCreateInfo& createInfo,
15236 VmaAllocation allocation)
15237 {
15238 CallParams callParams;
15239 GetBasicParams(callParams);
15240
15241 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15242 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15243 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15244 vkMemReq.size,
15245 vkMemReq.alignment,
15246 vkMemReq.memoryTypeBits,
15247 requiresDedicatedAllocation ? 1 : 0,
15248 prefersDedicatedAllocation ? 1 : 0,
15249 createInfo.flags,
15250 createInfo.usage,
15251 createInfo.requiredFlags,
15252 createInfo.preferredFlags,
15253 createInfo.memoryTypeBits,
15254 createInfo.pool,
15255 allocation,
15256 userDataStr.GetString());
15257 Flush();
15258 }
15259
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15260 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15261 const VkMemoryRequirements& vkMemReq,
15262 bool requiresDedicatedAllocation,
15263 bool prefersDedicatedAllocation,
15264 const VmaAllocationCreateInfo& createInfo,
15265 VmaAllocation allocation)
15266 {
15267 CallParams callParams;
15268 GetBasicParams(callParams);
15269
15270 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15271 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15272 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15273 vkMemReq.size,
15274 vkMemReq.alignment,
15275 vkMemReq.memoryTypeBits,
15276 requiresDedicatedAllocation ? 1 : 0,
15277 prefersDedicatedAllocation ? 1 : 0,
15278 createInfo.flags,
15279 createInfo.usage,
15280 createInfo.requiredFlags,
15281 createInfo.preferredFlags,
15282 createInfo.memoryTypeBits,
15283 createInfo.pool,
15284 allocation,
15285 userDataStr.GetString());
15286 Flush();
15287 }
15288
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15289 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15290 VmaAllocation allocation)
15291 {
15292 CallParams callParams;
15293 GetBasicParams(callParams);
15294
15295 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15296 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15297 allocation);
15298 Flush();
15299 }
15300
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15301 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15302 uint64_t allocationCount,
15303 const VmaAllocation* pAllocations)
15304 {
15305 CallParams callParams;
15306 GetBasicParams(callParams);
15307
15308 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15309 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15310 PrintPointerList(allocationCount, pAllocations);
15311 fprintf(m_File, "\n");
15312 Flush();
15313 }
15314
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15315 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15316 VmaAllocation allocation,
15317 const void* pUserData)
15318 {
15319 CallParams callParams;
15320 GetBasicParams(callParams);
15321
15322 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15323 UserDataString userDataStr(
15324 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15325 pUserData);
15326 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15327 allocation,
15328 userDataStr.GetString());
15329 Flush();
15330 }
15331
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15332 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15333 VmaAllocation allocation)
15334 {
15335 CallParams callParams;
15336 GetBasicParams(callParams);
15337
15338 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15339 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15340 allocation);
15341 Flush();
15342 }
15343
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15344 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15345 VmaAllocation allocation)
15346 {
15347 CallParams callParams;
15348 GetBasicParams(callParams);
15349
15350 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15351 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15352 allocation);
15353 Flush();
15354 }
15355
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15356 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15357 VmaAllocation allocation)
15358 {
15359 CallParams callParams;
15360 GetBasicParams(callParams);
15361
15362 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15363 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15364 allocation);
15365 Flush();
15366 }
15367
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15368 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15369 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15370 {
15371 CallParams callParams;
15372 GetBasicParams(callParams);
15373
15374 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15375 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15376 allocation,
15377 offset,
15378 size);
15379 Flush();
15380 }
15381
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15382 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15383 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15384 {
15385 CallParams callParams;
15386 GetBasicParams(callParams);
15387
15388 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15389 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15390 allocation,
15391 offset,
15392 size);
15393 Flush();
15394 }
15395
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15396 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15397 const VkBufferCreateInfo& bufCreateInfo,
15398 const VmaAllocationCreateInfo& allocCreateInfo,
15399 VmaAllocation allocation)
15400 {
15401 CallParams callParams;
15402 GetBasicParams(callParams);
15403
15404 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15405 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15406 fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15407 bufCreateInfo.flags,
15408 bufCreateInfo.size,
15409 bufCreateInfo.usage,
15410 bufCreateInfo.sharingMode,
15411 allocCreateInfo.flags,
15412 allocCreateInfo.usage,
15413 allocCreateInfo.requiredFlags,
15414 allocCreateInfo.preferredFlags,
15415 allocCreateInfo.memoryTypeBits,
15416 allocCreateInfo.pool,
15417 allocation,
15418 userDataStr.GetString());
15419 Flush();
15420 }
15421
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15422 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15423 const VkImageCreateInfo& imageCreateInfo,
15424 const VmaAllocationCreateInfo& allocCreateInfo,
15425 VmaAllocation allocation)
15426 {
15427 CallParams callParams;
15428 GetBasicParams(callParams);
15429
15430 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15431 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15432 fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15433 imageCreateInfo.flags,
15434 imageCreateInfo.imageType,
15435 imageCreateInfo.format,
15436 imageCreateInfo.extent.width,
15437 imageCreateInfo.extent.height,
15438 imageCreateInfo.extent.depth,
15439 imageCreateInfo.mipLevels,
15440 imageCreateInfo.arrayLayers,
15441 imageCreateInfo.samples,
15442 imageCreateInfo.tiling,
15443 imageCreateInfo.usage,
15444 imageCreateInfo.sharingMode,
15445 imageCreateInfo.initialLayout,
15446 allocCreateInfo.flags,
15447 allocCreateInfo.usage,
15448 allocCreateInfo.requiredFlags,
15449 allocCreateInfo.preferredFlags,
15450 allocCreateInfo.memoryTypeBits,
15451 allocCreateInfo.pool,
15452 allocation,
15453 userDataStr.GetString());
15454 Flush();
15455 }
15456
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15457 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15458 VmaAllocation allocation)
15459 {
15460 CallParams callParams;
15461 GetBasicParams(callParams);
15462
15463 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15464 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15465 allocation);
15466 Flush();
15467 }
15468
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15469 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15470 VmaAllocation allocation)
15471 {
15472 CallParams callParams;
15473 GetBasicParams(callParams);
15474
15475 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15476 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15477 allocation);
15478 Flush();
15479 }
15480
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15481 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15482 VmaAllocation allocation)
15483 {
15484 CallParams callParams;
15485 GetBasicParams(callParams);
15486
15487 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15488 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15489 allocation);
15490 Flush();
15491 }
15492
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15493 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15494 VmaAllocation allocation)
15495 {
15496 CallParams callParams;
15497 GetBasicParams(callParams);
15498
15499 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15500 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15501 allocation);
15502 Flush();
15503 }
15504
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15505 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15506 VmaPool pool)
15507 {
15508 CallParams callParams;
15509 GetBasicParams(callParams);
15510
15511 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15512 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15513 pool);
15514 Flush();
15515 }
15516
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15517 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15518 const VmaDefragmentationInfo2& info,
15519 VmaDefragmentationContext ctx)
15520 {
15521 CallParams callParams;
15522 GetBasicParams(callParams);
15523
15524 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15525 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15526 info.flags);
15527 PrintPointerList(info.allocationCount, info.pAllocations);
15528 fprintf(m_File, ",");
15529 PrintPointerList(info.poolCount, info.pPools);
15530 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15531 info.maxCpuBytesToMove,
15532 info.maxCpuAllocationsToMove,
15533 info.maxGpuBytesToMove,
15534 info.maxGpuAllocationsToMove,
15535 info.commandBuffer,
15536 ctx);
15537 Flush();
15538 }
15539
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15540 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15541 VmaDefragmentationContext ctx)
15542 {
15543 CallParams callParams;
15544 GetBasicParams(callParams);
15545
15546 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15547 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15548 ctx);
15549 Flush();
15550 }
15551
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15552 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15553 VmaPool pool,
15554 const char* name)
15555 {
15556 CallParams callParams;
15557 GetBasicParams(callParams);
15558
15559 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15560 fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15561 pool, name != VMA_NULL ? name : "");
15562 Flush();
15563 }
15564
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15565 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15566 {
15567 if(pUserData != VMA_NULL)
15568 {
15569 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15570 {
15571 m_Str = (const char*)pUserData;
15572 }
15573 else
15574 {
15575 // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15576 snprintf(m_PtrStr, 17, "%p", pUserData);
15577 m_Str = m_PtrStr;
15578 }
15579 }
15580 else
15581 {
15582 m_Str = "";
15583 }
15584 }
15585
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15586 void VmaRecorder::WriteConfiguration(
15587 const VkPhysicalDeviceProperties& devProps,
15588 const VkPhysicalDeviceMemoryProperties& memProps,
15589 uint32_t vulkanApiVersion,
15590 bool dedicatedAllocationExtensionEnabled,
15591 bool bindMemory2ExtensionEnabled,
15592 bool memoryBudgetExtensionEnabled,
15593 bool deviceCoherentMemoryExtensionEnabled)
15594 {
15595 fprintf(m_File, "Config,Begin\n");
15596
15597 fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15598
15599 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15600 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15601 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15602 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15603 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15604 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15605
15606 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15607 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15608 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15609
15610 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15611 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15612 {
15613 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15614 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15615 }
15616 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15617 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15618 {
15619 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15620 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15621 }
15622
15623 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15624 fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15625 fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15626 fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15627
15628 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15629 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15630 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15631 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15632 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15633 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15634 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15635 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15636 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15637
15638 fprintf(m_File, "Config,End\n");
15639 }
15640
GetBasicParams(CallParams & outParams)15641 void VmaRecorder::GetBasicParams(CallParams& outParams)
15642 {
15643 #if defined(_WIN32)
15644 outParams.threadId = GetCurrentThreadId();
15645 #else
15646 // Use C++11 features to get thread id and convert it to uint32_t.
15647 // There is room for optimization since sstream is quite slow.
15648 // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15649 std::thread::id thread_id = std::this_thread::get_id();
15650 stringstream thread_id_to_string_converter;
15651 thread_id_to_string_converter << thread_id;
15652 string thread_id_as_string = thread_id_to_string_converter.str();
15653 outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15654 #endif
15655
15656 auto current_time = std::chrono::high_resolution_clock::now();
15657
15658 outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15659 }
15660
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15661 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15662 {
15663 if(count)
15664 {
15665 fprintf(m_File, "%p", pItems[0]);
15666 for(uint64_t i = 1; i < count; ++i)
15667 {
15668 fprintf(m_File, " %p", pItems[i]);
15669 }
15670 }
15671 }
15672
Flush()15673 void VmaRecorder::Flush()
15674 {
15675 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15676 {
15677 fflush(m_File);
15678 }
15679 }
15680
15681 #endif // #if VMA_RECORDING_ENABLED
15682
15683 ////////////////////////////////////////////////////////////////////////////////
15684 // VmaAllocationObjectAllocator
15685
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15686 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15687 m_Allocator(pAllocationCallbacks, 1024)
15688 {
15689 }
15690
Allocate(Types...args)15691 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15692 {
15693 VmaMutexLock mutexLock(m_Mutex);
15694 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15695 }
15696
Free(VmaAllocation hAlloc)15697 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15698 {
15699 VmaMutexLock mutexLock(m_Mutex);
15700 m_Allocator.Free(hAlloc);
15701 }
15702
15703 ////////////////////////////////////////////////////////////////////////////////
15704 // VmaAllocator_T
15705
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15706 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15707 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15708 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15709 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15710 m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15711 m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15712 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15713 m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15714 m_hDevice(pCreateInfo->device),
15715 m_hInstance(pCreateInfo->instance),
15716 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15717 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15718 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15719 m_AllocationObjectAllocator(&m_AllocationCallbacks),
15720 m_HeapSizeLimitMask(0),
15721 m_PreferredLargeHeapBlockSize(0),
15722 m_PhysicalDevice(pCreateInfo->physicalDevice),
15723 m_CurrentFrameIndex(0),
15724 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15725 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15726 m_NextPoolId(0),
15727 m_GlobalMemoryTypeBits(UINT32_MAX)
15728 #if VMA_RECORDING_ENABLED
15729 ,m_pRecorder(VMA_NULL)
15730 #endif
15731 {
15732 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15733 {
15734 m_UseKhrDedicatedAllocation = false;
15735 m_UseKhrBindMemory2 = false;
15736 }
15737
15738 if(VMA_DEBUG_DETECT_CORRUPTION)
15739 {
15740 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15741 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15742 }
15743
15744 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15745
15746 if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15747 {
15748 #if !(VMA_DEDICATED_ALLOCATION)
15749 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15750 {
15751 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15752 }
15753 #endif
15754 #if !(VMA_BIND_MEMORY2)
15755 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15756 {
15757 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15758 }
15759 #endif
15760 }
15761 #if !(VMA_MEMORY_BUDGET)
15762 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15763 {
15764 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15765 }
15766 #endif
15767 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15768 if(m_UseKhrBufferDeviceAddress)
15769 {
15770 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
15771 }
15772 #endif
15773 #if VMA_VULKAN_VERSION < 1002000
15774 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15775 {
15776 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15777 }
15778 #endif
15779 #if VMA_VULKAN_VERSION < 1001000
15780 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15781 {
15782 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15783 }
15784 #endif
15785
15786 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15787 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15788 memset(&m_MemProps, 0, sizeof(m_MemProps));
15789
15790 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15791 memset(&m_pReservedBlockVectors, 0, sizeof(m_pReservedBlockVectors));
15792 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15793 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15794
15795 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15796 {
15797 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15798 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15799 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15800 }
15801
15802 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15803
15804 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15805 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15806
15807 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15808 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15809 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15810 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15811
15812 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15813 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15814
15815 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15816
15817 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15818 {
15819 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15820 {
15821 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15822 if(limit != VK_WHOLE_SIZE)
15823 {
15824 m_HeapSizeLimitMask |= 1u << heapIndex;
15825 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15826 {
15827 m_MemProps.memoryHeaps[heapIndex].size = limit;
15828 }
15829 }
15830 }
15831 }
15832
15833 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15834 {
15835 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15836
15837 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15838 this,
15839 VK_NULL_HANDLE, // hParentPool
15840 memTypeIndex,
15841 preferredBlockSize,
15842 0,
15843 pCreateInfo->maxBlockCount,
15844 GetBufferImageGranularity(),
15845 pCreateInfo->frameInUseCount,
15846 false, // explicitBlockSize
15847 false); // linearAlgorithm
15848 m_pReservedBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15849 this,
15850 VK_NULL_HANDLE, // hParentPool
15851 memTypeIndex,
15852 preferredBlockSize,
15853 0,
15854 1, // max block count 1
15855 GetBufferImageGranularity(),
15856 pCreateInfo->frameInUseCount,
15857 false, // explicitBlockSize
15858 false); // linearAlgorithm
15859 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15860 // becase minBlockCount is 0.
15861 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15862
15863 }
15864 }
15865
Init(const VmaAllocatorCreateInfo * pCreateInfo)15866 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15867 {
15868 VkResult res = VK_SUCCESS;
15869
15870 if(pCreateInfo->pRecordSettings != VMA_NULL &&
15871 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15872 {
15873 #if VMA_RECORDING_ENABLED
15874 m_pRecorder = vma_new(this, VmaRecorder)();
15875 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15876 if(res != VK_SUCCESS)
15877 {
15878 return res;
15879 }
15880 m_pRecorder->WriteConfiguration(
15881 m_PhysicalDeviceProperties,
15882 m_MemProps,
15883 m_VulkanApiVersion,
15884 m_UseKhrDedicatedAllocation,
15885 m_UseKhrBindMemory2,
15886 m_UseExtMemoryBudget,
15887 m_UseAmdDeviceCoherentMemory);
15888 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15889 #else
15890 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15891 return VK_ERROR_FEATURE_NOT_PRESENT;
15892 #endif
15893 }
15894
15895 #if VMA_MEMORY_BUDGET
15896 if(m_UseExtMemoryBudget)
15897 {
15898 UpdateVulkanBudget();
15899 }
15900 #endif // #if VMA_MEMORY_BUDGET
15901
15902 return res;
15903 }
15904
~VmaAllocator_T()15905 VmaAllocator_T::~VmaAllocator_T()
15906 {
15907 #if VMA_RECORDING_ENABLED
15908 if(m_pRecorder != VMA_NULL)
15909 {
15910 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15911 vma_delete(this, m_pRecorder);
15912 }
15913 #endif
15914
15915 VMA_ASSERT(m_Pools.empty());
15916
15917 for(size_t i = GetMemoryTypeCount(); i--; )
15918 {
15919 if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15920 {
15921 VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15922 }
15923
15924 vma_delete(this, m_pDedicatedAllocations[i]);
15925 vma_delete(this, m_pBlockVectors[i]);
15926 vma_delete(this, m_pReservedBlockVectors[i]);
15927 }
15928 }
15929
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15930 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15931 {
15932 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15933 ImportVulkanFunctions_Static();
15934 #endif
15935
15936 if(pVulkanFunctions != VMA_NULL)
15937 {
15938 ImportVulkanFunctions_Custom(pVulkanFunctions);
15939 }
15940
15941 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15942 ImportVulkanFunctions_Dynamic();
15943 #endif
15944
15945 ValidateVulkanFunctions();
15946 }
15947
15948 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15949
ImportVulkanFunctions_Static()15950 void VmaAllocator_T::ImportVulkanFunctions_Static()
15951 {
15952 // Vulkan 1.0
15953 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15954 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15955 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15956 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15957 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15958 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15959 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15960 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15961 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15962 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15963 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15964 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15965 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15966 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15967 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15968 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15969 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15970
15971 // Vulkan 1.1
15972 #if VMA_VULKAN_VERSION >= 1001000
15973 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15974 {
15975 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15976 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15977 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15978 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15979 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15980 }
15981 #endif
15982 }
15983
15984 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15985
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15986 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15987 {
15988 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15989
15990 #define VMA_COPY_IF_NOT_NULL(funcName) \
15991 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15992
15993 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15994 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15995 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15996 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15997 VMA_COPY_IF_NOT_NULL(vkMapMemory);
15998 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15999 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
16000 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
16001 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
16002 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
16003 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
16004 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
16005 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16006 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16007 VMA_COPY_IF_NOT_NULL(vkCreateImage);
16008 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16009 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16010
16011 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16012 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16013 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16014 #endif
16015
16016 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16017 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16018 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16019 #endif
16020
16021 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16022 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16023 #endif
16024
16025 #undef VMA_COPY_IF_NOT_NULL
16026 }
16027
16028 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16029
ImportVulkanFunctions_Dynamic()16030 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16031 {
16032 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16033 if(m_VulkanFunctions.memberName == VMA_NULL) \
16034 m_VulkanFunctions.memberName = \
16035 (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16036 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16037 if(m_VulkanFunctions.memberName == VMA_NULL) \
16038 m_VulkanFunctions.memberName = \
16039 (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16040
16041 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16042 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16043 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16044 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16045 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16046 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16047 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16048 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16049 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16050 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16051 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16052 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16053 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16054 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16055 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16056 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16057 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16058
16059 #if VMA_DEDICATED_ALLOCATION
16060 if(m_UseKhrDedicatedAllocation)
16061 {
16062 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16063 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16064 }
16065 #endif
16066
16067 #if VMA_BIND_MEMORY2
16068 if(m_UseKhrBindMemory2)
16069 {
16070 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16071 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16072 }
16073 #endif // #if VMA_BIND_MEMORY2
16074
16075 #if VMA_MEMORY_BUDGET
16076 if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16077 {
16078 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16079 }
16080 #endif // #if VMA_MEMORY_BUDGET
16081
16082 #undef VMA_FETCH_DEVICE_FUNC
16083 #undef VMA_FETCH_INSTANCE_FUNC
16084 }
16085
16086 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16087
ValidateVulkanFunctions()16088 void VmaAllocator_T::ValidateVulkanFunctions()
16089 {
16090 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16091 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16092 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16093 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16094 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16095 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16096 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16097 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16098 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16099 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16100 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16101 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16102 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16103 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16104 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16105 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16106 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16107
16108 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16109 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16110 {
16111 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16112 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16113 }
16114 #endif
16115
16116 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16117 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16118 {
16119 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16120 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16121 }
16122 #endif
16123
16124 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16125 if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16126 {
16127 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16128 }
16129 #endif
16130 }
16131
CalcPreferredBlockSize(uint32_t memTypeIndex)16132 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16133 {
16134 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16135 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16136 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16137 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16138 }
16139
AllocateMemoryOfType(VkDeviceSize size,VkDeviceSize alignment,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16140 VkResult VmaAllocator_T::AllocateMemoryOfType(
16141 VkDeviceSize size,
16142 VkDeviceSize alignment,
16143 bool dedicatedAllocation,
16144 VkBuffer dedicatedBuffer,
16145 VkBufferUsageFlags dedicatedBufferUsage,
16146 VkImage dedicatedImage,
16147 const VmaAllocationCreateInfo& createInfo,
16148 uint32_t memTypeIndex,
16149 VmaSuballocationType suballocType,
16150 size_t allocationCount,
16151 VmaAllocation* pAllocations)
16152 {
16153 VMA_ASSERT(pAllocations != VMA_NULL);
16154 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16155
16156 VmaAllocationCreateInfo finalCreateInfo = createInfo;
16157
16158 // If memory type is not HOST_VISIBLE, disable MAPPED.
16159 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16160 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16161 {
16162 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16163 }
16164 // If memory is lazily allocated, it should be always dedicated.
16165 if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16166 {
16167 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16168 }
16169
16170 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16171 VMA_ASSERT(blockVector);
16172
16173 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16174 bool preferDedicatedMemory =
16175 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16176 dedicatedAllocation ||
16177 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16178 size > preferredBlockSize / 2;
16179
16180 if(preferDedicatedMemory &&
16181 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16182 finalCreateInfo.pool == VK_NULL_HANDLE)
16183 {
16184 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16185 }
16186
16187 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16188 {
16189 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16190 {
16191 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16192 }
16193 else
16194 {
16195 return AllocateDedicatedMemory(
16196 size,
16197 suballocType,
16198 memTypeIndex,
16199 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16200 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16201 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16202 finalCreateInfo.pUserData,
16203 dedicatedBuffer,
16204 dedicatedBufferUsage,
16205 dedicatedImage,
16206 allocationCount,
16207 pAllocations);
16208 }
16209 }
16210 else
16211 {
16212 VkResult res = blockVector->Allocate(
16213 m_CurrentFrameIndex.load(),
16214 size,
16215 alignment,
16216 finalCreateInfo,
16217 suballocType,
16218 allocationCount,
16219 pAllocations);
16220 if(res == VK_SUCCESS)
16221 {
16222 return res;
16223 }
16224
16225 // 5. Try dedicated memory.
16226 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16227 {
16228 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16229 }
16230 else
16231 {
16232 res = AllocateDedicatedMemory(
16233 size,
16234 suballocType,
16235 memTypeIndex,
16236 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16237 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16238 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16239 finalCreateInfo.pUserData,
16240 dedicatedBuffer,
16241 dedicatedBufferUsage,
16242 dedicatedImage,
16243 allocationCount,
16244 pAllocations);
16245 if(res == VK_SUCCESS)
16246 {
16247 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16248 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16249 return VK_SUCCESS;
16250 }
16251 else
16252 {
16253 // Everything failed: Return error code.
16254 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16255 return res;
16256 }
16257 }
16258 }
16259 }
16260
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool withinBudget,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,size_t allocationCount,VmaAllocation * pAllocations)16261 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16262 VkDeviceSize size,
16263 VmaSuballocationType suballocType,
16264 uint32_t memTypeIndex,
16265 bool withinBudget,
16266 bool map,
16267 bool isUserDataString,
16268 void* pUserData,
16269 VkBuffer dedicatedBuffer,
16270 VkBufferUsageFlags dedicatedBufferUsage,
16271 VkImage dedicatedImage,
16272 size_t allocationCount,
16273 VmaAllocation* pAllocations)
16274 {
16275 VMA_ASSERT(allocationCount > 0 && pAllocations);
16276
16277 if(withinBudget)
16278 {
16279 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16280 VmaBudget heapBudget = {};
16281 GetBudget(&heapBudget, heapIndex, 1);
16282 if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16283 {
16284 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16285 }
16286 }
16287
16288 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16289 allocInfo.memoryTypeIndex = memTypeIndex;
16290 allocInfo.allocationSize = size;
16291
16292 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16293 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16294 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16295 {
16296 if(dedicatedBuffer != VK_NULL_HANDLE)
16297 {
16298 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16299 dedicatedAllocInfo.buffer = dedicatedBuffer;
16300 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16301 }
16302 else if(dedicatedImage != VK_NULL_HANDLE)
16303 {
16304 dedicatedAllocInfo.image = dedicatedImage;
16305 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16306 }
16307 }
16308 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16309
16310 #if VMA_BUFFER_DEVICE_ADDRESS
16311 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16312 if(m_UseKhrBufferDeviceAddress)
16313 {
16314 bool canContainBufferWithDeviceAddress = true;
16315 if(dedicatedBuffer != VK_NULL_HANDLE)
16316 {
16317 canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16318 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16319 }
16320 else if(dedicatedImage != VK_NULL_HANDLE)
16321 {
16322 canContainBufferWithDeviceAddress = false;
16323 }
16324 if(canContainBufferWithDeviceAddress)
16325 {
16326 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16327 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16328 }
16329 }
16330 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16331
16332 size_t allocIndex;
16333 VkResult res = VK_SUCCESS;
16334 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16335 {
16336 res = AllocateDedicatedMemoryPage(
16337 size,
16338 suballocType,
16339 memTypeIndex,
16340 allocInfo,
16341 map,
16342 isUserDataString,
16343 pUserData,
16344 pAllocations + allocIndex);
16345 if(res != VK_SUCCESS)
16346 {
16347 break;
16348 }
16349 }
16350
16351 if(res == VK_SUCCESS)
16352 {
16353 // Register them in m_pDedicatedAllocations.
16354 {
16355 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16356 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16357 VMA_ASSERT(pDedicatedAllocations);
16358 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16359 {
16360 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16361 }
16362 }
16363
16364 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16365 }
16366 else
16367 {
16368 // Free all already created allocations.
16369 while(allocIndex--)
16370 {
16371 VmaAllocation currAlloc = pAllocations[allocIndex];
16372 VkDeviceMemory hMemory = currAlloc->GetMemory();
16373
16374 /*
16375 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16376 before vkFreeMemory.
16377
16378 if(currAlloc->GetMappedData() != VMA_NULL)
16379 {
16380 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16381 }
16382 */
16383
16384 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16385 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16386 currAlloc->SetUserData(this, VMA_NULL);
16387 m_AllocationObjectAllocator.Free(currAlloc);
16388 }
16389
16390 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16391 }
16392
16393 return res;
16394 }
16395
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16396 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16397 VkDeviceSize size,
16398 VmaSuballocationType suballocType,
16399 uint32_t memTypeIndex,
16400 const VkMemoryAllocateInfo& allocInfo,
16401 bool map,
16402 bool isUserDataString,
16403 void* pUserData,
16404 VmaAllocation* pAllocation)
16405 {
16406 VkDeviceMemory hMemory = VK_NULL_HANDLE;
16407 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16408 if(res < 0)
16409 {
16410 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16411 return res;
16412 }
16413
16414 void* pMappedData = VMA_NULL;
16415 if(map)
16416 {
16417 res = (*m_VulkanFunctions.vkMapMemory)(
16418 m_hDevice,
16419 hMemory,
16420 0,
16421 VK_WHOLE_SIZE,
16422 0,
16423 &pMappedData);
16424 if(res < 0)
16425 {
16426 VMA_DEBUG_LOG(" vkMapMemory FAILED");
16427 FreeVulkanMemory(memTypeIndex, size, hMemory);
16428 return res;
16429 }
16430 }
16431
16432 *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16433 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16434 (*pAllocation)->SetUserData(this, pUserData);
16435 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16436 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16437 {
16438 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16439 }
16440
16441 return VK_SUCCESS;
16442 }
16443
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16444 void VmaAllocator_T::GetBufferMemoryRequirements(
16445 VkBuffer hBuffer,
16446 VkMemoryRequirements& memReq,
16447 bool& requiresDedicatedAllocation,
16448 bool& prefersDedicatedAllocation) const
16449 {
16450 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16451 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16452 {
16453 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16454 memReqInfo.buffer = hBuffer;
16455
16456 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16457
16458 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16459 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16460
16461 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16462
16463 memReq = memReq2.memoryRequirements;
16464 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16465 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16466 }
16467 else
16468 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16469 {
16470 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16471 requiresDedicatedAllocation = false;
16472 prefersDedicatedAllocation = false;
16473 }
16474 }
16475
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16476 void VmaAllocator_T::GetImageMemoryRequirements(
16477 VkImage hImage,
16478 VkMemoryRequirements& memReq,
16479 bool& requiresDedicatedAllocation,
16480 bool& prefersDedicatedAllocation) const
16481 {
16482 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16483 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16484 {
16485 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16486 memReqInfo.image = hImage;
16487
16488 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16489
16490 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16491 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16492
16493 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16494
16495 memReq = memReq2.memoryRequirements;
16496 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16497 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16498 }
16499 else
16500 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16501 {
16502 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16503 requiresDedicatedAllocation = false;
16504 prefersDedicatedAllocation = false;
16505 }
16506 }
16507
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16508 VkResult VmaAllocator_T::AllocateMemory(
16509 const VkMemoryRequirements& vkMemReq,
16510 bool requiresDedicatedAllocation,
16511 bool prefersDedicatedAllocation,
16512 VkBuffer dedicatedBuffer,
16513 VkBufferUsageFlags dedicatedBufferUsage,
16514 VkImage dedicatedImage,
16515 const VmaAllocationCreateInfo& createInfo,
16516 VmaSuballocationType suballocType,
16517 size_t allocationCount,
16518 VmaAllocation* pAllocations)
16519 {
16520 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16521
16522 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16523
16524 if(vkMemReq.size == 0)
16525 {
16526 return VK_ERROR_VALIDATION_FAILED_EXT;
16527 }
16528 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16529 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16530 {
16531 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16532 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16533 }
16534 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16535 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16536 {
16537 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16538 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16539 }
16540 if(requiresDedicatedAllocation)
16541 {
16542 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16543 {
16544 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16545 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16546 }
16547 if(createInfo.pool != VK_NULL_HANDLE)
16548 {
16549 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16550 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16551 }
16552 }
16553 if((createInfo.pool != VK_NULL_HANDLE) &&
16554 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16555 {
16556 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16557 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16558 }
16559
16560 if(createInfo.pool != VK_NULL_HANDLE)
16561 {
16562 const VkDeviceSize alignmentForPool = VMA_MAX(
16563 vkMemReq.alignment,
16564 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16565
16566 VmaAllocationCreateInfo createInfoForPool = createInfo;
16567 // If memory type is not HOST_VISIBLE, disable MAPPED.
16568 if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16569 (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16570 {
16571 createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16572 }
16573
16574 return createInfo.pool->m_BlockVector.Allocate(
16575 m_CurrentFrameIndex.load(),
16576 vkMemReq.size,
16577 alignmentForPool,
16578 createInfoForPool,
16579 suballocType,
16580 allocationCount,
16581 pAllocations);
16582 }
16583 else
16584 {
16585 // Bit mask of memory Vulkan types acceptable for this allocation.
16586 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16587 uint32_t memTypeIndex = UINT32_MAX;
16588 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16589 if(res == VK_SUCCESS)
16590 {
16591 VkDeviceSize alignmentForMemType = VMA_MAX(
16592 vkMemReq.alignment,
16593 GetMemoryTypeMinAlignment(memTypeIndex));
16594
16595 res = AllocateMemoryOfType(
16596 vkMemReq.size,
16597 alignmentForMemType,
16598 requiresDedicatedAllocation || prefersDedicatedAllocation,
16599 dedicatedBuffer,
16600 dedicatedBufferUsage,
16601 dedicatedImage,
16602 createInfo,
16603 memTypeIndex,
16604 suballocType,
16605 allocationCount,
16606 pAllocations);
16607 // Succeeded on first try.
16608 if(res == VK_SUCCESS)
16609 {
16610 return res;
16611 }
16612 // Allocation from this memory type failed. Try other compatible memory types.
16613 else
16614 {
16615 for(;;)
16616 {
16617 // Remove old memTypeIndex from list of possibilities.
16618 memoryTypeBits &= ~(1u << memTypeIndex);
16619 // Find alternative memTypeIndex.
16620 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16621 if(res == VK_SUCCESS)
16622 {
16623 alignmentForMemType = VMA_MAX(
16624 vkMemReq.alignment,
16625 GetMemoryTypeMinAlignment(memTypeIndex));
16626
16627 res = AllocateMemoryOfType(
16628 vkMemReq.size,
16629 alignmentForMemType,
16630 requiresDedicatedAllocation || prefersDedicatedAllocation,
16631 dedicatedBuffer,
16632 dedicatedBufferUsage,
16633 dedicatedImage,
16634 createInfo,
16635 memTypeIndex,
16636 suballocType,
16637 allocationCount,
16638 pAllocations);
16639 // Allocation from this alternative memory type succeeded.
16640 if(res == VK_SUCCESS)
16641 {
16642 return res;
16643 }
16644 // else: Allocation from this memory type failed. Try next one - next loop iteration.
16645 }
16646 // No other matching memory type index could be found.
16647 else
16648 {
16649 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16650 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16651 }
16652 }
16653 }
16654 }
16655 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16656 else
16657 return res;
16658 }
16659 }
16660
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16661 void VmaAllocator_T::FreeMemory(
16662 size_t allocationCount,
16663 const VmaAllocation* pAllocations)
16664 {
16665 VMA_ASSERT(pAllocations);
16666
16667 for(size_t allocIndex = allocationCount; allocIndex--; )
16668 {
16669 VmaAllocation allocation = pAllocations[allocIndex];
16670
16671 if(allocation != VK_NULL_HANDLE)
16672 {
16673 if(TouchAllocation(allocation))
16674 {
16675 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16676 {
16677 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16678 }
16679
16680 switch(allocation->GetType())
16681 {
16682 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16683 {
16684 VmaBlockVector* pBlockVector = VMA_NULL;
16685 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16686 if(hPool != VK_NULL_HANDLE)
16687 {
16688 pBlockVector = &hPool->m_BlockVector;
16689 }
16690 else
16691 {
16692 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16693 pBlockVector = m_pBlockVectors[memTypeIndex];
16694 }
16695 pBlockVector->Free(allocation);
16696 }
16697 break;
16698 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16699 FreeDedicatedMemory(allocation);
16700 break;
16701 default:
16702 VMA_ASSERT(0);
16703 }
16704 }
16705
16706 // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16707 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16708 allocation->SetUserData(this, VMA_NULL);
16709 m_AllocationObjectAllocator.Free(allocation);
16710 }
16711 }
16712 }
16713
16714 // OH ISSUE: VMA preAlloc
AllocateReservedMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16715 VkResult VmaAllocator_T::AllocateReservedMemory(
16716 const VkMemoryRequirements& vkMemReq,
16717 bool requiresDedicatedAllocation,
16718 bool prefersDedicatedAllocation,
16719 VkBuffer dedicatedBuffer,
16720 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
16721 VkImage dedicatedImage,
16722 const VmaAllocationCreateInfo& createInfo,
16723 VmaSuballocationType suballocType,
16724 size_t allocationCount,
16725 VmaAllocation* pAllocations)
16726 {
16727 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16728 if (allocationCount != 1) {
16729 return VK_NOT_READY;
16730 }
16731 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16732 uint32_t memTypeIndex = UINT32_MAX;
16733 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16734 if(res != VK_SUCCESS) {
16735 return res;
16736 }
16737 VkDeviceSize alignmentForMemType = VMA_MAX(
16738 vkMemReq.alignment,
16739 GetMemoryTypeMinAlignment(memTypeIndex));
16740 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16741 VMA_ASSERT(reservedBlockVector);
16742
16743 res = reservedBlockVector->AllocateReserved(
16744 m_CurrentFrameIndex.load(),
16745 vkMemReq.size,
16746 alignmentForMemType,
16747 createInfo,
16748 suballocType,
16749 pAllocations);
16750 return res;
16751 }
16752
FreeReservedMemory(size_t allocationCount,const VmaAllocation * pAllocations)16753 void VmaAllocator_T::FreeReservedMemory(
16754 size_t allocationCount,
16755 const VmaAllocation* pAllocations)
16756 {
16757 VMA_ASSERT(pAllocations);
16758
16759 for(size_t allocIndex = allocationCount; allocIndex--; )
16760 {
16761 VmaAllocation allocation = pAllocations[allocIndex];
16762
16763 if(allocation != VK_NULL_HANDLE)
16764 {
16765 if(TouchAllocation(allocation))
16766 {
16767 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16768 {
16769 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16770 }
16771
16772 switch(allocation->GetType())
16773 {
16774 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16775 {
16776 VmaBlockVector* pBlockVector = VMA_NULL;
16777 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16778 if(hPool != VK_NULL_HANDLE)
16779 {
16780 pBlockVector = &hPool->m_BlockVector;
16781 }
16782 else
16783 {
16784 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16785 pBlockVector = m_pReservedBlockVectors[memTypeIndex];
16786 }
16787 VMA_ASSERT(pBlockVector);
16788 pBlockVector->FreeReserved(allocation);
16789 }
16790 break;
16791 default:
16792 VMA_ASSERT(0);
16793 }
16794 }
16795 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16796 }
16797 }
16798 }
16799
SwapReservedBlock(VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16800 VkResult VmaAllocator_T::SwapReservedBlock(
16801 VkImage image,
16802 const VmaAllocationCreateInfo* pCreateInfo,
16803 VmaAllocation* pAllocation,
16804 VmaAllocationInfo* pAllocationInfo)
16805 {
16806 VmaAllocation oldAllocation = *pAllocation;
16807 if (oldAllocation->GetType() != VmaAllocation_T::ALLOCATION_TYPE_BLOCK) {
16808 return VK_ERROR_UNKNOWN;
16809 }
16810 VmaPool hPool = oldAllocation->GetBlock()->GetParentPool();
16811 if(hPool != VK_NULL_HANDLE) {
16812 return VK_ERROR_UNKNOWN;
16813 }
16814 const uint32_t memTypeIndex = oldAllocation->GetMemoryTypeIndex();
16815 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16816 VMA_ASSERT(reservedBlockVector);
16817 if (reservedBlockVector->IsEmpty()) {
16818 return VK_NOT_READY;
16819 }
16820 if (!reservedBlockVector->IsLastBlockBindComplete()) {
16821 return VK_INCOMPLETE;
16822 }
16823
16824 VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
16825 VMA_ASSERT(blockVector);
16826 if (!blockVector->SwapLastBlock(reservedBlockVector)) {
16827 return VK_ERROR_UNKNOWN;
16828 }
16829 return VK_SUCCESS;
16830 }
16831
GetPreAllocBlockSize()16832 uint32_t VmaAllocator_T::GetPreAllocBlockSize()
16833 {
16834 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[0];
16835 VMA_ASSERT(reservedBlockVector);
16836 return reservedBlockVector->GetBlockCount();
16837 }
16838
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16839 VkResult VmaAllocator_T::ResizeAllocation(
16840 const VmaAllocation alloc,
16841 VkDeviceSize newSize)
16842 {
16843 // This function is deprecated and so it does nothing. It's left for backward compatibility.
16844 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16845 {
16846 return VK_ERROR_VALIDATION_FAILED_EXT;
16847 }
16848 if(newSize == alloc->GetSize())
16849 {
16850 return VK_SUCCESS;
16851 }
16852 return VK_ERROR_OUT_OF_POOL_MEMORY;
16853 }
16854
CalculateStats(VmaStats * pStats)16855 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16856 {
16857 // Initialize.
16858 InitStatInfo(pStats->total);
16859 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16860 InitStatInfo(pStats->memoryType[i]);
16861 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16862 InitStatInfo(pStats->memoryHeap[i]);
16863
16864 // Process default pools.
16865 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16866 {
16867 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16868 VMA_ASSERT(pBlockVector);
16869 pBlockVector->AddStats(pStats);
16870 }
16871
16872 // Process custom pools.
16873 {
16874 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16875 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16876 {
16877 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16878 }
16879 }
16880
16881 // Process dedicated allocations.
16882 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16883 {
16884 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16885 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16886 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16887 VMA_ASSERT(pDedicatedAllocVector);
16888 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16889 {
16890 VmaStatInfo allocationStatInfo;
16891 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16892 VmaAddStatInfo(pStats->total, allocationStatInfo);
16893 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16894 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16895 }
16896 }
16897
16898 // Postprocess.
16899 VmaPostprocessCalcStatInfo(pStats->total);
16900 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16901 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16902 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16903 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16904 }
16905
FreeEmptyBlock()16906 void VmaAllocator_T::FreeEmptyBlock()
16907 {
16908 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
16909 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16910 VMA_ASSERT(pBlockVector);
16911 pBlockVector->FreeEmptyBlock();
16912
16913 VmaBlockVector* const pReservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16914 VMA_ASSERT(pReservedBlockVector);
16915 pReservedBlockVector->FreeEmptyBlock();
16916 }
16917 }
16918
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16919 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16920 {
16921 #if VMA_MEMORY_BUDGET
16922 if(m_UseExtMemoryBudget)
16923 {
16924 if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16925 {
16926 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16927 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16928 {
16929 const uint32_t heapIndex = firstHeap + i;
16930
16931 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16932 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16933
16934 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16935 {
16936 outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16937 outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16938 }
16939 else
16940 {
16941 outBudget->usage = 0;
16942 }
16943
16944 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16945 outBudget->budget = VMA_MIN(
16946 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16947 }
16948 }
16949 else
16950 {
16951 UpdateVulkanBudget(); // Outside of mutex lock
16952 GetBudget(outBudget, firstHeap, heapCount); // Recursion
16953 }
16954 }
16955 else
16956 #endif
16957 {
16958 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16959 {
16960 const uint32_t heapIndex = firstHeap + i;
16961
16962 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16963 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16964
16965 outBudget->usage = outBudget->blockBytes;
16966 outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16967 }
16968 }
16969 }
16970
16971 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16972
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16973 VkResult VmaAllocator_T::DefragmentationBegin(
16974 const VmaDefragmentationInfo2& info,
16975 VmaDefragmentationStats* pStats,
16976 VmaDefragmentationContext* pContext)
16977 {
16978 if(info.pAllocationsChanged != VMA_NULL)
16979 {
16980 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16981 }
16982
16983 *pContext = vma_new(this, VmaDefragmentationContext_T)(
16984 this, m_CurrentFrameIndex.load(), info.flags, pStats);
16985
16986 (*pContext)->AddPools(info.poolCount, info.pPools);
16987 (*pContext)->AddAllocations(
16988 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16989
16990 VkResult res = (*pContext)->Defragment(
16991 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16992 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16993 info.commandBuffer, pStats, info.flags);
16994
16995 if(res != VK_NOT_READY)
16996 {
16997 vma_delete(this, *pContext);
16998 *pContext = VMA_NULL;
16999 }
17000
17001 return res;
17002 }
17003
DefragmentationEnd(VmaDefragmentationContext context)17004 VkResult VmaAllocator_T::DefragmentationEnd(
17005 VmaDefragmentationContext context)
17006 {
17007 vma_delete(this, context);
17008 return VK_SUCCESS;
17009 }
17010
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)17011 VkResult VmaAllocator_T::DefragmentationPassBegin(
17012 VmaDefragmentationPassInfo* pInfo,
17013 VmaDefragmentationContext context)
17014 {
17015 return context->DefragmentPassBegin(pInfo);
17016 }
DefragmentationPassEnd(VmaDefragmentationContext context)17017 VkResult VmaAllocator_T::DefragmentationPassEnd(
17018 VmaDefragmentationContext context)
17019 {
17020 return context->DefragmentPassEnd();
17021
17022 }
17023
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)17024 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17025 {
17026 if(hAllocation->CanBecomeLost())
17027 {
17028 /*
17029 Warning: This is a carefully designed algorithm.
17030 Do not modify unless you really know what you're doing :)
17031 */
17032 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17033 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17034 for(;;)
17035 {
17036 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17037 {
17038 pAllocationInfo->memoryType = UINT32_MAX;
17039 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17040 pAllocationInfo->offset = 0;
17041 pAllocationInfo->size = hAllocation->GetSize();
17042 pAllocationInfo->pMappedData = VMA_NULL;
17043 pAllocationInfo->pUserData = hAllocation->GetUserData();
17044 return;
17045 }
17046 else if(localLastUseFrameIndex == localCurrFrameIndex)
17047 {
17048 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17049 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17050 pAllocationInfo->offset = hAllocation->GetOffset();
17051 pAllocationInfo->size = hAllocation->GetSize();
17052 pAllocationInfo->pMappedData = VMA_NULL;
17053 pAllocationInfo->pUserData = hAllocation->GetUserData();
17054 return;
17055 }
17056 else // Last use time earlier than current time.
17057 {
17058 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17059 {
17060 localLastUseFrameIndex = localCurrFrameIndex;
17061 }
17062 }
17063 }
17064 }
17065 else
17066 {
17067 #if VMA_STATS_STRING_ENABLED
17068 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17069 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17070 for(;;)
17071 {
17072 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17073 if(localLastUseFrameIndex == localCurrFrameIndex)
17074 {
17075 break;
17076 }
17077 else // Last use time earlier than current time.
17078 {
17079 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17080 {
17081 localLastUseFrameIndex = localCurrFrameIndex;
17082 }
17083 }
17084 }
17085 #endif
17086
17087 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17088 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17089 pAllocationInfo->offset = hAllocation->GetOffset();
17090 pAllocationInfo->size = hAllocation->GetSize();
17091 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17092 pAllocationInfo->pUserData = hAllocation->GetUserData();
17093 }
17094 }
17095
TouchAllocation(VmaAllocation hAllocation)17096 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17097 {
17098 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17099 if(hAllocation->CanBecomeLost())
17100 {
17101 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17102 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17103 for(;;)
17104 {
17105 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17106 {
17107 return false;
17108 }
17109 else if(localLastUseFrameIndex == localCurrFrameIndex)
17110 {
17111 return true;
17112 }
17113 else // Last use time earlier than current time.
17114 {
17115 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17116 {
17117 localLastUseFrameIndex = localCurrFrameIndex;
17118 }
17119 }
17120 }
17121 }
17122 else
17123 {
17124 #if VMA_STATS_STRING_ENABLED
17125 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17126 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17127 for(;;)
17128 {
17129 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17130 if(localLastUseFrameIndex == localCurrFrameIndex)
17131 {
17132 break;
17133 }
17134 else // Last use time earlier than current time.
17135 {
17136 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17137 {
17138 localLastUseFrameIndex = localCurrFrameIndex;
17139 }
17140 }
17141 }
17142 #endif
17143
17144 return true;
17145 }
17146 }
17147
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17148 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17149 {
17150 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17151
17152 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17153
17154 if(newCreateInfo.maxBlockCount == 0)
17155 {
17156 newCreateInfo.maxBlockCount = SIZE_MAX;
17157 }
17158 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17159 {
17160 return VK_ERROR_INITIALIZATION_FAILED;
17161 }
17162 // Memory type index out of range or forbidden.
17163 if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17164 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17165 {
17166 return VK_ERROR_FEATURE_NOT_PRESENT;
17167 }
17168
17169 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17170
17171 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17172
17173 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17174 if(res != VK_SUCCESS)
17175 {
17176 vma_delete(this, *pPool);
17177 *pPool = VMA_NULL;
17178 return res;
17179 }
17180
17181 // Add to m_Pools.
17182 {
17183 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17184 (*pPool)->SetId(m_NextPoolId++);
17185 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
17186 }
17187
17188 return VK_SUCCESS;
17189 }
17190
DestroyPool(VmaPool pool)17191 void VmaAllocator_T::DestroyPool(VmaPool pool)
17192 {
17193 // Remove from m_Pools.
17194 {
17195 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17196 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
17197 VMA_ASSERT(success && "Pool not found in Allocator.");
17198 }
17199
17200 vma_delete(this, pool);
17201 }
17202
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)17203 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17204 {
17205 pool->m_BlockVector.GetPoolStats(pPoolStats);
17206 }
17207
SetCurrentFrameIndex(uint32_t frameIndex)17208 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17209 {
17210 m_CurrentFrameIndex.store(frameIndex);
17211
17212 #if VMA_MEMORY_BUDGET
17213 if(m_UseExtMemoryBudget)
17214 {
17215 UpdateVulkanBudget();
17216 }
17217 #endif // #if VMA_MEMORY_BUDGET
17218 }
17219
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)17220 void VmaAllocator_T::MakePoolAllocationsLost(
17221 VmaPool hPool,
17222 size_t* pLostAllocationCount)
17223 {
17224 hPool->m_BlockVector.MakePoolAllocationsLost(
17225 m_CurrentFrameIndex.load(),
17226 pLostAllocationCount);
17227 }
17228
CheckPoolCorruption(VmaPool hPool)17229 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17230 {
17231 return hPool->m_BlockVector.CheckCorruption();
17232 }
17233
CheckCorruption(uint32_t memoryTypeBits)17234 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17235 {
17236 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17237
17238 // Process default pools.
17239 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17240 {
17241 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17242 {
17243 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17244 VMA_ASSERT(pBlockVector);
17245 VkResult localRes = pBlockVector->CheckCorruption();
17246 switch(localRes)
17247 {
17248 case VK_ERROR_FEATURE_NOT_PRESENT:
17249 break;
17250 case VK_SUCCESS:
17251 finalRes = VK_SUCCESS;
17252 break;
17253 default:
17254 return localRes;
17255 }
17256 }
17257 }
17258
17259 // Process custom pools.
17260 {
17261 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17262 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17263 {
17264 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17265 {
17266 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17267 switch(localRes)
17268 {
17269 case VK_ERROR_FEATURE_NOT_PRESENT:
17270 break;
17271 case VK_SUCCESS:
17272 finalRes = VK_SUCCESS;
17273 break;
17274 default:
17275 return localRes;
17276 }
17277 }
17278 }
17279 }
17280
17281 return finalRes;
17282 }
17283
CreateLostAllocation(VmaAllocation * pAllocation)17284 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17285 {
17286 *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17287 (*pAllocation)->InitLost();
17288 }
17289
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)17290 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17291 {
17292 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17293
17294 // HeapSizeLimit is in effect for this heap.
17295 if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17296 {
17297 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17298 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17299 for(;;)
17300 {
17301 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17302 if(blockBytesAfterAllocation > heapSize)
17303 {
17304 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17305 }
17306 if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17307 {
17308 break;
17309 }
17310 }
17311 }
17312 else
17313 {
17314 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17315 }
17316
17317 // VULKAN CALL vkAllocateMemory.
17318 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17319
17320 if(res == VK_SUCCESS)
17321 {
17322 #if VMA_MEMORY_BUDGET
17323 ++m_Budget.m_OperationsSinceBudgetFetch;
17324 #endif
17325
17326 // Informative callback.
17327 if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17328 {
17329 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17330 }
17331 }
17332 else
17333 {
17334 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17335 }
17336
17337 return res;
17338 }
17339
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)17340 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17341 {
17342 // Informative callback.
17343 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17344 {
17345 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17346 }
17347
17348 // VULKAN CALL vkFreeMemory.
17349 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17350
17351 m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17352 }
17353
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)17354 VkResult VmaAllocator_T::BindVulkanBuffer(
17355 VkDeviceMemory memory,
17356 VkDeviceSize memoryOffset,
17357 VkBuffer buffer,
17358 const void* pNext)
17359 {
17360 if(pNext != VMA_NULL)
17361 {
17362 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17363 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17364 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17365 {
17366 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17367 bindBufferMemoryInfo.pNext = pNext;
17368 bindBufferMemoryInfo.buffer = buffer;
17369 bindBufferMemoryInfo.memory = memory;
17370 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17371 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17372 }
17373 else
17374 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17375 {
17376 return VK_ERROR_EXTENSION_NOT_PRESENT;
17377 }
17378 }
17379 else
17380 {
17381 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17382 }
17383 }
17384
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17385 VkResult VmaAllocator_T::BindVulkanImage(
17386 VkDeviceMemory memory,
17387 VkDeviceSize memoryOffset,
17388 VkImage image,
17389 const void* pNext)
17390 {
17391 if(pNext != VMA_NULL)
17392 {
17393 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17394 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17395 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17396 {
17397 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17398 bindBufferMemoryInfo.pNext = pNext;
17399 bindBufferMemoryInfo.image = image;
17400 bindBufferMemoryInfo.memory = memory;
17401 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17402 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17403 }
17404 else
17405 #endif // #if VMA_BIND_MEMORY2
17406 {
17407 return VK_ERROR_EXTENSION_NOT_PRESENT;
17408 }
17409 }
17410 else
17411 {
17412 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17413 }
17414 }
17415
Map(VmaAllocation hAllocation,void ** ppData)17416 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17417 {
17418 if(hAllocation->CanBecomeLost())
17419 {
17420 return VK_ERROR_MEMORY_MAP_FAILED;
17421 }
17422
17423 switch(hAllocation->GetType())
17424 {
17425 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17426 {
17427 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17428 char *pBytes = VMA_NULL;
17429 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17430 if(res == VK_SUCCESS)
17431 {
17432 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17433 hAllocation->BlockAllocMap();
17434 }
17435 return res;
17436 }
17437 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17438 return hAllocation->DedicatedAllocMap(this, ppData);
17439 default:
17440 VMA_ASSERT(0);
17441 return VK_ERROR_MEMORY_MAP_FAILED;
17442 }
17443 }
17444
Unmap(VmaAllocation hAllocation)17445 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17446 {
17447 switch(hAllocation->GetType())
17448 {
17449 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17450 {
17451 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17452 hAllocation->BlockAllocUnmap();
17453 pBlock->Unmap(this, 1);
17454 }
17455 break;
17456 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17457 hAllocation->DedicatedAllocUnmap(this);
17458 break;
17459 default:
17460 VMA_ASSERT(0);
17461 }
17462 }
17463
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17464 VkResult VmaAllocator_T::BindBufferMemory(
17465 VmaAllocation hAllocation,
17466 VkDeviceSize allocationLocalOffset,
17467 VkBuffer hBuffer,
17468 const void* pNext)
17469 {
17470 VkResult res = VK_SUCCESS;
17471 switch(hAllocation->GetType())
17472 {
17473 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17474 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17475 break;
17476 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17477 {
17478 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17479 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17480 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17481 break;
17482 }
17483 default:
17484 VMA_ASSERT(0);
17485 }
17486 return res;
17487 }
17488
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17489 VkResult VmaAllocator_T::BindImageMemory(
17490 VmaAllocation hAllocation,
17491 VkDeviceSize allocationLocalOffset,
17492 VkImage hImage,
17493 const void* pNext)
17494 {
17495 VkResult res = VK_SUCCESS;
17496 switch(hAllocation->GetType())
17497 {
17498 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17499 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17500 break;
17501 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17502 {
17503 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17504 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17505 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17506 if (res == VK_SUCCESS) {
17507 pBlock->SetBindCompleteFlag(true);
17508 }
17509 break;
17510 }
17511 default:
17512 VMA_ASSERT(0);
17513 }
17514 return res;
17515 }
17516
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17517 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17518 VmaAllocation hAllocation,
17519 VkDeviceSize offset, VkDeviceSize size,
17520 VMA_CACHE_OPERATION op)
17521 {
17522 VkResult res = VK_SUCCESS;
17523
17524 VkMappedMemoryRange memRange = {};
17525 if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17526 {
17527 switch(op)
17528 {
17529 case VMA_CACHE_FLUSH:
17530 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17531 break;
17532 case VMA_CACHE_INVALIDATE:
17533 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17534 break;
17535 default:
17536 VMA_ASSERT(0);
17537 }
17538 }
17539 // else: Just ignore this call.
17540 return res;
17541 }
17542
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17543 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17544 uint32_t allocationCount,
17545 const VmaAllocation* allocations,
17546 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17547 VMA_CACHE_OPERATION op)
17548 {
17549 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17550 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17551 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17552
17553 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17554 {
17555 const VmaAllocation alloc = allocations[allocIndex];
17556 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17557 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17558 VkMappedMemoryRange newRange;
17559 if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17560 {
17561 ranges.push_back(newRange);
17562 }
17563 }
17564
17565 VkResult res = VK_SUCCESS;
17566 if(!ranges.empty())
17567 {
17568 switch(op)
17569 {
17570 case VMA_CACHE_FLUSH:
17571 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17572 break;
17573 case VMA_CACHE_INVALIDATE:
17574 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17575 break;
17576 default:
17577 VMA_ASSERT(0);
17578 }
17579 }
17580 // else: Just ignore this call.
17581 return res;
17582 }
17583
FreeDedicatedMemory(const VmaAllocation allocation)17584 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17585 {
17586 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17587
17588 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17589 {
17590 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17591 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17592 VMA_ASSERT(pDedicatedAllocations);
17593 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17594 VMA_ASSERT(success);
17595 }
17596
17597 VkDeviceMemory hMemory = allocation->GetMemory();
17598
17599 /*
17600 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17601 before vkFreeMemory.
17602
17603 if(allocation->GetMappedData() != VMA_NULL)
17604 {
17605 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17606 }
17607 */
17608
17609 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17610
17611 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17612 }
17613
CalculateGpuDefragmentationMemoryTypeBits()17614 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17615 {
17616 VkBufferCreateInfo dummyBufCreateInfo;
17617 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17618
17619 uint32_t memoryTypeBits = 0;
17620
17621 // Create buffer.
17622 VkBuffer buf = VK_NULL_HANDLE;
17623 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17624 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17625 if(res == VK_SUCCESS)
17626 {
17627 // Query for supported memory types.
17628 VkMemoryRequirements memReq;
17629 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17630 memoryTypeBits = memReq.memoryTypeBits;
17631
17632 // Destroy buffer.
17633 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17634 }
17635
17636 return memoryTypeBits;
17637 }
17638
CalculateGlobalMemoryTypeBits()17639 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17640 {
17641 // Make sure memory information is already fetched.
17642 VMA_ASSERT(GetMemoryTypeCount() > 0);
17643
17644 uint32_t memoryTypeBits = UINT32_MAX;
17645
17646 if(!m_UseAmdDeviceCoherentMemory)
17647 {
17648 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17649 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17650 {
17651 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17652 {
17653 memoryTypeBits &= ~(1u << memTypeIndex);
17654 }
17655 }
17656 }
17657
17658 return memoryTypeBits;
17659 }
17660
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17661 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17662 VmaAllocation allocation,
17663 VkDeviceSize offset, VkDeviceSize size,
17664 VkMappedMemoryRange& outRange) const
17665 {
17666 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17667 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17668 {
17669 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17670 const VkDeviceSize allocationSize = allocation->GetSize();
17671 VMA_ASSERT(offset <= allocationSize);
17672
17673 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17674 outRange.pNext = VMA_NULL;
17675 outRange.memory = allocation->GetMemory();
17676
17677 switch(allocation->GetType())
17678 {
17679 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17680 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17681 if(size == VK_WHOLE_SIZE)
17682 {
17683 outRange.size = allocationSize - outRange.offset;
17684 }
17685 else
17686 {
17687 VMA_ASSERT(offset + size <= allocationSize);
17688 outRange.size = VMA_MIN(
17689 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17690 allocationSize - outRange.offset);
17691 }
17692 break;
17693 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17694 {
17695 // 1. Still within this allocation.
17696 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17697 if(size == VK_WHOLE_SIZE)
17698 {
17699 size = allocationSize - offset;
17700 }
17701 else
17702 {
17703 VMA_ASSERT(offset + size <= allocationSize);
17704 }
17705 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17706
17707 // 2. Adjust to whole block.
17708 const VkDeviceSize allocationOffset = allocation->GetOffset();
17709 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17710 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17711 outRange.offset += allocationOffset;
17712 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17713
17714 break;
17715 }
17716 default:
17717 VMA_ASSERT(0);
17718 }
17719 return true;
17720 }
17721 return false;
17722 }
17723
17724 #if VMA_MEMORY_BUDGET
17725
UpdateVulkanBudget()17726 void VmaAllocator_T::UpdateVulkanBudget()
17727 {
17728 VMA_ASSERT(m_UseExtMemoryBudget);
17729
17730 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17731
17732 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17733 VmaPnextChainPushFront(&memProps, &budgetProps);
17734
17735 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17736
17737 {
17738 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17739
17740 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17741 {
17742 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17743 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17744 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17745
17746 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17747 if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17748 {
17749 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17750 }
17751 else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17752 {
17753 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17754 }
17755 if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17756 {
17757 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17758 }
17759 }
17760 m_Budget.m_OperationsSinceBudgetFetch = 0;
17761 }
17762 }
17763
17764 #endif // #if VMA_MEMORY_BUDGET
17765
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17766 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17767 {
17768 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17769 !hAllocation->CanBecomeLost() &&
17770 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17771 {
17772 void* pData = VMA_NULL;
17773 VkResult res = Map(hAllocation, &pData);
17774 if(res == VK_SUCCESS)
17775 {
17776 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17777 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17778 Unmap(hAllocation);
17779 }
17780 else
17781 {
17782 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17783 }
17784 }
17785 }
17786
GetGpuDefragmentationMemoryTypeBits()17787 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17788 {
17789 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17790 if(memoryTypeBits == UINT32_MAX)
17791 {
17792 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17793 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17794 }
17795 return memoryTypeBits;
17796 }
17797
17798 #if VMA_STATS_STRING_ENABLED
17799
PrintDetailedMap(VmaJsonWriter & json)17800 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17801 {
17802 bool dedicatedAllocationsStarted = false;
17803 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17804 {
17805 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17806 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17807 VMA_ASSERT(pDedicatedAllocVector);
17808 if(pDedicatedAllocVector->empty() == false)
17809 {
17810 if(dedicatedAllocationsStarted == false)
17811 {
17812 dedicatedAllocationsStarted = true;
17813 json.WriteString("DedicatedAllocations");
17814 json.BeginObject();
17815 }
17816
17817 json.BeginString("Type ");
17818 json.ContinueString(memTypeIndex);
17819 json.EndString();
17820
17821 json.BeginArray();
17822
17823 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17824 {
17825 json.BeginObject(true);
17826 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17827 hAlloc->PrintParameters(json);
17828 json.EndObject();
17829 }
17830
17831 json.EndArray();
17832 }
17833 }
17834 if(dedicatedAllocationsStarted)
17835 {
17836 json.EndObject();
17837 }
17838
17839 {
17840 bool allocationsStarted = false;
17841 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17842 {
17843 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17844 {
17845 if(allocationsStarted == false)
17846 {
17847 allocationsStarted = true;
17848 json.WriteString("DefaultPools");
17849 json.BeginObject();
17850 }
17851
17852 json.BeginString("Type ");
17853 json.ContinueString(memTypeIndex);
17854 json.EndString();
17855
17856 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17857 }
17858 }
17859 if(allocationsStarted)
17860 {
17861 json.EndObject();
17862 }
17863 }
17864
17865 // Custom pools
17866 {
17867 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17868 const size_t poolCount = m_Pools.size();
17869 if(poolCount > 0)
17870 {
17871 json.WriteString("Pools");
17872 json.BeginObject();
17873 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17874 {
17875 json.BeginString();
17876 json.ContinueString(m_Pools[poolIndex]->GetId());
17877 json.EndString();
17878
17879 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17880 }
17881 json.EndObject();
17882 }
17883 }
17884 }
17885
17886 #endif // #if VMA_STATS_STRING_ENABLED
17887
17888 ////////////////////////////////////////////////////////////////////////////////
17889 // Public interface
17890
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17891 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17892 const VmaAllocatorCreateInfo* pCreateInfo,
17893 VmaAllocator* pAllocator)
17894 {
17895 VMA_ASSERT(pCreateInfo && pAllocator);
17896 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17897 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17898 VMA_DEBUG_LOG("vmaCreateAllocator");
17899 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17900 return (*pAllocator)->Init(pCreateInfo);
17901 }
17902
vmaDestroyAllocator(VmaAllocator allocator)17903 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17904 VmaAllocator allocator)
17905 {
17906 if(allocator != VK_NULL_HANDLE)
17907 {
17908 VMA_DEBUG_LOG("vmaDestroyAllocator");
17909 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17910 vma_delete(&allocationCallbacks, allocator);
17911 }
17912 }
17913
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17914 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17915 {
17916 VMA_ASSERT(allocator && pAllocatorInfo);
17917 pAllocatorInfo->instance = allocator->m_hInstance;
17918 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17919 pAllocatorInfo->device = allocator->m_hDevice;
17920 }
17921
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17922 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17923 VmaAllocator allocator,
17924 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17925 {
17926 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17927 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17928 }
17929
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17930 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17931 VmaAllocator allocator,
17932 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17933 {
17934 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17935 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17936 }
17937
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17938 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17939 VmaAllocator allocator,
17940 uint32_t memoryTypeIndex,
17941 VkMemoryPropertyFlags* pFlags)
17942 {
17943 VMA_ASSERT(allocator && pFlags);
17944 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17945 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17946 }
17947
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17948 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17949 VmaAllocator allocator,
17950 uint32_t frameIndex)
17951 {
17952 VMA_ASSERT(allocator);
17953 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17954
17955 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17956
17957 allocator->SetCurrentFrameIndex(frameIndex);
17958 }
17959
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17960 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17961 VmaAllocator allocator,
17962 VmaStats* pStats)
17963 {
17964 VMA_ASSERT(allocator && pStats);
17965 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17966 allocator->CalculateStats(pStats);
17967 }
17968
vmaFreeEmptyBlock(VmaAllocator allocator)17969 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
17970 VmaAllocator allocator)
17971 {
17972 VMA_ASSERT(allocator);
17973 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17974 allocator->FreeEmptyBlock();
17975 }
17976
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17977 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17978 VmaAllocator allocator,
17979 VmaBudget* pBudget)
17980 {
17981 VMA_ASSERT(allocator && pBudget);
17982 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17983 allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17984 }
17985
17986 #if VMA_STATS_STRING_ENABLED
17987
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17988 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17989 VmaAllocator allocator,
17990 char** ppStatsString,
17991 VkBool32 detailedMap)
17992 {
17993 VMA_ASSERT(allocator && ppStatsString);
17994 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17995
17996 VmaStringBuilder sb(allocator);
17997 {
17998 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17999 json.BeginObject();
18000
18001 VmaBudget budget[VK_MAX_MEMORY_HEAPS];
18002 allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
18003
18004 VmaStats stats;
18005 allocator->CalculateStats(&stats);
18006
18007 json.WriteString("Total");
18008 VmaPrintStatInfo(json, stats.total);
18009
18010 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18011 {
18012 json.BeginString("Heap ");
18013 json.ContinueString(heapIndex);
18014 json.EndString();
18015 json.BeginObject();
18016
18017 json.WriteString("Size");
18018 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18019
18020 json.WriteString("Flags");
18021 json.BeginArray(true);
18022 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18023 {
18024 json.WriteString("DEVICE_LOCAL");
18025 }
18026 json.EndArray();
18027
18028 json.WriteString("Budget");
18029 json.BeginObject();
18030 {
18031 json.WriteString("BlockBytes");
18032 json.WriteNumber(budget[heapIndex].blockBytes);
18033 json.WriteString("AllocationBytes");
18034 json.WriteNumber(budget[heapIndex].allocationBytes);
18035 json.WriteString("Usage");
18036 json.WriteNumber(budget[heapIndex].usage);
18037 json.WriteString("Budget");
18038 json.WriteNumber(budget[heapIndex].budget);
18039 }
18040 json.EndObject();
18041
18042 if(stats.memoryHeap[heapIndex].blockCount > 0)
18043 {
18044 json.WriteString("Stats");
18045 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18046 }
18047
18048 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18049 {
18050 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18051 {
18052 json.BeginString("Type ");
18053 json.ContinueString(typeIndex);
18054 json.EndString();
18055
18056 json.BeginObject();
18057
18058 json.WriteString("Flags");
18059 json.BeginArray(true);
18060 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18061 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18062 {
18063 json.WriteString("DEVICE_LOCAL");
18064 }
18065 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18066 {
18067 json.WriteString("HOST_VISIBLE");
18068 }
18069 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18070 {
18071 json.WriteString("HOST_COHERENT");
18072 }
18073 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18074 {
18075 json.WriteString("HOST_CACHED");
18076 }
18077 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18078 {
18079 json.WriteString("LAZILY_ALLOCATED");
18080 }
18081 if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18082 {
18083 json.WriteString(" PROTECTED");
18084 }
18085 if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18086 {
18087 json.WriteString(" DEVICE_COHERENT");
18088 }
18089 if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18090 {
18091 json.WriteString(" DEVICE_UNCACHED");
18092 }
18093 json.EndArray();
18094
18095 if(stats.memoryType[typeIndex].blockCount > 0)
18096 {
18097 json.WriteString("Stats");
18098 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18099 }
18100
18101 json.EndObject();
18102 }
18103 }
18104
18105 json.EndObject();
18106 }
18107 if(detailedMap == VK_TRUE)
18108 {
18109 allocator->PrintDetailedMap(json);
18110 }
18111
18112 json.EndObject();
18113 }
18114
18115 const size_t len = sb.GetLength();
18116 char* const pChars = vma_new_array(allocator, char, len + 1);
18117 if(len > 0)
18118 {
18119 memcpy(pChars, sb.GetData(), len);
18120 }
18121 pChars[len] = '\0';
18122 *ppStatsString = pChars;
18123 }
18124
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)18125 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18126 VmaAllocator allocator,
18127 char* pStatsString)
18128 {
18129 if(pStatsString != VMA_NULL)
18130 {
18131 VMA_ASSERT(allocator);
18132 size_t len = strlen(pStatsString);
18133 vma_delete_array(allocator, pStatsString, len + 1);
18134 }
18135 }
18136
18137 #endif // #if VMA_STATS_STRING_ENABLED
18138
18139 /*
18140 This function is not protected by any mutex because it just reads immutable data.
18141 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18142 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18143 VmaAllocator allocator,
18144 uint32_t memoryTypeBits,
18145 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18146 uint32_t* pMemoryTypeIndex)
18147 {
18148 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18149 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18150 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18151
18152 memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18153
18154 if(pAllocationCreateInfo->memoryTypeBits != 0)
18155 {
18156 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18157 }
18158
18159 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18160 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18161 uint32_t notPreferredFlags = 0;
18162
18163 // Convert usage to requiredFlags and preferredFlags.
18164 switch(pAllocationCreateInfo->usage)
18165 {
18166 case VMA_MEMORY_USAGE_UNKNOWN:
18167 break;
18168 case VMA_MEMORY_USAGE_GPU_ONLY:
18169 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18170 {
18171 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18172 }
18173 break;
18174 case VMA_MEMORY_USAGE_CPU_ONLY:
18175 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18176 break;
18177 case VMA_MEMORY_USAGE_CPU_TO_GPU:
18178 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18179 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18180 {
18181 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18182 }
18183 break;
18184 case VMA_MEMORY_USAGE_GPU_TO_CPU:
18185 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18186 preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18187 break;
18188 case VMA_MEMORY_USAGE_CPU_COPY:
18189 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18190 break;
18191 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
18192 requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18193 break;
18194 default:
18195 VMA_ASSERT(0);
18196 break;
18197 }
18198
18199 // Avoid DEVICE_COHERENT unless explicitly requested.
18200 if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18201 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18202 {
18203 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18204 }
18205
18206 *pMemoryTypeIndex = UINT32_MAX;
18207 uint32_t minCost = UINT32_MAX;
18208 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18209 memTypeIndex < allocator->GetMemoryTypeCount();
18210 ++memTypeIndex, memTypeBit <<= 1)
18211 {
18212 // This memory type is acceptable according to memoryTypeBits bitmask.
18213 if((memTypeBit & memoryTypeBits) != 0)
18214 {
18215 const VkMemoryPropertyFlags currFlags =
18216 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18217 // This memory type contains requiredFlags.
18218 if((requiredFlags & ~currFlags) == 0)
18219 {
18220 // Calculate cost as number of bits from preferredFlags not present in this memory type.
18221 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18222 VmaCountBitsSet(currFlags & notPreferredFlags);
18223 // Remember memory type with lowest cost.
18224 if(currCost < minCost)
18225 {
18226 *pMemoryTypeIndex = memTypeIndex;
18227 if(currCost == 0)
18228 {
18229 return VK_SUCCESS;
18230 }
18231 minCost = currCost;
18232 }
18233 }
18234 }
18235 }
18236 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18237 }
18238
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18239 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18240 VmaAllocator allocator,
18241 const VkBufferCreateInfo* pBufferCreateInfo,
18242 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18243 uint32_t* pMemoryTypeIndex)
18244 {
18245 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18246 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18247 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18248 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18249
18250 const VkDevice hDev = allocator->m_hDevice;
18251 VkBuffer hBuffer = VK_NULL_HANDLE;
18252 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18253 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18254 if(res == VK_SUCCESS)
18255 {
18256 VkMemoryRequirements memReq = {};
18257 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18258 hDev, hBuffer, &memReq);
18259
18260 res = vmaFindMemoryTypeIndex(
18261 allocator,
18262 memReq.memoryTypeBits,
18263 pAllocationCreateInfo,
18264 pMemoryTypeIndex);
18265
18266 allocator->GetVulkanFunctions().vkDestroyBuffer(
18267 hDev, hBuffer, allocator->GetAllocationCallbacks());
18268 }
18269 return res;
18270 }
18271
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18272 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18273 VmaAllocator allocator,
18274 const VkImageCreateInfo* pImageCreateInfo,
18275 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18276 uint32_t* pMemoryTypeIndex)
18277 {
18278 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18279 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18280 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18281 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18282
18283 const VkDevice hDev = allocator->m_hDevice;
18284 VkImage hImage = VK_NULL_HANDLE;
18285 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18286 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18287 if(res == VK_SUCCESS)
18288 {
18289 VkMemoryRequirements memReq = {};
18290 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18291 hDev, hImage, &memReq);
18292
18293 res = vmaFindMemoryTypeIndex(
18294 allocator,
18295 memReq.memoryTypeBits,
18296 pAllocationCreateInfo,
18297 pMemoryTypeIndex);
18298
18299 allocator->GetVulkanFunctions().vkDestroyImage(
18300 hDev, hImage, allocator->GetAllocationCallbacks());
18301 }
18302 return res;
18303 }
18304
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)18305 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18306 VmaAllocator allocator,
18307 const VmaPoolCreateInfo* pCreateInfo,
18308 VmaPool* pPool)
18309 {
18310 VMA_ASSERT(allocator && pCreateInfo && pPool);
18311
18312 VMA_DEBUG_LOG("vmaCreatePool");
18313
18314 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18315
18316 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18317
18318 #if VMA_RECORDING_ENABLED
18319 if(allocator->GetRecorder() != VMA_NULL)
18320 {
18321 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18322 }
18323 #endif
18324
18325 return res;
18326 }
18327
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)18328 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18329 VmaAllocator allocator,
18330 VmaPool pool)
18331 {
18332 VMA_ASSERT(allocator);
18333
18334 if(pool == VK_NULL_HANDLE)
18335 {
18336 return;
18337 }
18338
18339 VMA_DEBUG_LOG("vmaDestroyPool");
18340
18341 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18342
18343 #if VMA_RECORDING_ENABLED
18344 if(allocator->GetRecorder() != VMA_NULL)
18345 {
18346 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18347 }
18348 #endif
18349
18350 allocator->DestroyPool(pool);
18351 }
18352
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)18353 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18354 VmaAllocator allocator,
18355 VmaPool pool,
18356 VmaPoolStats* pPoolStats)
18357 {
18358 VMA_ASSERT(allocator && pool && pPoolStats);
18359
18360 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18361
18362 allocator->GetPoolStats(pool, pPoolStats);
18363 }
18364
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)18365 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18366 VmaAllocator allocator,
18367 VmaPool pool,
18368 size_t* pLostAllocationCount)
18369 {
18370 VMA_ASSERT(allocator && pool);
18371
18372 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18373
18374 #if VMA_RECORDING_ENABLED
18375 if(allocator->GetRecorder() != VMA_NULL)
18376 {
18377 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18378 }
18379 #endif
18380
18381 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18382 }
18383
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)18384 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18385 {
18386 VMA_ASSERT(allocator && pool);
18387
18388 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18389
18390 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18391
18392 return allocator->CheckPoolCorruption(pool);
18393 }
18394
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18395 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18396 VmaAllocator allocator,
18397 VmaPool pool,
18398 const char** ppName)
18399 {
18400 VMA_ASSERT(allocator && pool && ppName);
18401
18402 VMA_DEBUG_LOG("vmaGetPoolName");
18403
18404 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18405
18406 *ppName = pool->GetName();
18407 }
18408
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18409 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18410 VmaAllocator allocator,
18411 VmaPool pool,
18412 const char* pName)
18413 {
18414 VMA_ASSERT(allocator && pool);
18415
18416 VMA_DEBUG_LOG("vmaSetPoolName");
18417
18418 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18419
18420 pool->SetName(pName);
18421
18422 #if VMA_RECORDING_ENABLED
18423 if(allocator->GetRecorder() != VMA_NULL)
18424 {
18425 allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18426 }
18427 #endif
18428 }
18429
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18430 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18431 VmaAllocator allocator,
18432 const VkMemoryRequirements* pVkMemoryRequirements,
18433 const VmaAllocationCreateInfo* pCreateInfo,
18434 VmaAllocation* pAllocation,
18435 VmaAllocationInfo* pAllocationInfo)
18436 {
18437 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18438
18439 VMA_DEBUG_LOG("vmaAllocateMemory");
18440
18441 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18442
18443 VkResult result = allocator->AllocateMemory(
18444 *pVkMemoryRequirements,
18445 false, // requiresDedicatedAllocation
18446 false, // prefersDedicatedAllocation
18447 VK_NULL_HANDLE, // dedicatedBuffer
18448 UINT32_MAX, // dedicatedBufferUsage
18449 VK_NULL_HANDLE, // dedicatedImage
18450 *pCreateInfo,
18451 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18452 1, // allocationCount
18453 pAllocation);
18454
18455 #if VMA_RECORDING_ENABLED
18456 if(allocator->GetRecorder() != VMA_NULL)
18457 {
18458 allocator->GetRecorder()->RecordAllocateMemory(
18459 allocator->GetCurrentFrameIndex(),
18460 *pVkMemoryRequirements,
18461 *pCreateInfo,
18462 *pAllocation);
18463 }
18464 #endif
18465
18466 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18467 {
18468 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18469 }
18470
18471 return result;
18472 }
18473
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18474 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18475 VmaAllocator allocator,
18476 const VkMemoryRequirements* pVkMemoryRequirements,
18477 const VmaAllocationCreateInfo* pCreateInfo,
18478 size_t allocationCount,
18479 VmaAllocation* pAllocations,
18480 VmaAllocationInfo* pAllocationInfo)
18481 {
18482 if(allocationCount == 0)
18483 {
18484 return VK_SUCCESS;
18485 }
18486
18487 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18488
18489 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18490
18491 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18492
18493 VkResult result = allocator->AllocateMemory(
18494 *pVkMemoryRequirements,
18495 false, // requiresDedicatedAllocation
18496 false, // prefersDedicatedAllocation
18497 VK_NULL_HANDLE, // dedicatedBuffer
18498 UINT32_MAX, // dedicatedBufferUsage
18499 VK_NULL_HANDLE, // dedicatedImage
18500 *pCreateInfo,
18501 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18502 allocationCount,
18503 pAllocations);
18504
18505 #if VMA_RECORDING_ENABLED
18506 if(allocator->GetRecorder() != VMA_NULL)
18507 {
18508 allocator->GetRecorder()->RecordAllocateMemoryPages(
18509 allocator->GetCurrentFrameIndex(),
18510 *pVkMemoryRequirements,
18511 *pCreateInfo,
18512 (uint64_t)allocationCount,
18513 pAllocations);
18514 }
18515 #endif
18516
18517 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18518 {
18519 for(size_t i = 0; i < allocationCount; ++i)
18520 {
18521 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18522 }
18523 }
18524
18525 return result;
18526 }
18527
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18528 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18529 VmaAllocator allocator,
18530 VkBuffer buffer,
18531 const VmaAllocationCreateInfo* pCreateInfo,
18532 VmaAllocation* pAllocation,
18533 VmaAllocationInfo* pAllocationInfo)
18534 {
18535 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18536
18537 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18538
18539 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18540
18541 VkMemoryRequirements vkMemReq = {};
18542 bool requiresDedicatedAllocation = false;
18543 bool prefersDedicatedAllocation = false;
18544 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18545 requiresDedicatedAllocation,
18546 prefersDedicatedAllocation);
18547
18548 VkResult result = allocator->AllocateMemory(
18549 vkMemReq,
18550 requiresDedicatedAllocation,
18551 prefersDedicatedAllocation,
18552 buffer, // dedicatedBuffer
18553 UINT32_MAX, // dedicatedBufferUsage
18554 VK_NULL_HANDLE, // dedicatedImage
18555 *pCreateInfo,
18556 VMA_SUBALLOCATION_TYPE_BUFFER,
18557 1, // allocationCount
18558 pAllocation);
18559
18560 #if VMA_RECORDING_ENABLED
18561 if(allocator->GetRecorder() != VMA_NULL)
18562 {
18563 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18564 allocator->GetCurrentFrameIndex(),
18565 vkMemReq,
18566 requiresDedicatedAllocation,
18567 prefersDedicatedAllocation,
18568 *pCreateInfo,
18569 *pAllocation);
18570 }
18571 #endif
18572
18573 if(pAllocationInfo && result == VK_SUCCESS)
18574 {
18575 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18576 }
18577
18578 return result;
18579 }
18580
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18581 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18582 VmaAllocator allocator,
18583 VkImage image,
18584 const VmaAllocationCreateInfo* pCreateInfo,
18585 VmaAllocation* pAllocation,
18586 VmaAllocationInfo* pAllocationInfo)
18587 {
18588 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18589
18590 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18591
18592 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18593
18594 VkMemoryRequirements vkMemReq = {};
18595 bool requiresDedicatedAllocation = false;
18596 bool prefersDedicatedAllocation = false;
18597 allocator->GetImageMemoryRequirements(image, vkMemReq,
18598 requiresDedicatedAllocation, prefersDedicatedAllocation);
18599
18600 VkResult result = allocator->AllocateMemory(
18601 vkMemReq,
18602 requiresDedicatedAllocation,
18603 prefersDedicatedAllocation,
18604 VK_NULL_HANDLE, // dedicatedBuffer
18605 UINT32_MAX, // dedicatedBufferUsage
18606 image, // dedicatedImage
18607 *pCreateInfo,
18608 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18609 1, // allocationCount
18610 pAllocation);
18611
18612 #if VMA_RECORDING_ENABLED
18613 if(allocator->GetRecorder() != VMA_NULL)
18614 {
18615 allocator->GetRecorder()->RecordAllocateMemoryForImage(
18616 allocator->GetCurrentFrameIndex(),
18617 vkMemReq,
18618 requiresDedicatedAllocation,
18619 prefersDedicatedAllocation,
18620 *pCreateInfo,
18621 *pAllocation);
18622 }
18623 #endif
18624
18625 if(pAllocationInfo && result == VK_SUCCESS)
18626 {
18627 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18628 }
18629
18630 return result;
18631 }
18632
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18633 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18634 VmaAllocator allocator,
18635 VmaAllocation allocation)
18636 {
18637 VMA_ASSERT(allocator);
18638
18639 if(allocation == VK_NULL_HANDLE)
18640 {
18641 return;
18642 }
18643
18644 VMA_DEBUG_LOG("vmaFreeMemory");
18645
18646 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18647
18648 #if VMA_RECORDING_ENABLED
18649 if(allocator->GetRecorder() != VMA_NULL)
18650 {
18651 allocator->GetRecorder()->RecordFreeMemory(
18652 allocator->GetCurrentFrameIndex(),
18653 allocation);
18654 }
18655 #endif
18656
18657 allocator->FreeMemory(
18658 1, // allocationCount
18659 &allocation);
18660 }
18661
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18662 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18663 VmaAllocator allocator,
18664 size_t allocationCount,
18665 const VmaAllocation* pAllocations)
18666 {
18667 if(allocationCount == 0)
18668 {
18669 return;
18670 }
18671
18672 VMA_ASSERT(allocator);
18673
18674 VMA_DEBUG_LOG("vmaFreeMemoryPages");
18675
18676 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18677
18678 #if VMA_RECORDING_ENABLED
18679 if(allocator->GetRecorder() != VMA_NULL)
18680 {
18681 allocator->GetRecorder()->RecordFreeMemoryPages(
18682 allocator->GetCurrentFrameIndex(),
18683 (uint64_t)allocationCount,
18684 pAllocations);
18685 }
18686 #endif
18687
18688 allocator->FreeMemory(allocationCount, pAllocations);
18689 }
18690
18691 // OH ISSUE: VMA preAlloc
vmaAllocateReservedMemoryForImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,const VmaAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,VmaAllocationInfo * VMA_NULLABLE pAllocationInfo)18692 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
18693 VmaAllocator VMA_NOT_NULL allocator,
18694 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
18695 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
18696 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
18697 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo)
18698 {
18699 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18700 VMA_DEBUG_LOG("vmaAllocateReservedMemoryForImage");
18701 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18702
18703 VkMemoryRequirements vkMemReq = {};
18704 bool requiresDedicatedAllocation = false;
18705 bool prefersDedicatedAllocation = false;
18706 allocator->GetImageMemoryRequirements(image, vkMemReq,
18707 requiresDedicatedAllocation, prefersDedicatedAllocation);
18708
18709 VkResult result = allocator->AllocateReservedMemory(
18710 vkMemReq,
18711 requiresDedicatedAllocation,
18712 prefersDedicatedAllocation,
18713 VK_NULL_HANDLE, // dedicatedBuffer
18714 UINT32_MAX, // dedicatedBufferUsage
18715 image, // dedicatedImage
18716 *pCreateInfo,
18717 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18718 1, // allocationCount
18719 pAllocation);
18720
18721 if(pAllocationInfo && result == VK_SUCCESS)
18722 {
18723 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18724 }
18725
18726 return result;
18727 }
18728
vmaGetNewBlockStats(VmaAllocation allocation,bool * pStats)18729 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
18730 VmaAllocation allocation,
18731 bool* pStats)
18732 {
18733 VMA_ASSERT(allocation);
18734 VMA_DEBUG_LOG("vmaGetNewBlockStats");
18735 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18736
18737 if (pStats != NULL) {
18738 *pStats = allocation->IsNewBlockFlag();
18739 }
18740 return VK_SUCCESS;
18741 }
18742
vmaClearNewBlockStats(VmaAllocation allocation)18743 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
18744 VmaAllocation allocation)
18745 {
18746 VMA_ASSERT(allocation);
18747 VMA_DEBUG_LOG("vmaClearNewBlockStats");
18748 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18749
18750 allocation->ClearNewBlockFlag();
18751 return VK_SUCCESS;
18752 }
18753
vmaSwapReservedBlock(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18754 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
18755 VmaAllocator allocator,
18756 VkImage image,
18757 const VmaAllocationCreateInfo* pCreateInfo,
18758 VmaAllocation* pAllocation,
18759 VmaAllocationInfo* pAllocationInfo)
18760 {
18761 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18762 VMA_DEBUG_LOG("vmaSwapReservedBlock");
18763 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18764
18765 return allocator->SwapReservedBlock(image, pCreateInfo, pAllocation, pAllocationInfo);
18766 }
18767
vmaFreeReservedMemory(VmaAllocator allocator,VmaAllocation allocation)18768 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
18769 VmaAllocator allocator,
18770 VmaAllocation allocation)
18771 {
18772 VMA_ASSERT(allocator);
18773 if (allocation == VK_NULL_HANDLE)
18774 {
18775 return;
18776 }
18777 VMA_DEBUG_LOG("vmaFreeReservedMemory");
18778 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18779
18780 allocator->FreeReservedMemory(
18781 1, // allocationCount
18782 &allocation);
18783 }
18784
vmaGetPreAllocBlockSize(VmaAllocator allocator,uint32_t * pStats)18785 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(VmaAllocator allocator, uint32_t* pStats)
18786 {
18787 VMA_ASSERT(allocator);
18788 VMA_DEBUG_LOG("vmaGetPreAllocBlockSize");
18789
18790 if (pStats != NULL) {
18791 *pStats = allocator->GetPreAllocBlockSize();
18792 }
18793 return VK_SUCCESS;
18794 }
18795
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18796 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18797 VmaAllocator allocator,
18798 VmaAllocation allocation,
18799 VkDeviceSize newSize)
18800 {
18801 VMA_ASSERT(allocator && allocation);
18802
18803 VMA_DEBUG_LOG("vmaResizeAllocation");
18804
18805 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18806
18807 return allocator->ResizeAllocation(allocation, newSize);
18808 }
18809
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18810 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18811 VmaAllocator allocator,
18812 VmaAllocation allocation,
18813 VmaAllocationInfo* pAllocationInfo)
18814 {
18815 VMA_ASSERT(allocator && allocation && pAllocationInfo);
18816
18817 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18818
18819 #if VMA_RECORDING_ENABLED
18820 if(allocator->GetRecorder() != VMA_NULL)
18821 {
18822 allocator->GetRecorder()->RecordGetAllocationInfo(
18823 allocator->GetCurrentFrameIndex(),
18824 allocation);
18825 }
18826 #endif
18827
18828 allocator->GetAllocationInfo(allocation, pAllocationInfo);
18829 }
18830
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18831 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18832 VmaAllocator allocator,
18833 VmaAllocation allocation)
18834 {
18835 VMA_ASSERT(allocator && allocation);
18836
18837 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18838
18839 #if VMA_RECORDING_ENABLED
18840 if(allocator->GetRecorder() != VMA_NULL)
18841 {
18842 allocator->GetRecorder()->RecordTouchAllocation(
18843 allocator->GetCurrentFrameIndex(),
18844 allocation);
18845 }
18846 #endif
18847
18848 return allocator->TouchAllocation(allocation);
18849 }
18850
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18851 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18852 VmaAllocator allocator,
18853 VmaAllocation allocation,
18854 void* pUserData)
18855 {
18856 VMA_ASSERT(allocator && allocation);
18857
18858 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18859
18860 allocation->SetUserData(allocator, pUserData);
18861
18862 #if VMA_RECORDING_ENABLED
18863 if(allocator->GetRecorder() != VMA_NULL)
18864 {
18865 allocator->GetRecorder()->RecordSetAllocationUserData(
18866 allocator->GetCurrentFrameIndex(),
18867 allocation,
18868 pUserData);
18869 }
18870 #endif
18871 }
18872
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18873 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18874 VmaAllocator allocator,
18875 VmaAllocation* pAllocation)
18876 {
18877 VMA_ASSERT(allocator && pAllocation);
18878
18879 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18880
18881 allocator->CreateLostAllocation(pAllocation);
18882
18883 #if VMA_RECORDING_ENABLED
18884 if(allocator->GetRecorder() != VMA_NULL)
18885 {
18886 allocator->GetRecorder()->RecordCreateLostAllocation(
18887 allocator->GetCurrentFrameIndex(),
18888 *pAllocation);
18889 }
18890 #endif
18891 }
18892
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18893 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18894 VmaAllocator allocator,
18895 VmaAllocation allocation,
18896 void** ppData)
18897 {
18898 VMA_ASSERT(allocator && allocation && ppData);
18899
18900 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18901
18902 VkResult res = allocator->Map(allocation, ppData);
18903
18904 #if VMA_RECORDING_ENABLED
18905 if(allocator->GetRecorder() != VMA_NULL)
18906 {
18907 allocator->GetRecorder()->RecordMapMemory(
18908 allocator->GetCurrentFrameIndex(),
18909 allocation);
18910 }
18911 #endif
18912
18913 return res;
18914 }
18915
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18916 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18917 VmaAllocator allocator,
18918 VmaAllocation allocation)
18919 {
18920 VMA_ASSERT(allocator && allocation);
18921
18922 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18923
18924 #if VMA_RECORDING_ENABLED
18925 if(allocator->GetRecorder() != VMA_NULL)
18926 {
18927 allocator->GetRecorder()->RecordUnmapMemory(
18928 allocator->GetCurrentFrameIndex(),
18929 allocation);
18930 }
18931 #endif
18932
18933 allocator->Unmap(allocation);
18934 }
18935
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18936 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18937 {
18938 VMA_ASSERT(allocator && allocation);
18939
18940 VMA_DEBUG_LOG("vmaFlushAllocation");
18941
18942 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18943
18944 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18945
18946 #if VMA_RECORDING_ENABLED
18947 if(allocator->GetRecorder() != VMA_NULL)
18948 {
18949 allocator->GetRecorder()->RecordFlushAllocation(
18950 allocator->GetCurrentFrameIndex(),
18951 allocation, offset, size);
18952 }
18953 #endif
18954
18955 return res;
18956 }
18957
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18958 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18959 {
18960 VMA_ASSERT(allocator && allocation);
18961
18962 VMA_DEBUG_LOG("vmaInvalidateAllocation");
18963
18964 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18965
18966 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18967
18968 #if VMA_RECORDING_ENABLED
18969 if(allocator->GetRecorder() != VMA_NULL)
18970 {
18971 allocator->GetRecorder()->RecordInvalidateAllocation(
18972 allocator->GetCurrentFrameIndex(),
18973 allocation, offset, size);
18974 }
18975 #endif
18976
18977 return res;
18978 }
18979
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18980 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18981 VmaAllocator allocator,
18982 uint32_t allocationCount,
18983 const VmaAllocation* allocations,
18984 const VkDeviceSize* offsets,
18985 const VkDeviceSize* sizes)
18986 {
18987 VMA_ASSERT(allocator);
18988
18989 if(allocationCount == 0)
18990 {
18991 return VK_SUCCESS;
18992 }
18993
18994 VMA_ASSERT(allocations);
18995
18996 VMA_DEBUG_LOG("vmaFlushAllocations");
18997
18998 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18999
19000 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
19001
19002 #if VMA_RECORDING_ENABLED
19003 if(allocator->GetRecorder() != VMA_NULL)
19004 {
19005 //TODO
19006 }
19007 #endif
19008
19009 return res;
19010 }
19011
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)19012 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19013 VmaAllocator allocator,
19014 uint32_t allocationCount,
19015 const VmaAllocation* allocations,
19016 const VkDeviceSize* offsets,
19017 const VkDeviceSize* sizes)
19018 {
19019 VMA_ASSERT(allocator);
19020
19021 if(allocationCount == 0)
19022 {
19023 return VK_SUCCESS;
19024 }
19025
19026 VMA_ASSERT(allocations);
19027
19028 VMA_DEBUG_LOG("vmaInvalidateAllocations");
19029
19030 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19031
19032 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19033
19034 #if VMA_RECORDING_ENABLED
19035 if(allocator->GetRecorder() != VMA_NULL)
19036 {
19037 //TODO
19038 }
19039 #endif
19040
19041 return res;
19042 }
19043
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)19044 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19045 {
19046 VMA_ASSERT(allocator);
19047
19048 VMA_DEBUG_LOG("vmaCheckCorruption");
19049
19050 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19051
19052 return allocator->CheckCorruption(memoryTypeBits);
19053 }
19054
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)19055 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19056 VmaAllocator allocator,
19057 const VmaAllocation* pAllocations,
19058 size_t allocationCount,
19059 VkBool32* pAllocationsChanged,
19060 const VmaDefragmentationInfo *pDefragmentationInfo,
19061 VmaDefragmentationStats* pDefragmentationStats)
19062 {
19063 // Deprecated interface, reimplemented using new one.
19064
19065 VmaDefragmentationInfo2 info2 = {};
19066 info2.allocationCount = (uint32_t)allocationCount;
19067 info2.pAllocations = pAllocations;
19068 info2.pAllocationsChanged = pAllocationsChanged;
19069 if(pDefragmentationInfo != VMA_NULL)
19070 {
19071 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19072 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19073 }
19074 else
19075 {
19076 info2.maxCpuAllocationsToMove = UINT32_MAX;
19077 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19078 }
19079 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19080
19081 VmaDefragmentationContext ctx;
19082 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19083 if(res == VK_NOT_READY)
19084 {
19085 res = vmaDefragmentationEnd( allocator, ctx);
19086 }
19087 return res;
19088 }
19089
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)19090 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19091 VmaAllocator allocator,
19092 const VmaDefragmentationInfo2* pInfo,
19093 VmaDefragmentationStats* pStats,
19094 VmaDefragmentationContext *pContext)
19095 {
19096 VMA_ASSERT(allocator && pInfo && pContext);
19097
19098 // Degenerate case: Nothing to defragment.
19099 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19100 {
19101 return VK_SUCCESS;
19102 }
19103
19104 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19105 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19106 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19107 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19108
19109 VMA_DEBUG_LOG("vmaDefragmentationBegin");
19110
19111 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19112
19113 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19114
19115 #if VMA_RECORDING_ENABLED
19116 if(allocator->GetRecorder() != VMA_NULL)
19117 {
19118 allocator->GetRecorder()->RecordDefragmentationBegin(
19119 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19120 }
19121 #endif
19122
19123 return res;
19124 }
19125
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)19126 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19127 VmaAllocator allocator,
19128 VmaDefragmentationContext context)
19129 {
19130 VMA_ASSERT(allocator);
19131
19132 VMA_DEBUG_LOG("vmaDefragmentationEnd");
19133
19134 if(context != VK_NULL_HANDLE)
19135 {
19136 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19137
19138 #if VMA_RECORDING_ENABLED
19139 if(allocator->GetRecorder() != VMA_NULL)
19140 {
19141 allocator->GetRecorder()->RecordDefragmentationEnd(
19142 allocator->GetCurrentFrameIndex(), context);
19143 }
19144 #endif
19145
19146 return allocator->DefragmentationEnd(context);
19147 }
19148 else
19149 {
19150 return VK_SUCCESS;
19151 }
19152 }
19153
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)19154 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19155 VmaAllocator allocator,
19156 VmaDefragmentationContext context,
19157 VmaDefragmentationPassInfo* pInfo
19158 )
19159 {
19160 VMA_ASSERT(allocator);
19161 VMA_ASSERT(pInfo);
19162 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
19163
19164 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19165
19166 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19167
19168 if(context == VK_NULL_HANDLE)
19169 {
19170 pInfo->moveCount = 0;
19171 return VK_SUCCESS;
19172 }
19173
19174 return allocator->DefragmentationPassBegin(pInfo, context);
19175 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)19176 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19177 VmaAllocator allocator,
19178 VmaDefragmentationContext context)
19179 {
19180 VMA_ASSERT(allocator);
19181
19182 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19183 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19184
19185 if(context == VK_NULL_HANDLE)
19186 return VK_SUCCESS;
19187
19188 return allocator->DefragmentationPassEnd(context);
19189 }
19190
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)19191 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19192 VmaAllocator allocator,
19193 VmaAllocation allocation,
19194 VkBuffer buffer)
19195 {
19196 VMA_ASSERT(allocator && allocation && buffer);
19197
19198 VMA_DEBUG_LOG("vmaBindBufferMemory");
19199
19200 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19201
19202 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19203 }
19204
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)19205 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19206 VmaAllocator allocator,
19207 VmaAllocation allocation,
19208 VkDeviceSize allocationLocalOffset,
19209 VkBuffer buffer,
19210 const void* pNext)
19211 {
19212 VMA_ASSERT(allocator && allocation && buffer);
19213
19214 VMA_DEBUG_LOG("vmaBindBufferMemory2");
19215
19216 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19217
19218 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19219 }
19220
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)19221 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19222 VmaAllocator allocator,
19223 VmaAllocation allocation,
19224 VkImage image)
19225 {
19226 VMA_ASSERT(allocator && allocation && image);
19227
19228 VMA_DEBUG_LOG("vmaBindImageMemory");
19229
19230 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19231
19232 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19233 }
19234
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)19235 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19236 VmaAllocator allocator,
19237 VmaAllocation allocation,
19238 VkDeviceSize allocationLocalOffset,
19239 VkImage image,
19240 const void* pNext)
19241 {
19242 VMA_ASSERT(allocator && allocation && image);
19243
19244 VMA_DEBUG_LOG("vmaBindImageMemory2");
19245
19246 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19247
19248 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19249 }
19250
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19251 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19252 VmaAllocator allocator,
19253 const VkBufferCreateInfo* pBufferCreateInfo,
19254 const VmaAllocationCreateInfo* pAllocationCreateInfo,
19255 VkBuffer* pBuffer,
19256 VmaAllocation* pAllocation,
19257 VmaAllocationInfo* pAllocationInfo)
19258 {
19259 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19260
19261 if(pBufferCreateInfo->size == 0)
19262 {
19263 return VK_ERROR_VALIDATION_FAILED_EXT;
19264 }
19265 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19266 !allocator->m_UseKhrBufferDeviceAddress)
19267 {
19268 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
19269 return VK_ERROR_VALIDATION_FAILED_EXT;
19270 }
19271
19272 VMA_DEBUG_LOG("vmaCreateBuffer");
19273
19274 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19275
19276 *pBuffer = VK_NULL_HANDLE;
19277 *pAllocation = VK_NULL_HANDLE;
19278
19279 // 1. Create VkBuffer.
19280 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19281 allocator->m_hDevice,
19282 pBufferCreateInfo,
19283 allocator->GetAllocationCallbacks(),
19284 pBuffer);
19285 if(res >= 0)
19286 {
19287 // 2. vkGetBufferMemoryRequirements.
19288 VkMemoryRequirements vkMemReq = {};
19289 bool requiresDedicatedAllocation = false;
19290 bool prefersDedicatedAllocation = false;
19291 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19292 requiresDedicatedAllocation, prefersDedicatedAllocation);
19293
19294 // 3. Allocate memory using allocator.
19295 res = allocator->AllocateMemory(
19296 vkMemReq,
19297 requiresDedicatedAllocation,
19298 prefersDedicatedAllocation,
19299 *pBuffer, // dedicatedBuffer
19300 pBufferCreateInfo->usage, // dedicatedBufferUsage
19301 VK_NULL_HANDLE, // dedicatedImage
19302 *pAllocationCreateInfo,
19303 VMA_SUBALLOCATION_TYPE_BUFFER,
19304 1, // allocationCount
19305 pAllocation);
19306
19307 #if VMA_RECORDING_ENABLED
19308 if(allocator->GetRecorder() != VMA_NULL)
19309 {
19310 allocator->GetRecorder()->RecordCreateBuffer(
19311 allocator->GetCurrentFrameIndex(),
19312 *pBufferCreateInfo,
19313 *pAllocationCreateInfo,
19314 *pAllocation);
19315 }
19316 #endif
19317
19318 if(res >= 0)
19319 {
19320 // 3. Bind buffer with memory.
19321 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19322 {
19323 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19324 }
19325 if(res >= 0)
19326 {
19327 // All steps succeeded.
19328 #if VMA_STATS_STRING_ENABLED
19329 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19330 #endif
19331 if(pAllocationInfo != VMA_NULL)
19332 {
19333 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19334 }
19335
19336 return VK_SUCCESS;
19337 }
19338 allocator->FreeMemory(
19339 1, // allocationCount
19340 pAllocation);
19341 *pAllocation = VK_NULL_HANDLE;
19342 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19343 *pBuffer = VK_NULL_HANDLE;
19344 return res;
19345 }
19346 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19347 *pBuffer = VK_NULL_HANDLE;
19348 return res;
19349 }
19350 return res;
19351 }
19352
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)19353 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19354 VmaAllocator allocator,
19355 VkBuffer buffer,
19356 VmaAllocation allocation)
19357 {
19358 VMA_ASSERT(allocator);
19359
19360 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19361 {
19362 return;
19363 }
19364
19365 VMA_DEBUG_LOG("vmaDestroyBuffer");
19366
19367 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19368
19369 #if VMA_RECORDING_ENABLED
19370 if(allocator->GetRecorder() != VMA_NULL)
19371 {
19372 allocator->GetRecorder()->RecordDestroyBuffer(
19373 allocator->GetCurrentFrameIndex(),
19374 allocation);
19375 }
19376 #endif
19377
19378 if(buffer != VK_NULL_HANDLE)
19379 {
19380 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19381 }
19382
19383 if(allocation != VK_NULL_HANDLE)
19384 {
19385 allocator->FreeMemory(
19386 1, // allocationCount
19387 &allocation);
19388 }
19389 }
19390
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19391 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19392 VmaAllocator allocator,
19393 const VkImageCreateInfo* pImageCreateInfo,
19394 const VmaAllocationCreateInfo* pAllocationCreateInfo,
19395 VkImage* pImage,
19396 VmaAllocation* pAllocation,
19397 VmaAllocationInfo* pAllocationInfo)
19398 {
19399 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19400
19401 if(pImageCreateInfo->extent.width == 0 ||
19402 pImageCreateInfo->extent.height == 0 ||
19403 pImageCreateInfo->extent.depth == 0 ||
19404 pImageCreateInfo->mipLevels == 0 ||
19405 pImageCreateInfo->arrayLayers == 0)
19406 {
19407 return VK_ERROR_VALIDATION_FAILED_EXT;
19408 }
19409
19410 VMA_DEBUG_LOG("vmaCreateImage");
19411
19412 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19413
19414 *pImage = VK_NULL_HANDLE;
19415 *pAllocation = VK_NULL_HANDLE;
19416
19417 // 1. Create VkImage.
19418 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19419 allocator->m_hDevice,
19420 pImageCreateInfo,
19421 allocator->GetAllocationCallbacks(),
19422 pImage);
19423 if(res >= 0)
19424 {
19425 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19426 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19427 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19428
19429 // 2. Allocate memory using allocator.
19430 VkMemoryRequirements vkMemReq = {};
19431 bool requiresDedicatedAllocation = false;
19432 bool prefersDedicatedAllocation = false;
19433 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19434 requiresDedicatedAllocation, prefersDedicatedAllocation);
19435
19436 res = allocator->AllocateMemory(
19437 vkMemReq,
19438 requiresDedicatedAllocation,
19439 prefersDedicatedAllocation,
19440 VK_NULL_HANDLE, // dedicatedBuffer
19441 UINT32_MAX, // dedicatedBufferUsage
19442 *pImage, // dedicatedImage
19443 *pAllocationCreateInfo,
19444 suballocType,
19445 1, // allocationCount
19446 pAllocation);
19447
19448 #if VMA_RECORDING_ENABLED
19449 if(allocator->GetRecorder() != VMA_NULL)
19450 {
19451 allocator->GetRecorder()->RecordCreateImage(
19452 allocator->GetCurrentFrameIndex(),
19453 *pImageCreateInfo,
19454 *pAllocationCreateInfo,
19455 *pAllocation);
19456 }
19457 #endif
19458
19459 if(res >= 0)
19460 {
19461 // 3. Bind image with memory.
19462 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19463 {
19464 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19465 }
19466 if(res >= 0)
19467 {
19468 // All steps succeeded.
19469 #if VMA_STATS_STRING_ENABLED
19470 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19471 #endif
19472 if(pAllocationInfo != VMA_NULL)
19473 {
19474 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19475 }
19476
19477 return VK_SUCCESS;
19478 }
19479 allocator->FreeMemory(
19480 1, // allocationCount
19481 pAllocation);
19482 *pAllocation = VK_NULL_HANDLE;
19483 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19484 *pImage = VK_NULL_HANDLE;
19485 return res;
19486 }
19487 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19488 *pImage = VK_NULL_HANDLE;
19489 return res;
19490 }
19491 return res;
19492 }
19493
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)19494 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19495 VmaAllocator allocator,
19496 VkImage image,
19497 VmaAllocation allocation)
19498 {
19499 VMA_ASSERT(allocator);
19500
19501 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19502 {
19503 return;
19504 }
19505
19506 VMA_DEBUG_LOG("vmaDestroyImage");
19507
19508 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19509
19510 #if VMA_RECORDING_ENABLED
19511 if(allocator->GetRecorder() != VMA_NULL)
19512 {
19513 allocator->GetRecorder()->RecordDestroyImage(
19514 allocator->GetCurrentFrameIndex(),
19515 allocation);
19516 }
19517 #endif
19518
19519 if(image != VK_NULL_HANDLE)
19520 {
19521 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19522 }
19523 if(allocation != VK_NULL_HANDLE)
19524 {
19525 allocator->FreeMemory(
19526 1, // allocationCount
19527 &allocation);
19528 }
19529 }
19530
19531 // OH ISSUE: VMA preAlloc
vmaCreateFakeImage(VmaAllocator allocator,VkImage * pImage)19532 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
19533 VmaAllocator allocator,
19534 VkImage* pImage)
19535 {
19536 VMA_ASSERT(allocator && pImage);
19537 VMA_DEBUG_LOG("vmaCreateFakeImage");
19538 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19539
19540 *pImage = VK_NULL_HANDLE;
19541 const VkImageCreateInfo imageCreateInfo = {
19542 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
19543 nullptr, // pNext
19544 0, // VkImageCreateFlags
19545 VK_IMAGE_TYPE_2D, // VkImageType
19546 VK_FORMAT_R8G8B8A8_UNORM, // VkFormat
19547 { 10, 10, 1 }, // VkExtent3D
19548 1, // mipLevels
19549 1, // arrayLayers
19550 VK_SAMPLE_COUNT_1_BIT, // samples
19551 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling
19552 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // VkImageUsageFlags
19553 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode
19554 0, // queueFamilyCount
19555 nullptr, // pQueueFamilyIndices
19556 VK_IMAGE_LAYOUT_UNDEFINED // initialLayout
19557 };
19558
19559 // 1. Create VkImage.
19560 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19561 allocator->m_hDevice,
19562 &imageCreateInfo,
19563 nullptr,
19564 pImage);
19565 return res;
19566 }
19567
vmaDestroyFakeImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image)19568 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
19569 VmaAllocator VMA_NOT_NULL allocator,
19570 VkImage VMA_NULLABLE_NON_DISPATCHABLE image)
19571 {
19572 VMA_ASSERT(allocator);
19573 if(image == VK_NULL_HANDLE)
19574 {
19575 return;
19576 }
19577 VMA_DEBUG_LOG("vmaDestroyFakeImage");
19578 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19579
19580 if(image != VK_NULL_HANDLE)
19581 {
19582 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19583 }
19584 }
19585
19586 #endif // #ifdef VMA_IMPLEMENTATION
19587