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 friend void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2);
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 * blockVector1,VmaBlockVector * blockVector2)12722 void SwapLastBlock(VmaBlockVector* blockVector1, VmaBlockVector* blockVector2)
12723 {
12724 VmaDeviceMemoryBlock* lastBlock1 = blockVector1->m_Blocks.back();
12725 blockVector1->m_Blocks.pop_back();
12726 VmaDeviceMemoryBlock* lastBlock2 = blockVector2->m_Blocks.back();
12727 blockVector2->m_Blocks.pop_back();
12728
12729 blockVector1->m_Blocks.push_back(lastBlock2);
12730 blockVector2->m_Blocks.push_back(lastBlock1);
12731 }
12732
AllocatePage(uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12733 VkResult VmaBlockVector::AllocatePage(
12734 uint32_t currentFrameIndex,
12735 VkDeviceSize size,
12736 VkDeviceSize alignment,
12737 const VmaAllocationCreateInfo& createInfo,
12738 VmaSuballocationType suballocType,
12739 VmaAllocation* pAllocation)
12740 {
12741 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12742 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
12743 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12744 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12745
12746 VkDeviceSize freeMemory;
12747 {
12748 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12749 VmaBudget heapBudget = {};
12750 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
12751 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12752 }
12753
12754 const bool canFallbackToDedicated = !IsCustomPool();
12755 const bool canCreateNewBlock =
12756 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12757 (m_Blocks.size() < m_MaxBlockCount) &&
12758 (freeMemory >= size || !canFallbackToDedicated);
12759 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12760
12761 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
12762 // Which in turn is available only when maxBlockCount = 1.
12763 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
12764 {
12765 canMakeOtherLost = false;
12766 }
12767
12768 // Upper address can only be used with linear allocator and within single memory block.
12769 if(isUpperAddress &&
12770 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12771 {
12772 return VK_ERROR_FEATURE_NOT_PRESENT;
12773 }
12774
12775 // Validate strategy.
12776 switch(strategy)
12777 {
12778 case 0:
12779 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
12780 break;
12781 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
12782 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
12783 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
12784 break;
12785 default:
12786 return VK_ERROR_FEATURE_NOT_PRESENT;
12787 }
12788
12789 // Early reject: requested allocation size is larger that maximum block size for this block vector.
12790 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12791 {
12792 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12793 }
12794
12795 /*
12796 Under certain condition, this whole section can be skipped for optimization, so
12797 we move on directly to trying to allocate with canMakeOtherLost. That's the case
12798 e.g. for custom pools with linear algorithm.
12799 */
12800 if(!canMakeOtherLost || canCreateNewBlock)
12801 {
12802 // 1. Search existing allocations. Try to allocate without making other allocations lost.
12803 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
12804 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
12805
12806 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12807 {
12808 // Use only last block.
12809 if(!m_Blocks.empty())
12810 {
12811 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12812 VMA_ASSERT(pCurrBlock);
12813 VkResult res = AllocateFromBlock(
12814 pCurrBlock,
12815 currentFrameIndex,
12816 size,
12817 alignment,
12818 allocFlagsCopy,
12819 createInfo.pUserData,
12820 suballocType,
12821 strategy,
12822 pAllocation);
12823 if(res == VK_SUCCESS)
12824 {
12825 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12826 return VK_SUCCESS;
12827 }
12828 }
12829 }
12830 else
12831 {
12832 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12833 {
12834 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12835 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12836 {
12837 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12838 VMA_ASSERT(pCurrBlock);
12839 VkResult res = AllocateFromBlock(
12840 pCurrBlock,
12841 currentFrameIndex,
12842 size,
12843 alignment,
12844 allocFlagsCopy,
12845 createInfo.pUserData,
12846 suballocType,
12847 strategy,
12848 pAllocation);
12849 if(res == VK_SUCCESS)
12850 {
12851 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12852 return VK_SUCCESS;
12853 }
12854 }
12855 }
12856 else // WORST_FIT, FIRST_FIT
12857 {
12858 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12859 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12860 {
12861 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12862 VMA_ASSERT(pCurrBlock);
12863 VkResult res = AllocateFromBlock(
12864 pCurrBlock,
12865 currentFrameIndex,
12866 size,
12867 alignment,
12868 allocFlagsCopy,
12869 createInfo.pUserData,
12870 suballocType,
12871 strategy,
12872 pAllocation);
12873 if(res == VK_SUCCESS)
12874 {
12875 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12876 return VK_SUCCESS;
12877 }
12878 }
12879 }
12880 }
12881
12882 // 2. Try to create new block.
12883 if(canCreateNewBlock)
12884 {
12885 // Calculate optimal size for new block.
12886 VkDeviceSize newBlockSize = m_PreferredBlockSize;
12887 uint32_t newBlockSizeShift = 0;
12888 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12889
12890 if(!m_ExplicitBlockSize)
12891 {
12892 // Allocate 1/8, 1/4, 1/2 as first blocks.
12893 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12894 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12895 {
12896 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12897 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12898 {
12899 newBlockSize = smallerNewBlockSize;
12900 ++newBlockSizeShift;
12901 }
12902 else
12903 {
12904 break;
12905 }
12906 }
12907 }
12908
12909 size_t newBlockIndex = 0;
12910 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12911 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12912 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12913 if(!m_ExplicitBlockSize)
12914 {
12915 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12916 {
12917 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12918 if(smallerNewBlockSize >= size)
12919 {
12920 newBlockSize = smallerNewBlockSize;
12921 ++newBlockSizeShift;
12922 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12923 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12924 }
12925 else
12926 {
12927 break;
12928 }
12929 }
12930 }
12931
12932 if(res == VK_SUCCESS)
12933 {
12934 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12935 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12936
12937 res = AllocateFromBlock(
12938 pBlock,
12939 currentFrameIndex,
12940 size,
12941 alignment,
12942 allocFlagsCopy,
12943 createInfo.pUserData,
12944 suballocType,
12945 strategy,
12946 pAllocation);
12947 if(res == VK_SUCCESS)
12948 {
12949 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12950 return VK_SUCCESS;
12951 }
12952 else
12953 {
12954 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12955 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12956 }
12957 }
12958 }
12959 }
12960
12961 // 3. Try to allocate from existing blocks with making other allocations lost.
12962 if(canMakeOtherLost)
12963 {
12964 uint32_t tryIndex = 0;
12965 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
12966 {
12967 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
12968 VmaAllocationRequest bestRequest = {};
12969 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
12970
12971 // 1. Search existing allocations.
12972 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
12973 {
12974 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12975 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
12976 {
12977 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12978 VMA_ASSERT(pCurrBlock);
12979 VmaAllocationRequest currRequest = {};
12980 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
12981 currentFrameIndex,
12982 m_FrameInUseCount,
12983 m_BufferImageGranularity,
12984 size,
12985 alignment,
12986 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
12987 suballocType,
12988 canMakeOtherLost,
12989 strategy,
12990 &currRequest))
12991 {
12992 const VkDeviceSize currRequestCost = currRequest.CalcCost();
12993 if(pBestRequestBlock == VMA_NULL ||
12994 currRequestCost < bestRequestCost)
12995 {
12996 pBestRequestBlock = pCurrBlock;
12997 bestRequest = currRequest;
12998 bestRequestCost = currRequestCost;
12999
13000 if(bestRequestCost == 0)
13001 {
13002 break;
13003 }
13004 }
13005 }
13006 }
13007 }
13008 else // WORST_FIT, FIRST_FIT
13009 {
13010 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
13011 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13012 {
13013 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
13014 VMA_ASSERT(pCurrBlock);
13015 VmaAllocationRequest currRequest = {};
13016 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
13017 currentFrameIndex,
13018 m_FrameInUseCount,
13019 m_BufferImageGranularity,
13020 size,
13021 alignment,
13022 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
13023 suballocType,
13024 canMakeOtherLost,
13025 strategy,
13026 &currRequest))
13027 {
13028 const VkDeviceSize currRequestCost = currRequest.CalcCost();
13029 if(pBestRequestBlock == VMA_NULL ||
13030 currRequestCost < bestRequestCost ||
13031 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13032 {
13033 pBestRequestBlock = pCurrBlock;
13034 bestRequest = currRequest;
13035 bestRequestCost = currRequestCost;
13036
13037 if(bestRequestCost == 0 ||
13038 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
13039 {
13040 break;
13041 }
13042 }
13043 }
13044 }
13045 }
13046
13047 if(pBestRequestBlock != VMA_NULL)
13048 {
13049 if(mapped)
13050 {
13051 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
13052 if(res != VK_SUCCESS)
13053 {
13054 return res;
13055 }
13056 }
13057
13058 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
13059 currentFrameIndex,
13060 m_FrameInUseCount,
13061 &bestRequest))
13062 {
13063 // Allocate from this pBlock.
13064 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13065 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
13066 UpdateHasEmptyBlock();
13067 (*pAllocation)->InitBlockAllocation(
13068 pBestRequestBlock,
13069 bestRequest.offset,
13070 alignment,
13071 size,
13072 m_MemoryTypeIndex,
13073 suballocType,
13074 mapped,
13075 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13076 IsNewBlockFlag());
13077 ClearNewBlockFlag();
13078 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
13079 VMA_DEBUG_LOG(" Returned from existing block");
13080 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
13081 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13082 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13083 {
13084 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13085 }
13086 if(IsCorruptionDetectionEnabled())
13087 {
13088 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
13089 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13090 }
13091 return VK_SUCCESS;
13092 }
13093 // else: Some allocations must have been touched while we are here. Next try.
13094 }
13095 else
13096 {
13097 // Could not find place in any of the blocks - break outer loop.
13098 break;
13099 }
13100 }
13101 /* Maximum number of tries exceeded - a very unlike event when many other
13102 threads are simultaneously touching allocations making it impossible to make
13103 lost at the same time as we try to allocate. */
13104 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
13105 {
13106 return VK_ERROR_TOO_MANY_OBJECTS;
13107 }
13108 }
13109
13110 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13111 }
13112
Free(const VmaAllocation hAllocation)13113 void VmaBlockVector::Free(
13114 const VmaAllocation hAllocation)
13115 {
13116 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
13117
13118 bool budgetExceeded = false;
13119 {
13120 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
13121 VmaBudget heapBudget = {};
13122 m_hAllocator->GetBudget(&heapBudget, heapIndex, 1);
13123 budgetExceeded = heapBudget.usage >= heapBudget.budget;
13124 }
13125
13126 // Scope for lock.
13127 {
13128 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13129
13130 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13131
13132 if(IsCorruptionDetectionEnabled())
13133 {
13134 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13135 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13136 }
13137
13138 if(hAllocation->IsPersistentMap())
13139 {
13140 pBlock->Unmap(m_hAllocator, 1);
13141 }
13142
13143 pBlock->m_pMetadata->Free(hAllocation);
13144 VMA_HEAVY_ASSERT(pBlock->Validate());
13145
13146 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13147
13148 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
13149 // pBlock became empty after this deallocation.
13150 if(pBlock->m_pMetadata->IsEmpty())
13151 {
13152 // Already has empty block. We don't want to have two, so delete this one.
13153 if((m_HasEmptyBlock || budgetExceeded) && canDeleteBlock)
13154 {
13155 pBlockToDelete = pBlock;
13156 Remove(pBlock);
13157 }
13158 // else: We now have an empty block - leave it.
13159 }
13160 // pBlock didn't become empty, but we have another empty block - find and free that one.
13161 // (This is optional, heuristics.)
13162 else if(m_HasEmptyBlock && canDeleteBlock)
13163 {
13164 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
13165 if(pLastBlock->m_pMetadata->IsEmpty())
13166 {
13167 pBlockToDelete = pLastBlock;
13168 m_Blocks.pop_back();
13169 }
13170 }
13171
13172 UpdateHasEmptyBlock();
13173 IncrementallySortBlocks();
13174 }
13175
13176 // Destruction of a free block. Deferred until this point, outside of mutex
13177 // lock, for performance reason.
13178 if(pBlockToDelete != VMA_NULL)
13179 {
13180 VMA_DEBUG_LOG(" Deleted empty block");
13181 pBlockToDelete->Destroy(m_hAllocator);
13182 vma_delete(m_hAllocator, pBlockToDelete);
13183 }
13184 }
13185
13186 // OH ISSUE: VMA preAlloc
FreeReserved(const VmaAllocation hAllocation)13187 void VmaBlockVector::FreeReserved(
13188 const VmaAllocation hAllocation)
13189 {
13190 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13191
13192 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
13193
13194 if(IsCorruptionDetectionEnabled())
13195 {
13196 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
13197 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
13198 }
13199
13200 if(hAllocation->IsPersistentMap())
13201 {
13202 pBlock->Unmap(m_hAllocator, 1);
13203 }
13204
13205 pBlock->m_pMetadata->Free(hAllocation);
13206 VMA_HEAVY_ASSERT(pBlock->Validate());
13207
13208 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
13209 IncrementallySortBlocks();
13210 }
13211
CalcMaxBlockSize()13212 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
13213 {
13214 VkDeviceSize result = 0;
13215 for(size_t i = m_Blocks.size(); i--; )
13216 {
13217 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
13218 if(result >= m_PreferredBlockSize)
13219 {
13220 break;
13221 }
13222 }
13223 return result;
13224 }
13225
Remove(VmaDeviceMemoryBlock * pBlock)13226 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
13227 {
13228 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13229 {
13230 if(m_Blocks[blockIndex] == pBlock)
13231 {
13232 VmaVectorRemove(m_Blocks, blockIndex);
13233 return;
13234 }
13235 }
13236 VMA_ASSERT(0);
13237 }
13238
IncrementallySortBlocks()13239 void VmaBlockVector::IncrementallySortBlocks()
13240 {
13241 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
13242 {
13243 // Bubble sort only until first swap.
13244 for(size_t i = 1; i < m_Blocks.size(); ++i)
13245 {
13246 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
13247 {
13248 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
13249 return;
13250 }
13251 }
13252 }
13253 }
13254
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,uint32_t currentFrameIndex,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)13255 VkResult VmaBlockVector::AllocateFromBlock(
13256 VmaDeviceMemoryBlock* pBlock,
13257 uint32_t currentFrameIndex,
13258 VkDeviceSize size,
13259 VkDeviceSize alignment,
13260 VmaAllocationCreateFlags allocFlags,
13261 void* pUserData,
13262 VmaSuballocationType suballocType,
13263 uint32_t strategy,
13264 VmaAllocation* pAllocation)
13265 {
13266 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
13267 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
13268 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
13269 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
13270
13271 VmaAllocationRequest currRequest = {};
13272 if(pBlock->m_pMetadata->CreateAllocationRequest(
13273 currentFrameIndex,
13274 m_FrameInUseCount,
13275 m_BufferImageGranularity,
13276 size,
13277 alignment,
13278 isUpperAddress,
13279 suballocType,
13280 false, // canMakeOtherLost
13281 strategy,
13282 &currRequest))
13283 {
13284 // Allocate from pCurrBlock.
13285 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
13286
13287 if(mapped)
13288 {
13289 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
13290 if(res != VK_SUCCESS)
13291 {
13292 return res;
13293 }
13294 }
13295
13296 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(currentFrameIndex, isUserDataString);
13297 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
13298 UpdateHasEmptyBlock();
13299 (*pAllocation)->InitBlockAllocation(
13300 pBlock,
13301 currRequest.offset,
13302 alignment,
13303 size,
13304 m_MemoryTypeIndex,
13305 suballocType,
13306 mapped,
13307 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0,
13308 IsNewBlockFlag());
13309 ClearNewBlockFlag();
13310 VMA_HEAVY_ASSERT(pBlock->Validate());
13311 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
13312 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), size);
13313 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
13314 {
13315 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
13316 }
13317 if(IsCorruptionDetectionEnabled())
13318 {
13319 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
13320 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
13321 }
13322 return VK_SUCCESS;
13323 }
13324 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
13325 }
13326
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)13327 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
13328 {
13329 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
13330 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
13331 allocInfo.allocationSize = blockSize;
13332
13333 #if VMA_BUFFER_DEVICE_ADDRESS
13334 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
13335 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
13336 if(m_hAllocator->m_UseKhrBufferDeviceAddress)
13337 {
13338 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
13339 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
13340 }
13341 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
13342
13343 VkDeviceMemory mem = VK_NULL_HANDLE;
13344 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
13345 if(res < 0)
13346 {
13347 return res;
13348 }
13349
13350 // New VkDeviceMemory successfully created.
13351
13352 // Create new Allocation for it.
13353 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
13354 pBlock->Init(
13355 m_hAllocator,
13356 m_hParentPool,
13357 m_MemoryTypeIndex,
13358 mem,
13359 allocInfo.allocationSize,
13360 m_NextBlockId++,
13361 m_Algorithm);
13362 m_NewBlockFlag = true;
13363
13364 m_Blocks.push_back(pBlock);
13365 if(pNewBlockIndex != VMA_NULL)
13366 {
13367 *pNewBlockIndex = m_Blocks.size() - 1;
13368 }
13369
13370 return VK_SUCCESS;
13371 }
13372
ApplyDefragmentationMovesCpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,const VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves)13373 void VmaBlockVector::ApplyDefragmentationMovesCpu(
13374 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13375 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
13376 {
13377 const size_t blockCount = m_Blocks.size();
13378 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
13379
13380 enum BLOCK_FLAG
13381 {
13382 BLOCK_FLAG_USED = 0x00000001,
13383 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
13384 };
13385
13386 struct BlockInfo
13387 {
13388 uint32_t flags;
13389 void* pMappedData;
13390 };
13391 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
13392 blockInfo(blockCount, BlockInfo(), VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
13393 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
13394
13395 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13396 const size_t moveCount = moves.size();
13397 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13398 {
13399 const VmaDefragmentationMove& move = moves[moveIndex];
13400 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
13401 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
13402 }
13403
13404 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13405
13406 // Go over all blocks. Get mapped pointer or map if necessary.
13407 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13408 {
13409 BlockInfo& currBlockInfo = blockInfo[blockIndex];
13410 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13411 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
13412 {
13413 currBlockInfo.pMappedData = pBlock->GetMappedData();
13414 // It is not originally mapped - map it.
13415 if(currBlockInfo.pMappedData == VMA_NULL)
13416 {
13417 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
13418 if(pDefragCtx->res == VK_SUCCESS)
13419 {
13420 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
13421 }
13422 }
13423 }
13424 }
13425
13426 // Go over all moves. Do actual data transfer.
13427 if(pDefragCtx->res == VK_SUCCESS)
13428 {
13429 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
13430 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
13431
13432 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13433 {
13434 const VmaDefragmentationMove& move = moves[moveIndex];
13435
13436 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
13437 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
13438
13439 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
13440
13441 // Invalidate source.
13442 if(isNonCoherent)
13443 {
13444 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
13445 memRange.memory = pSrcBlock->GetDeviceMemory();
13446 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
13447 memRange.size = VMA_MIN(
13448 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
13449 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
13450 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13451 }
13452
13453 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
13454 memmove(
13455 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
13456 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
13457 static_cast<size_t>(move.size));
13458
13459 if(IsCorruptionDetectionEnabled())
13460 {
13461 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
13462 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
13463 }
13464
13465 // Flush destination.
13466 if(isNonCoherent)
13467 {
13468 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
13469 memRange.memory = pDstBlock->GetDeviceMemory();
13470 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
13471 memRange.size = VMA_MIN(
13472 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
13473 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
13474 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
13475 }
13476 }
13477 }
13478
13479 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
13480 // Regardless of pCtx->res == VK_SUCCESS.
13481 for(size_t blockIndex = blockCount; blockIndex--; )
13482 {
13483 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
13484 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
13485 {
13486 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13487 pBlock->Unmap(m_hAllocator, 1);
13488 }
13489 }
13490 }
13491
ApplyDefragmentationMovesGpu(class VmaBlockVectorDefragmentationContext * pDefragCtx,VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkCommandBuffer commandBuffer)13492 void VmaBlockVector::ApplyDefragmentationMovesGpu(
13493 class VmaBlockVectorDefragmentationContext* pDefragCtx,
13494 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
13495 VkCommandBuffer commandBuffer)
13496 {
13497 const size_t blockCount = m_Blocks.size();
13498
13499 pDefragCtx->blockContexts.resize(blockCount);
13500 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
13501
13502 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
13503 const size_t moveCount = moves.size();
13504 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13505 {
13506 const VmaDefragmentationMove& move = moves[moveIndex];
13507
13508 //if(move.type == VMA_ALLOCATION_TYPE_UNKNOWN)
13509 {
13510 // Old school move still require us to map the whole block
13511 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13512 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
13513 }
13514 }
13515
13516 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
13517
13518 // Go over all blocks. Create and bind buffer for whole block if necessary.
13519 {
13520 VkBufferCreateInfo bufCreateInfo;
13521 VmaFillGpuDefragmentationBufferCreateInfo(bufCreateInfo);
13522
13523 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
13524 {
13525 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
13526 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13527 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
13528 {
13529 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
13530 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
13531 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
13532 if(pDefragCtx->res == VK_SUCCESS)
13533 {
13534 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
13535 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
13536 }
13537 }
13538 }
13539 }
13540
13541 // Go over all moves. Post data transfer commands to command buffer.
13542 if(pDefragCtx->res == VK_SUCCESS)
13543 {
13544 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
13545 {
13546 const VmaDefragmentationMove& move = moves[moveIndex];
13547
13548 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
13549 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
13550
13551 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
13552
13553 VkBufferCopy region = {
13554 move.srcOffset,
13555 move.dstOffset,
13556 move.size };
13557 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
13558 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, ®ion);
13559 }
13560 }
13561
13562 // Save buffers to defrag context for later destruction.
13563 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
13564 {
13565 pDefragCtx->res = VK_NOT_READY;
13566 }
13567 }
13568
FreeEmptyBlocks(VmaDefragmentationStats * pDefragmentationStats)13569 void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
13570 {
13571 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
13572 {
13573 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
13574 if(pBlock->m_pMetadata->IsEmpty())
13575 {
13576 if(m_Blocks.size() > m_MinBlockCount)
13577 {
13578 if(pDefragmentationStats != VMA_NULL)
13579 {
13580 ++pDefragmentationStats->deviceMemoryBlocksFreed;
13581 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
13582 }
13583
13584 VmaVectorRemove(m_Blocks, blockIndex);
13585 pBlock->Destroy(m_hAllocator);
13586 vma_delete(m_hAllocator, pBlock);
13587 }
13588 else
13589 {
13590 break;
13591 }
13592 }
13593 }
13594 UpdateHasEmptyBlock();
13595 }
13596
UpdateHasEmptyBlock()13597 void VmaBlockVector::UpdateHasEmptyBlock()
13598 {
13599 m_HasEmptyBlock = false;
13600 for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
13601 {
13602 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
13603 if(pBlock->m_pMetadata->IsEmpty())
13604 {
13605 m_HasEmptyBlock = true;
13606 break;
13607 }
13608 }
13609 }
13610
13611 #if VMA_STATS_STRING_ENABLED
13612
PrintDetailedMap(class VmaJsonWriter & json)13613 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
13614 {
13615 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13616
13617 json.BeginObject();
13618
13619 if(IsCustomPool())
13620 {
13621 const char* poolName = m_hParentPool->GetName();
13622 if(poolName != VMA_NULL && poolName[0] != '\0')
13623 {
13624 json.WriteString("Name");
13625 json.WriteString(poolName);
13626 }
13627
13628 json.WriteString("MemoryTypeIndex");
13629 json.WriteNumber(m_MemoryTypeIndex);
13630
13631 json.WriteString("BlockSize");
13632 json.WriteNumber(m_PreferredBlockSize);
13633
13634 json.WriteString("BlockCount");
13635 json.BeginObject(true);
13636 if(m_MinBlockCount > 0)
13637 {
13638 json.WriteString("Min");
13639 json.WriteNumber((uint64_t)m_MinBlockCount);
13640 }
13641 if(m_MaxBlockCount < SIZE_MAX)
13642 {
13643 json.WriteString("Max");
13644 json.WriteNumber((uint64_t)m_MaxBlockCount);
13645 }
13646 json.WriteString("Cur");
13647 json.WriteNumber((uint64_t)m_Blocks.size());
13648 json.EndObject();
13649
13650 if(m_FrameInUseCount > 0)
13651 {
13652 json.WriteString("FrameInUseCount");
13653 json.WriteNumber(m_FrameInUseCount);
13654 }
13655
13656 if(m_Algorithm != 0)
13657 {
13658 json.WriteString("Algorithm");
13659 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
13660 }
13661 }
13662 else
13663 {
13664 json.WriteString("PreferredBlockSize");
13665 json.WriteNumber(m_PreferredBlockSize);
13666 }
13667
13668 json.WriteString("Blocks");
13669 json.BeginObject();
13670 for(size_t i = 0; i < m_Blocks.size(); ++i)
13671 {
13672 json.BeginString();
13673 json.ContinueString(m_Blocks[i]->GetId());
13674 json.EndString();
13675
13676 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
13677 }
13678 json.EndObject();
13679
13680 json.EndObject();
13681 }
13682
13683 #endif // #if VMA_STATS_STRING_ENABLED
13684
Defragment(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags,VkDeviceSize & maxCpuBytesToMove,uint32_t & maxCpuAllocationsToMove,VkDeviceSize & maxGpuBytesToMove,uint32_t & maxGpuAllocationsToMove,VkCommandBuffer commandBuffer)13685 void VmaBlockVector::Defragment(
13686 class VmaBlockVectorDefragmentationContext* pCtx,
13687 VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags,
13688 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
13689 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
13690 VkCommandBuffer commandBuffer)
13691 {
13692 pCtx->res = VK_SUCCESS;
13693
13694 const VkMemoryPropertyFlags memPropFlags =
13695 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
13696 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
13697
13698 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
13699 isHostVisible;
13700 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
13701 !IsCorruptionDetectionEnabled() &&
13702 ((1u << m_MemoryTypeIndex) & m_hAllocator->GetGpuDefragmentationMemoryTypeBits()) != 0;
13703
13704 // There are options to defragment this memory type.
13705 if(canDefragmentOnCpu || canDefragmentOnGpu)
13706 {
13707 bool defragmentOnGpu;
13708 // There is only one option to defragment this memory type.
13709 if(canDefragmentOnGpu != canDefragmentOnCpu)
13710 {
13711 defragmentOnGpu = canDefragmentOnGpu;
13712 }
13713 // Both options are available: Heuristics to choose the best one.
13714 else
13715 {
13716 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
13717 m_hAllocator->IsIntegratedGpu();
13718 }
13719
13720 bool overlappingMoveSupported = !defragmentOnGpu;
13721
13722 if(m_hAllocator->m_UseMutex)
13723 {
13724 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13725 {
13726 if(!m_Mutex.TryLockWrite())
13727 {
13728 pCtx->res = VK_ERROR_INITIALIZATION_FAILED;
13729 return;
13730 }
13731 }
13732 else
13733 {
13734 m_Mutex.LockWrite();
13735 pCtx->mutexLocked = true;
13736 }
13737 }
13738
13739 pCtx->Begin(overlappingMoveSupported, flags);
13740
13741 // Defragment.
13742
13743 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
13744 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
13745 pCtx->res = pCtx->GetAlgorithm()->Defragment(pCtx->defragmentationMoves, maxBytesToMove, maxAllocationsToMove, flags);
13746
13747 // Accumulate statistics.
13748 if(pStats != VMA_NULL)
13749 {
13750 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
13751 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
13752 pStats->bytesMoved += bytesMoved;
13753 pStats->allocationsMoved += allocationsMoved;
13754 VMA_ASSERT(bytesMoved <= maxBytesToMove);
13755 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
13756 if(defragmentOnGpu)
13757 {
13758 maxGpuBytesToMove -= bytesMoved;
13759 maxGpuAllocationsToMove -= allocationsMoved;
13760 }
13761 else
13762 {
13763 maxCpuBytesToMove -= bytesMoved;
13764 maxCpuAllocationsToMove -= allocationsMoved;
13765 }
13766 }
13767
13768 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
13769 {
13770 if(m_hAllocator->m_UseMutex)
13771 m_Mutex.UnlockWrite();
13772
13773 if(pCtx->res >= VK_SUCCESS && !pCtx->defragmentationMoves.empty())
13774 pCtx->res = VK_NOT_READY;
13775
13776 return;
13777 }
13778
13779 if(pCtx->res >= VK_SUCCESS)
13780 {
13781 if(defragmentOnGpu)
13782 {
13783 ApplyDefragmentationMovesGpu(pCtx, pCtx->defragmentationMoves, commandBuffer);
13784 }
13785 else
13786 {
13787 ApplyDefragmentationMovesCpu(pCtx, pCtx->defragmentationMoves);
13788 }
13789 }
13790 }
13791 }
13792
DefragmentationEnd(class VmaBlockVectorDefragmentationContext * pCtx,uint32_t flags,VmaDefragmentationStats * pStats)13793 void VmaBlockVector::DefragmentationEnd(
13794 class VmaBlockVectorDefragmentationContext* pCtx,
13795 uint32_t flags,
13796 VmaDefragmentationStats* pStats)
13797 {
13798 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL && m_hAllocator->m_UseMutex)
13799 {
13800 VMA_ASSERT(pCtx->mutexLocked == false);
13801
13802 // Incremental defragmentation doesn't hold the lock, so when we enter here we don't actually have any
13803 // lock protecting us. Since we mutate state here, we have to take the lock out now
13804 m_Mutex.LockWrite();
13805 pCtx->mutexLocked = true;
13806 }
13807
13808 // If the mutex isn't locked we didn't do any work and there is nothing to delete.
13809 if(pCtx->mutexLocked || !m_hAllocator->m_UseMutex)
13810 {
13811 // Destroy buffers.
13812 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--;)
13813 {
13814 VmaBlockDefragmentationContext &blockCtx = pCtx->blockContexts[blockIndex];
13815 if(blockCtx.hBuffer)
13816 {
13817 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
13818 }
13819 }
13820
13821 if(pCtx->res >= VK_SUCCESS)
13822 {
13823 FreeEmptyBlocks(pStats);
13824 }
13825 }
13826
13827 if(pCtx->mutexLocked)
13828 {
13829 VMA_ASSERT(m_hAllocator->m_UseMutex);
13830 m_Mutex.UnlockWrite();
13831 }
13832 }
13833
ProcessDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationPassMoveInfo * pMove,uint32_t maxMoves)13834 uint32_t VmaBlockVector::ProcessDefragmentations(
13835 class VmaBlockVectorDefragmentationContext *pCtx,
13836 VmaDefragmentationPassMoveInfo* pMove, uint32_t maxMoves)
13837 {
13838 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13839
13840 const uint32_t moveCount = std::min(uint32_t(pCtx->defragmentationMoves.size()) - pCtx->defragmentationMovesProcessed, maxMoves);
13841
13842 for(uint32_t i = 0; i < moveCount; ++ i)
13843 {
13844 VmaDefragmentationMove& move = pCtx->defragmentationMoves[pCtx->defragmentationMovesProcessed + i];
13845
13846 pMove->allocation = move.hAllocation;
13847 pMove->memory = move.pDstBlock->GetDeviceMemory();
13848 pMove->offset = move.dstOffset;
13849
13850 ++ pMove;
13851 }
13852
13853 pCtx->defragmentationMovesProcessed += moveCount;
13854
13855 return moveCount;
13856 }
13857
CommitDefragmentations(class VmaBlockVectorDefragmentationContext * pCtx,VmaDefragmentationStats * pStats)13858 void VmaBlockVector::CommitDefragmentations(
13859 class VmaBlockVectorDefragmentationContext *pCtx,
13860 VmaDefragmentationStats* pStats)
13861 {
13862 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13863
13864 for(uint32_t i = pCtx->defragmentationMovesCommitted; i < pCtx->defragmentationMovesProcessed; ++ i)
13865 {
13866 const VmaDefragmentationMove &move = pCtx->defragmentationMoves[i];
13867
13868 move.pSrcBlock->m_pMetadata->FreeAtOffset(move.srcOffset);
13869 move.hAllocation->ChangeBlockAllocation(m_hAllocator, move.pDstBlock, move.dstOffset);
13870 }
13871
13872 pCtx->defragmentationMovesCommitted = pCtx->defragmentationMovesProcessed;
13873 FreeEmptyBlocks(pStats);
13874 }
13875
CalcAllocationCount()13876 size_t VmaBlockVector::CalcAllocationCount() const
13877 {
13878 size_t result = 0;
13879 for(size_t i = 0; i < m_Blocks.size(); ++i)
13880 {
13881 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
13882 }
13883 return result;
13884 }
13885
IsBufferImageGranularityConflictPossible()13886 bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
13887 {
13888 if(m_BufferImageGranularity == 1)
13889 {
13890 return false;
13891 }
13892 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
13893 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
13894 {
13895 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
13896 VMA_ASSERT(m_Algorithm == 0);
13897 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
13898 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
13899 {
13900 return true;
13901 }
13902 }
13903 return false;
13904 }
13905
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)13906 void VmaBlockVector::MakePoolAllocationsLost(
13907 uint32_t currentFrameIndex,
13908 size_t* pLostAllocationCount)
13909 {
13910 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13911 size_t lostAllocationCount = 0;
13912 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13913 {
13914 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13915 VMA_ASSERT(pBlock);
13916 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
13917 }
13918 if(pLostAllocationCount != VMA_NULL)
13919 {
13920 *pLostAllocationCount = lostAllocationCount;
13921 }
13922 }
13923
CheckCorruption()13924 VkResult VmaBlockVector::CheckCorruption()
13925 {
13926 if(!IsCorruptionDetectionEnabled())
13927 {
13928 return VK_ERROR_FEATURE_NOT_PRESENT;
13929 }
13930
13931 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13932 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13933 {
13934 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13935 VMA_ASSERT(pBlock);
13936 VkResult res = pBlock->CheckCorruption(m_hAllocator);
13937 if(res != VK_SUCCESS)
13938 {
13939 return res;
13940 }
13941 }
13942 return VK_SUCCESS;
13943 }
13944
AddStats(VmaStats * pStats)13945 void VmaBlockVector::AddStats(VmaStats* pStats)
13946 {
13947 const uint32_t memTypeIndex = m_MemoryTypeIndex;
13948 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
13949
13950 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
13951
13952 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
13953 {
13954 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
13955 VMA_ASSERT(pBlock);
13956 VMA_HEAVY_ASSERT(pBlock->Validate());
13957 VmaStatInfo allocationStatInfo;
13958 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
13959 VmaAddStatInfo(pStats->total, allocationStatInfo);
13960 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
13961 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
13962 }
13963 }
13964
FreeEmptyBlock()13965 void VmaBlockVector::FreeEmptyBlock()
13966 {
13967 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
13968 FreeEmptyBlocks(VMA_NULL);
13969 }
13970
13971 ////////////////////////////////////////////////////////////////////////////////
13972 // VmaDefragmentationAlgorithm_Generic members definition
13973
VmaDefragmentationAlgorithm_Generic(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)13974 VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
13975 VmaAllocator hAllocator,
13976 VmaBlockVector* pBlockVector,
13977 uint32_t currentFrameIndex,
13978 bool overlappingMoveSupported) :
13979 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
13980 m_AllocationCount(0),
13981 m_AllAllocations(false),
13982 m_BytesMoved(0),
13983 m_AllocationsMoved(0),
13984 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
13985 {
13986 // Create block info for each block.
13987 const size_t blockCount = m_pBlockVector->m_Blocks.size();
13988 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13989 {
13990 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
13991 pBlockInfo->m_OriginalBlockIndex = blockIndex;
13992 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
13993 m_Blocks.push_back(pBlockInfo);
13994 }
13995
13996 // Sort them by m_pBlock pointer value.
13997 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
13998 }
13999
~VmaDefragmentationAlgorithm_Generic()14000 VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
14001 {
14002 for(size_t i = m_Blocks.size(); i--; )
14003 {
14004 vma_delete(m_hAllocator, m_Blocks[i]);
14005 }
14006 }
14007
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14008 void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14009 {
14010 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
14011 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
14012 {
14013 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
14014 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
14015 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
14016 {
14017 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
14018 (*it)->m_Allocations.push_back(allocInfo);
14019 }
14020 else
14021 {
14022 VMA_ASSERT(0);
14023 }
14024
14025 ++m_AllocationCount;
14026 }
14027 }
14028
DefragmentRound(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,bool freeOldAllocations)14029 VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
14030 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14031 VkDeviceSize maxBytesToMove,
14032 uint32_t maxAllocationsToMove,
14033 bool freeOldAllocations)
14034 {
14035 if(m_Blocks.empty())
14036 {
14037 return VK_SUCCESS;
14038 }
14039
14040 // This is a choice based on research.
14041 // Option 1:
14042 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
14043 // Option 2:
14044 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
14045 // Option 3:
14046 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
14047
14048 size_t srcBlockMinIndex = 0;
14049 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
14050 /*
14051 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
14052 {
14053 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
14054 if(blocksWithNonMovableCount > 0)
14055 {
14056 srcBlockMinIndex = blocksWithNonMovableCount - 1;
14057 }
14058 }
14059 */
14060
14061 size_t srcBlockIndex = m_Blocks.size() - 1;
14062 size_t srcAllocIndex = SIZE_MAX;
14063 for(;;)
14064 {
14065 // 1. Find next allocation to move.
14066 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
14067 // 1.2. Then start from last to first m_Allocations.
14068 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
14069 {
14070 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
14071 {
14072 // Finished: no more allocations to process.
14073 if(srcBlockIndex == srcBlockMinIndex)
14074 {
14075 return VK_SUCCESS;
14076 }
14077 else
14078 {
14079 --srcBlockIndex;
14080 srcAllocIndex = SIZE_MAX;
14081 }
14082 }
14083 else
14084 {
14085 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
14086 }
14087 }
14088
14089 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
14090 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
14091
14092 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
14093 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
14094 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
14095 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
14096
14097 // 2. Try to find new place for this allocation in preceding or current block.
14098 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
14099 {
14100 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
14101 VmaAllocationRequest dstAllocRequest;
14102 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
14103 m_CurrentFrameIndex,
14104 m_pBlockVector->GetFrameInUseCount(),
14105 m_pBlockVector->GetBufferImageGranularity(),
14106 size,
14107 alignment,
14108 false, // upperAddress
14109 suballocType,
14110 false, // canMakeOtherLost
14111 strategy,
14112 &dstAllocRequest) &&
14113 MoveMakesSense(
14114 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
14115 {
14116 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
14117
14118 // Reached limit on number of allocations or bytes to move.
14119 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
14120 (m_BytesMoved + size > maxBytesToMove))
14121 {
14122 return VK_SUCCESS;
14123 }
14124
14125 VmaDefragmentationMove move = {};
14126 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
14127 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
14128 move.srcOffset = srcOffset;
14129 move.dstOffset = dstAllocRequest.offset;
14130 move.size = size;
14131 move.hAllocation = allocInfo.m_hAllocation;
14132 move.pSrcBlock = pSrcBlockInfo->m_pBlock;
14133 move.pDstBlock = pDstBlockInfo->m_pBlock;
14134
14135 moves.push_back(move);
14136
14137 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
14138 dstAllocRequest,
14139 suballocType,
14140 size,
14141 allocInfo.m_hAllocation);
14142
14143 if(freeOldAllocations)
14144 {
14145 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
14146 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
14147 }
14148
14149 if(allocInfo.m_pChanged != VMA_NULL)
14150 {
14151 *allocInfo.m_pChanged = VK_TRUE;
14152 }
14153
14154 ++m_AllocationsMoved;
14155 m_BytesMoved += size;
14156
14157 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
14158
14159 break;
14160 }
14161 }
14162
14163 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
14164
14165 if(srcAllocIndex > 0)
14166 {
14167 --srcAllocIndex;
14168 }
14169 else
14170 {
14171 if(srcBlockIndex > 0)
14172 {
14173 --srcBlockIndex;
14174 srcAllocIndex = SIZE_MAX;
14175 }
14176 else
14177 {
14178 return VK_SUCCESS;
14179 }
14180 }
14181 }
14182 }
14183
CalcBlocksWithNonMovableCount()14184 size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
14185 {
14186 size_t result = 0;
14187 for(size_t i = 0; i < m_Blocks.size(); ++i)
14188 {
14189 if(m_Blocks[i]->m_HasNonMovableAllocations)
14190 {
14191 ++result;
14192 }
14193 }
14194 return result;
14195 }
14196
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14197 VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
14198 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14199 VkDeviceSize maxBytesToMove,
14200 uint32_t maxAllocationsToMove,
14201 VmaDefragmentationFlags flags)
14202 {
14203 if(!m_AllAllocations && m_AllocationCount == 0)
14204 {
14205 return VK_SUCCESS;
14206 }
14207
14208 const size_t blockCount = m_Blocks.size();
14209 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14210 {
14211 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
14212
14213 if(m_AllAllocations)
14214 {
14215 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
14216 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
14217 it != pMetadata->m_Suballocations.end();
14218 ++it)
14219 {
14220 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
14221 {
14222 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
14223 pBlockInfo->m_Allocations.push_back(allocInfo);
14224 }
14225 }
14226 }
14227
14228 pBlockInfo->CalcHasNonMovableAllocations();
14229
14230 // This is a choice based on research.
14231 // Option 1:
14232 pBlockInfo->SortAllocationsByOffsetDescending();
14233 // Option 2:
14234 //pBlockInfo->SortAllocationsBySizeDescending();
14235 }
14236
14237 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
14238 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
14239
14240 // This is a choice based on research.
14241 const uint32_t roundCount = 2;
14242
14243 // Execute defragmentation rounds (the main part).
14244 VkResult result = VK_SUCCESS;
14245 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
14246 {
14247 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove, !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL));
14248 }
14249
14250 return result;
14251 }
14252
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)14253 bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
14254 size_t dstBlockIndex, VkDeviceSize dstOffset,
14255 size_t srcBlockIndex, VkDeviceSize srcOffset)
14256 {
14257 if(dstBlockIndex < srcBlockIndex)
14258 {
14259 return true;
14260 }
14261 if(dstBlockIndex > srcBlockIndex)
14262 {
14263 return false;
14264 }
14265 if(dstOffset < srcOffset)
14266 {
14267 return true;
14268 }
14269 return false;
14270 }
14271
14272 ////////////////////////////////////////////////////////////////////////////////
14273 // VmaDefragmentationAlgorithm_Fast
14274
VmaDefragmentationAlgorithm_Fast(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex,bool overlappingMoveSupported)14275 VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
14276 VmaAllocator hAllocator,
14277 VmaBlockVector* pBlockVector,
14278 uint32_t currentFrameIndex,
14279 bool overlappingMoveSupported) :
14280 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
14281 m_OverlappingMoveSupported(overlappingMoveSupported),
14282 m_AllocationCount(0),
14283 m_AllAllocations(false),
14284 m_BytesMoved(0),
14285 m_AllocationsMoved(0),
14286 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
14287 {
14288 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
14289
14290 }
14291
~VmaDefragmentationAlgorithm_Fast()14292 VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
14293 {
14294 }
14295
Defragment(VmaVector<VmaDefragmentationMove,VmaStlAllocator<VmaDefragmentationMove>> & moves,VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove,VmaDefragmentationFlags flags)14296 VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
14297 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
14298 VkDeviceSize maxBytesToMove,
14299 uint32_t maxAllocationsToMove,
14300 VmaDefragmentationFlags flags)
14301 {
14302 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
14303
14304 const size_t blockCount = m_pBlockVector->GetBlockCount();
14305 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
14306 {
14307 return VK_SUCCESS;
14308 }
14309
14310 PreprocessMetadata();
14311
14312 // Sort blocks in order from most destination.
14313
14314 m_BlockInfos.resize(blockCount);
14315 for(size_t i = 0; i < blockCount; ++i)
14316 {
14317 m_BlockInfos[i].origBlockIndex = i;
14318 }
14319
14320 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
14321 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
14322 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
14323 });
14324
14325 // THE MAIN ALGORITHM
14326
14327 FreeSpaceDatabase freeSpaceDb;
14328
14329 size_t dstBlockInfoIndex = 0;
14330 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14331 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14332 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14333 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
14334 VkDeviceSize dstOffset = 0;
14335
14336 bool end = false;
14337 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
14338 {
14339 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
14340 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
14341 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
14342 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
14343 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
14344 {
14345 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
14346 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
14347 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
14348 if(m_AllocationsMoved == maxAllocationsToMove ||
14349 m_BytesMoved + srcAllocSize > maxBytesToMove)
14350 {
14351 end = true;
14352 break;
14353 }
14354 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
14355
14356 VmaDefragmentationMove move = {};
14357 // Try to place it in one of free spaces from the database.
14358 size_t freeSpaceInfoIndex;
14359 VkDeviceSize dstAllocOffset;
14360 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
14361 freeSpaceInfoIndex, dstAllocOffset))
14362 {
14363 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
14364 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
14365 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
14366
14367 // Same block
14368 if(freeSpaceInfoIndex == srcBlockInfoIndex)
14369 {
14370 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14371
14372 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14373
14374 VmaSuballocation suballoc = *srcSuballocIt;
14375 suballoc.offset = dstAllocOffset;
14376 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
14377 m_BytesMoved += srcAllocSize;
14378 ++m_AllocationsMoved;
14379
14380 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14381 ++nextSuballocIt;
14382 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14383 srcSuballocIt = nextSuballocIt;
14384
14385 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14386
14387 move.srcBlockIndex = srcOrigBlockIndex;
14388 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14389 move.srcOffset = srcAllocOffset;
14390 move.dstOffset = dstAllocOffset;
14391 move.size = srcAllocSize;
14392
14393 moves.push_back(move);
14394 }
14395 // Different block
14396 else
14397 {
14398 // MOVE OPTION 2: Move the allocation to a different block.
14399
14400 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
14401
14402 VmaSuballocation suballoc = *srcSuballocIt;
14403 suballoc.offset = dstAllocOffset;
14404 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
14405 m_BytesMoved += srcAllocSize;
14406 ++m_AllocationsMoved;
14407
14408 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14409 ++nextSuballocIt;
14410 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14411 srcSuballocIt = nextSuballocIt;
14412
14413 InsertSuballoc(pFreeSpaceMetadata, suballoc);
14414
14415 move.srcBlockIndex = srcOrigBlockIndex;
14416 move.dstBlockIndex = freeSpaceOrigBlockIndex;
14417 move.srcOffset = srcAllocOffset;
14418 move.dstOffset = dstAllocOffset;
14419 move.size = srcAllocSize;
14420
14421 moves.push_back(move);
14422 }
14423 }
14424 else
14425 {
14426 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
14427
14428 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
14429 while(dstBlockInfoIndex < srcBlockInfoIndex &&
14430 dstAllocOffset + srcAllocSize > dstBlockSize)
14431 {
14432 // But before that, register remaining free space at the end of dst block.
14433 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
14434
14435 ++dstBlockInfoIndex;
14436 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
14437 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
14438 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
14439 dstBlockSize = pDstMetadata->GetSize();
14440 dstOffset = 0;
14441 dstAllocOffset = 0;
14442 }
14443
14444 // Same block
14445 if(dstBlockInfoIndex == srcBlockInfoIndex)
14446 {
14447 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
14448
14449 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
14450
14451 bool skipOver = overlap;
14452 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
14453 {
14454 // If destination and source place overlap, skip if it would move it
14455 // by only < 1/64 of its size.
14456 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
14457 }
14458
14459 if(skipOver)
14460 {
14461 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
14462
14463 dstOffset = srcAllocOffset + srcAllocSize;
14464 ++srcSuballocIt;
14465 }
14466 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
14467 else
14468 {
14469 srcSuballocIt->offset = dstAllocOffset;
14470 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
14471 dstOffset = dstAllocOffset + srcAllocSize;
14472 m_BytesMoved += srcAllocSize;
14473 ++m_AllocationsMoved;
14474 ++srcSuballocIt;
14475
14476 move.srcBlockIndex = srcOrigBlockIndex;
14477 move.dstBlockIndex = dstOrigBlockIndex;
14478 move.srcOffset = srcAllocOffset;
14479 move.dstOffset = dstAllocOffset;
14480 move.size = srcAllocSize;
14481
14482 moves.push_back(move);
14483 }
14484 }
14485 // Different block
14486 else
14487 {
14488 // MOVE OPTION 2: Move the allocation to a different block.
14489
14490 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
14491 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
14492
14493 VmaSuballocation suballoc = *srcSuballocIt;
14494 suballoc.offset = dstAllocOffset;
14495 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
14496 dstOffset = dstAllocOffset + srcAllocSize;
14497 m_BytesMoved += srcAllocSize;
14498 ++m_AllocationsMoved;
14499
14500 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
14501 ++nextSuballocIt;
14502 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
14503 srcSuballocIt = nextSuballocIt;
14504
14505 pDstMetadata->m_Suballocations.push_back(suballoc);
14506
14507 move.srcBlockIndex = srcOrigBlockIndex;
14508 move.dstBlockIndex = dstOrigBlockIndex;
14509 move.srcOffset = srcAllocOffset;
14510 move.dstOffset = dstAllocOffset;
14511 move.size = srcAllocSize;
14512
14513 moves.push_back(move);
14514 }
14515 }
14516 }
14517 }
14518
14519 m_BlockInfos.clear();
14520
14521 PostprocessMetadata();
14522
14523 return VK_SUCCESS;
14524 }
14525
PreprocessMetadata()14526 void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
14527 {
14528 const size_t blockCount = m_pBlockVector->GetBlockCount();
14529 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14530 {
14531 VmaBlockMetadata_Generic* const pMetadata =
14532 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14533 pMetadata->m_FreeCount = 0;
14534 pMetadata->m_SumFreeSize = pMetadata->GetSize();
14535 pMetadata->m_FreeSuballocationsBySize.clear();
14536 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14537 it != pMetadata->m_Suballocations.end(); )
14538 {
14539 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
14540 {
14541 VmaSuballocationList::iterator nextIt = it;
14542 ++nextIt;
14543 pMetadata->m_Suballocations.erase(it);
14544 it = nextIt;
14545 }
14546 else
14547 {
14548 ++it;
14549 }
14550 }
14551 }
14552 }
14553
PostprocessMetadata()14554 void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
14555 {
14556 const size_t blockCount = m_pBlockVector->GetBlockCount();
14557 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
14558 {
14559 VmaBlockMetadata_Generic* const pMetadata =
14560 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
14561 const VkDeviceSize blockSize = pMetadata->GetSize();
14562
14563 // No allocations in this block - entire area is free.
14564 if(pMetadata->m_Suballocations.empty())
14565 {
14566 pMetadata->m_FreeCount = 1;
14567 //pMetadata->m_SumFreeSize is already set to blockSize.
14568 VmaSuballocation suballoc = {
14569 0, // offset
14570 blockSize, // size
14571 VMA_NULL, // hAllocation
14572 VMA_SUBALLOCATION_TYPE_FREE };
14573 pMetadata->m_Suballocations.push_back(suballoc);
14574 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
14575 }
14576 // There are some allocations in this block.
14577 else
14578 {
14579 VkDeviceSize offset = 0;
14580 VmaSuballocationList::iterator it;
14581 for(it = pMetadata->m_Suballocations.begin();
14582 it != pMetadata->m_Suballocations.end();
14583 ++it)
14584 {
14585 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
14586 VMA_ASSERT(it->offset >= offset);
14587
14588 // Need to insert preceding free space.
14589 if(it->offset > offset)
14590 {
14591 ++pMetadata->m_FreeCount;
14592 const VkDeviceSize freeSize = it->offset - offset;
14593 VmaSuballocation suballoc = {
14594 offset, // offset
14595 freeSize, // size
14596 VMA_NULL, // hAllocation
14597 VMA_SUBALLOCATION_TYPE_FREE };
14598 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14599 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14600 {
14601 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
14602 }
14603 }
14604
14605 pMetadata->m_SumFreeSize -= it->size;
14606 offset = it->offset + it->size;
14607 }
14608
14609 // Need to insert trailing free space.
14610 if(offset < blockSize)
14611 {
14612 ++pMetadata->m_FreeCount;
14613 const VkDeviceSize freeSize = blockSize - offset;
14614 VmaSuballocation suballoc = {
14615 offset, // offset
14616 freeSize, // size
14617 VMA_NULL, // hAllocation
14618 VMA_SUBALLOCATION_TYPE_FREE };
14619 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
14620 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
14621 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
14622 {
14623 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
14624 }
14625 }
14626
14627 VMA_SORT(
14628 pMetadata->m_FreeSuballocationsBySize.begin(),
14629 pMetadata->m_FreeSuballocationsBySize.end(),
14630 VmaSuballocationItemSizeLess());
14631 }
14632
14633 VMA_HEAVY_ASSERT(pMetadata->Validate());
14634 }
14635 }
14636
InsertSuballoc(VmaBlockMetadata_Generic * pMetadata,const VmaSuballocation & suballoc)14637 void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
14638 {
14639 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
14640 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
14641 while(it != pMetadata->m_Suballocations.end())
14642 {
14643 if(it->offset < suballoc.offset)
14644 {
14645 ++it;
14646 }
14647 }
14648 pMetadata->m_Suballocations.insert(it, suballoc);
14649 }
14650
14651 ////////////////////////////////////////////////////////////////////////////////
14652 // VmaBlockVectorDefragmentationContext
14653
VmaBlockVectorDefragmentationContext(VmaAllocator hAllocator,VmaPool hCustomPool,VmaBlockVector * pBlockVector,uint32_t currFrameIndex)14654 VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
14655 VmaAllocator hAllocator,
14656 VmaPool hCustomPool,
14657 VmaBlockVector* pBlockVector,
14658 uint32_t currFrameIndex) :
14659 res(VK_SUCCESS),
14660 mutexLocked(false),
14661 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
14662 defragmentationMoves(VmaStlAllocator<VmaDefragmentationMove>(hAllocator->GetAllocationCallbacks())),
14663 defragmentationMovesProcessed(0),
14664 defragmentationMovesCommitted(0),
14665 hasDefragmentationPlan(0),
14666 m_hAllocator(hAllocator),
14667 m_hCustomPool(hCustomPool),
14668 m_pBlockVector(pBlockVector),
14669 m_CurrFrameIndex(currFrameIndex),
14670 m_pAlgorithm(VMA_NULL),
14671 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
14672 m_AllAllocations(false)
14673 {
14674 }
14675
~VmaBlockVectorDefragmentationContext()14676 VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
14677 {
14678 vma_delete(m_hAllocator, m_pAlgorithm);
14679 }
14680
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)14681 void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
14682 {
14683 AllocInfo info = { hAlloc, pChanged };
14684 m_Allocations.push_back(info);
14685 }
14686
Begin(bool overlappingMoveSupported,VmaDefragmentationFlags flags)14687 void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported, VmaDefragmentationFlags flags)
14688 {
14689 const bool allAllocations = m_AllAllocations ||
14690 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
14691
14692 /********************************
14693 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
14694 ********************************/
14695
14696 /*
14697 Fast algorithm is supported only when certain criteria are met:
14698 - VMA_DEBUG_MARGIN is 0.
14699 - All allocations in this block vector are moveable.
14700 - There is no possibility of image/buffer granularity conflict.
14701 - The defragmentation is not incremental
14702 */
14703 if(VMA_DEBUG_MARGIN == 0 &&
14704 allAllocations &&
14705 !m_pBlockVector->IsBufferImageGranularityConflictPossible() &&
14706 !(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL))
14707 {
14708 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
14709 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14710 }
14711 else
14712 {
14713 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
14714 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
14715 }
14716
14717 if(allAllocations)
14718 {
14719 m_pAlgorithm->AddAll();
14720 }
14721 else
14722 {
14723 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
14724 {
14725 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
14726 }
14727 }
14728 }
14729
14730 ////////////////////////////////////////////////////////////////////////////////
14731 // VmaDefragmentationContext
14732
VmaDefragmentationContext_T(VmaAllocator hAllocator,uint32_t currFrameIndex,uint32_t flags,VmaDefragmentationStats * pStats)14733 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
14734 VmaAllocator hAllocator,
14735 uint32_t currFrameIndex,
14736 uint32_t flags,
14737 VmaDefragmentationStats* pStats) :
14738 m_hAllocator(hAllocator),
14739 m_CurrFrameIndex(currFrameIndex),
14740 m_Flags(flags),
14741 m_pStats(pStats),
14742 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
14743 {
14744 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
14745 }
14746
~VmaDefragmentationContext_T()14747 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
14748 {
14749 for(size_t i = m_CustomPoolContexts.size(); i--; )
14750 {
14751 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
14752 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14753 vma_delete(m_hAllocator, pBlockVectorCtx);
14754 }
14755 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
14756 {
14757 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
14758 if(pBlockVectorCtx)
14759 {
14760 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_Flags, m_pStats);
14761 vma_delete(m_hAllocator, pBlockVectorCtx);
14762 }
14763 }
14764 }
14765
AddPools(uint32_t poolCount,const VmaPool * pPools)14766 void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pPools)
14767 {
14768 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
14769 {
14770 VmaPool pool = pPools[poolIndex];
14771 VMA_ASSERT(pool);
14772 // Pools with algorithm other than default are not defragmented.
14773 if(pool->m_BlockVector.GetAlgorithm() == 0)
14774 {
14775 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14776
14777 for(size_t i = m_CustomPoolContexts.size(); i--; )
14778 {
14779 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
14780 {
14781 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14782 break;
14783 }
14784 }
14785
14786 if(!pBlockVectorDefragCtx)
14787 {
14788 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14789 m_hAllocator,
14790 pool,
14791 &pool->m_BlockVector,
14792 m_CurrFrameIndex);
14793 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14794 }
14795
14796 pBlockVectorDefragCtx->AddAll();
14797 }
14798 }
14799 }
14800
AddAllocations(uint32_t allocationCount,const VmaAllocation * pAllocations,VkBool32 * pAllocationsChanged)14801 void VmaDefragmentationContext_T::AddAllocations(
14802 uint32_t allocationCount,
14803 const VmaAllocation* pAllocations,
14804 VkBool32* pAllocationsChanged)
14805 {
14806 // Dispatch pAllocations among defragmentators. Create them when necessary.
14807 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14808 {
14809 const VmaAllocation hAlloc = pAllocations[allocIndex];
14810 VMA_ASSERT(hAlloc);
14811 // DedicatedAlloc cannot be defragmented.
14812 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
14813 // Lost allocation cannot be defragmented.
14814 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
14815 {
14816 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
14817
14818 const VmaPool hAllocPool = hAlloc->GetBlock()->GetParentPool();
14819 // This allocation belongs to custom pool.
14820 if(hAllocPool != VK_NULL_HANDLE)
14821 {
14822 // Pools with algorithm other than default are not defragmented.
14823 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
14824 {
14825 for(size_t i = m_CustomPoolContexts.size(); i--; )
14826 {
14827 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
14828 {
14829 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
14830 break;
14831 }
14832 }
14833 if(!pBlockVectorDefragCtx)
14834 {
14835 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14836 m_hAllocator,
14837 hAllocPool,
14838 &hAllocPool->m_BlockVector,
14839 m_CurrFrameIndex);
14840 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
14841 }
14842 }
14843 }
14844 // This allocation belongs to default pool.
14845 else
14846 {
14847 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
14848 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
14849 if(!pBlockVectorDefragCtx)
14850 {
14851 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
14852 m_hAllocator,
14853 VMA_NULL, // hCustomPool
14854 m_hAllocator->m_pBlockVectors[memTypeIndex],
14855 m_CurrFrameIndex);
14856 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
14857 }
14858 }
14859
14860 if(pBlockVectorDefragCtx)
14861 {
14862 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
14863 &pAllocationsChanged[allocIndex] : VMA_NULL;
14864 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
14865 }
14866 }
14867 }
14868 }
14869
Defragment(VkDeviceSize maxCpuBytesToMove,uint32_t maxCpuAllocationsToMove,VkDeviceSize maxGpuBytesToMove,uint32_t maxGpuAllocationsToMove,VkCommandBuffer commandBuffer,VmaDefragmentationStats * pStats,VmaDefragmentationFlags flags)14870 VkResult VmaDefragmentationContext_T::Defragment(
14871 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
14872 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
14873 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats, VmaDefragmentationFlags flags)
14874 {
14875 if(pStats)
14876 {
14877 memset(pStats, 0, sizeof(VmaDefragmentationStats));
14878 }
14879
14880 if(flags & VMA_DEFRAGMENTATION_FLAG_INCREMENTAL)
14881 {
14882 // For incremental defragmetnations, we just earmark how much we can move
14883 // The real meat is in the defragmentation steps
14884 m_MaxCpuBytesToMove = maxCpuBytesToMove;
14885 m_MaxCpuAllocationsToMove = maxCpuAllocationsToMove;
14886
14887 m_MaxGpuBytesToMove = maxGpuBytesToMove;
14888 m_MaxGpuAllocationsToMove = maxGpuAllocationsToMove;
14889
14890 if(m_MaxCpuBytesToMove == 0 && m_MaxCpuAllocationsToMove == 0 &&
14891 m_MaxGpuBytesToMove == 0 && m_MaxGpuAllocationsToMove == 0)
14892 return VK_SUCCESS;
14893
14894 return VK_NOT_READY;
14895 }
14896
14897 if(commandBuffer == VK_NULL_HANDLE)
14898 {
14899 maxGpuBytesToMove = 0;
14900 maxGpuAllocationsToMove = 0;
14901 }
14902
14903 VkResult res = VK_SUCCESS;
14904
14905 // Process default pools.
14906 for(uint32_t memTypeIndex = 0;
14907 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
14908 ++memTypeIndex)
14909 {
14910 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14911 if(pBlockVectorCtx)
14912 {
14913 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14914 pBlockVectorCtx->GetBlockVector()->Defragment(
14915 pBlockVectorCtx,
14916 pStats, flags,
14917 maxCpuBytesToMove, maxCpuAllocationsToMove,
14918 maxGpuBytesToMove, maxGpuAllocationsToMove,
14919 commandBuffer);
14920 if(pBlockVectorCtx->res != VK_SUCCESS)
14921 {
14922 res = pBlockVectorCtx->res;
14923 }
14924 }
14925 }
14926
14927 // Process custom pools.
14928 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14929 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
14930 ++customCtxIndex)
14931 {
14932 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14933 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14934 pBlockVectorCtx->GetBlockVector()->Defragment(
14935 pBlockVectorCtx,
14936 pStats, flags,
14937 maxCpuBytesToMove, maxCpuAllocationsToMove,
14938 maxGpuBytesToMove, maxGpuAllocationsToMove,
14939 commandBuffer);
14940 if(pBlockVectorCtx->res != VK_SUCCESS)
14941 {
14942 res = pBlockVectorCtx->res;
14943 }
14944 }
14945
14946 return res;
14947 }
14948
DefragmentPassBegin(VmaDefragmentationPassInfo * pInfo)14949 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassInfo* pInfo)
14950 {
14951 VmaDefragmentationPassMoveInfo* pCurrentMove = pInfo->pMoves;
14952 uint32_t movesLeft = pInfo->moveCount;
14953
14954 // Process default pools.
14955 for(uint32_t memTypeIndex = 0;
14956 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
14957 ++memTypeIndex)
14958 {
14959 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
14960 if(pBlockVectorCtx)
14961 {
14962 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
14963
14964 if(!pBlockVectorCtx->hasDefragmentationPlan)
14965 {
14966 pBlockVectorCtx->GetBlockVector()->Defragment(
14967 pBlockVectorCtx,
14968 m_pStats, m_Flags,
14969 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
14970 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
14971 VK_NULL_HANDLE);
14972
14973 if(pBlockVectorCtx->res < VK_SUCCESS)
14974 continue;
14975
14976 pBlockVectorCtx->hasDefragmentationPlan = true;
14977 }
14978
14979 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
14980 pBlockVectorCtx,
14981 pCurrentMove, movesLeft);
14982
14983 movesLeft -= processed;
14984 pCurrentMove += processed;
14985 }
14986 }
14987
14988 // Process custom pools.
14989 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
14990 customCtxIndex < customCtxCount;
14991 ++customCtxIndex)
14992 {
14993 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
14994 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
14995
14996 if(!pBlockVectorCtx->hasDefragmentationPlan)
14997 {
14998 pBlockVectorCtx->GetBlockVector()->Defragment(
14999 pBlockVectorCtx,
15000 m_pStats, m_Flags,
15001 m_MaxCpuBytesToMove, m_MaxCpuAllocationsToMove,
15002 m_MaxGpuBytesToMove, m_MaxGpuAllocationsToMove,
15003 VK_NULL_HANDLE);
15004
15005 if(pBlockVectorCtx->res < VK_SUCCESS)
15006 continue;
15007
15008 pBlockVectorCtx->hasDefragmentationPlan = true;
15009 }
15010
15011 const uint32_t processed = pBlockVectorCtx->GetBlockVector()->ProcessDefragmentations(
15012 pBlockVectorCtx,
15013 pCurrentMove, movesLeft);
15014
15015 movesLeft -= processed;
15016 pCurrentMove += processed;
15017 }
15018
15019 pInfo->moveCount = pInfo->moveCount - movesLeft;
15020
15021 return VK_SUCCESS;
15022 }
DefragmentPassEnd()15023 VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
15024 {
15025 VkResult res = VK_SUCCESS;
15026
15027 // Process default pools.
15028 for(uint32_t memTypeIndex = 0;
15029 memTypeIndex < m_hAllocator->GetMemoryTypeCount();
15030 ++memTypeIndex)
15031 {
15032 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
15033 if(pBlockVectorCtx)
15034 {
15035 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
15036
15037 if(!pBlockVectorCtx->hasDefragmentationPlan)
15038 {
15039 res = VK_NOT_READY;
15040 continue;
15041 }
15042
15043 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15044 pBlockVectorCtx, m_pStats);
15045
15046 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15047 res = VK_NOT_READY;
15048 }
15049 }
15050
15051 // Process custom pools.
15052 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
15053 customCtxIndex < customCtxCount;
15054 ++customCtxIndex)
15055 {
15056 VmaBlockVectorDefragmentationContext *pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
15057 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
15058
15059 if(!pBlockVectorCtx->hasDefragmentationPlan)
15060 {
15061 res = VK_NOT_READY;
15062 continue;
15063 }
15064
15065 pBlockVectorCtx->GetBlockVector()->CommitDefragmentations(
15066 pBlockVectorCtx, m_pStats);
15067
15068 if(pBlockVectorCtx->defragmentationMoves.size() != pBlockVectorCtx->defragmentationMovesCommitted)
15069 res = VK_NOT_READY;
15070 }
15071
15072 return res;
15073 }
15074
15075 ////////////////////////////////////////////////////////////////////////////////
15076 // VmaRecorder
15077
15078 #if VMA_RECORDING_ENABLED
15079
VmaRecorder()15080 VmaRecorder::VmaRecorder() :
15081 m_UseMutex(true),
15082 m_Flags(0),
15083 m_File(VMA_NULL),
15084 m_RecordingStartTime(std::chrono::high_resolution_clock::now())
15085 {
15086 }
15087
Init(const VmaRecordSettings & settings,bool useMutex)15088 VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
15089 {
15090 m_UseMutex = useMutex;
15091 m_Flags = settings.flags;
15092
15093 #if defined(_WIN32)
15094 // Open file for writing.
15095 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
15096
15097 if(err != 0)
15098 {
15099 return VK_ERROR_INITIALIZATION_FAILED;
15100 }
15101 #else
15102 // Open file for writing.
15103 m_File = fopen(settings.pFilePath, "wb");
15104
15105 if(m_File == 0)
15106 {
15107 return VK_ERROR_INITIALIZATION_FAILED;
15108 }
15109 #endif
15110
15111 // Write header.
15112 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
15113 fprintf(m_File, "%s\n", "1,8");
15114
15115 return VK_SUCCESS;
15116 }
15117
~VmaRecorder()15118 VmaRecorder::~VmaRecorder()
15119 {
15120 if(m_File != VMA_NULL)
15121 {
15122 fclose(m_File);
15123 }
15124 }
15125
RecordCreateAllocator(uint32_t frameIndex)15126 void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
15127 {
15128 CallParams callParams;
15129 GetBasicParams(callParams);
15130
15131 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15132 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
15133 Flush();
15134 }
15135
RecordDestroyAllocator(uint32_t frameIndex)15136 void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
15137 {
15138 CallParams callParams;
15139 GetBasicParams(callParams);
15140
15141 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15142 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
15143 Flush();
15144 }
15145
RecordCreatePool(uint32_t frameIndex,const VmaPoolCreateInfo & createInfo,VmaPool pool)15146 void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
15147 {
15148 CallParams callParams;
15149 GetBasicParams(callParams);
15150
15151 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15152 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
15153 createInfo.memoryTypeIndex,
15154 createInfo.flags,
15155 createInfo.blockSize,
15156 (uint64_t)createInfo.minBlockCount,
15157 (uint64_t)createInfo.maxBlockCount,
15158 createInfo.frameInUseCount,
15159 pool);
15160 Flush();
15161 }
15162
RecordDestroyPool(uint32_t frameIndex,VmaPool pool)15163 void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
15164 {
15165 CallParams callParams;
15166 GetBasicParams(callParams);
15167
15168 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15169 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
15170 pool);
15171 Flush();
15172 }
15173
RecordAllocateMemory(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15174 void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
15175 const VkMemoryRequirements& vkMemReq,
15176 const VmaAllocationCreateInfo& createInfo,
15177 VmaAllocation allocation)
15178 {
15179 CallParams callParams;
15180 GetBasicParams(callParams);
15181
15182 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15183 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15184 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15185 vkMemReq.size,
15186 vkMemReq.alignment,
15187 vkMemReq.memoryTypeBits,
15188 createInfo.flags,
15189 createInfo.usage,
15190 createInfo.requiredFlags,
15191 createInfo.preferredFlags,
15192 createInfo.memoryTypeBits,
15193 createInfo.pool,
15194 allocation,
15195 userDataStr.GetString());
15196 Flush();
15197 }
15198
RecordAllocateMemoryPages(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,uint64_t allocationCount,const VmaAllocation * pAllocations)15199 void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
15200 const VkMemoryRequirements& vkMemReq,
15201 const VmaAllocationCreateInfo& createInfo,
15202 uint64_t allocationCount,
15203 const VmaAllocation* pAllocations)
15204 {
15205 CallParams callParams;
15206 GetBasicParams(callParams);
15207
15208 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15209 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15210 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
15211 vkMemReq.size,
15212 vkMemReq.alignment,
15213 vkMemReq.memoryTypeBits,
15214 createInfo.flags,
15215 createInfo.usage,
15216 createInfo.requiredFlags,
15217 createInfo.preferredFlags,
15218 createInfo.memoryTypeBits,
15219 createInfo.pool);
15220 PrintPointerList(allocationCount, pAllocations);
15221 fprintf(m_File, ",%s\n", userDataStr.GetString());
15222 Flush();
15223 }
15224
RecordAllocateMemoryForBuffer(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15225 void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
15226 const VkMemoryRequirements& vkMemReq,
15227 bool requiresDedicatedAllocation,
15228 bool prefersDedicatedAllocation,
15229 const VmaAllocationCreateInfo& createInfo,
15230 VmaAllocation allocation)
15231 {
15232 CallParams callParams;
15233 GetBasicParams(callParams);
15234
15235 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15236 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15237 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,
15238 vkMemReq.size,
15239 vkMemReq.alignment,
15240 vkMemReq.memoryTypeBits,
15241 requiresDedicatedAllocation ? 1 : 0,
15242 prefersDedicatedAllocation ? 1 : 0,
15243 createInfo.flags,
15244 createInfo.usage,
15245 createInfo.requiredFlags,
15246 createInfo.preferredFlags,
15247 createInfo.memoryTypeBits,
15248 createInfo.pool,
15249 allocation,
15250 userDataStr.GetString());
15251 Flush();
15252 }
15253
RecordAllocateMemoryForImage(uint32_t frameIndex,const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,const VmaAllocationCreateInfo & createInfo,VmaAllocation allocation)15254 void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
15255 const VkMemoryRequirements& vkMemReq,
15256 bool requiresDedicatedAllocation,
15257 bool prefersDedicatedAllocation,
15258 const VmaAllocationCreateInfo& createInfo,
15259 VmaAllocation allocation)
15260 {
15261 CallParams callParams;
15262 GetBasicParams(callParams);
15263
15264 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15265 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
15266 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,
15267 vkMemReq.size,
15268 vkMemReq.alignment,
15269 vkMemReq.memoryTypeBits,
15270 requiresDedicatedAllocation ? 1 : 0,
15271 prefersDedicatedAllocation ? 1 : 0,
15272 createInfo.flags,
15273 createInfo.usage,
15274 createInfo.requiredFlags,
15275 createInfo.preferredFlags,
15276 createInfo.memoryTypeBits,
15277 createInfo.pool,
15278 allocation,
15279 userDataStr.GetString());
15280 Flush();
15281 }
15282
RecordFreeMemory(uint32_t frameIndex,VmaAllocation allocation)15283 void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
15284 VmaAllocation allocation)
15285 {
15286 CallParams callParams;
15287 GetBasicParams(callParams);
15288
15289 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15290 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15291 allocation);
15292 Flush();
15293 }
15294
RecordFreeMemoryPages(uint32_t frameIndex,uint64_t allocationCount,const VmaAllocation * pAllocations)15295 void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
15296 uint64_t allocationCount,
15297 const VmaAllocation* pAllocations)
15298 {
15299 CallParams callParams;
15300 GetBasicParams(callParams);
15301
15302 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15303 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
15304 PrintPointerList(allocationCount, pAllocations);
15305 fprintf(m_File, "\n");
15306 Flush();
15307 }
15308
RecordSetAllocationUserData(uint32_t frameIndex,VmaAllocation allocation,const void * pUserData)15309 void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
15310 VmaAllocation allocation,
15311 const void* pUserData)
15312 {
15313 CallParams callParams;
15314 GetBasicParams(callParams);
15315
15316 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15317 UserDataString userDataStr(
15318 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
15319 pUserData);
15320 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15321 allocation,
15322 userDataStr.GetString());
15323 Flush();
15324 }
15325
RecordCreateLostAllocation(uint32_t frameIndex,VmaAllocation allocation)15326 void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
15327 VmaAllocation allocation)
15328 {
15329 CallParams callParams;
15330 GetBasicParams(callParams);
15331
15332 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15333 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15334 allocation);
15335 Flush();
15336 }
15337
RecordMapMemory(uint32_t frameIndex,VmaAllocation allocation)15338 void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
15339 VmaAllocation allocation)
15340 {
15341 CallParams callParams;
15342 GetBasicParams(callParams);
15343
15344 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15345 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15346 allocation);
15347 Flush();
15348 }
15349
RecordUnmapMemory(uint32_t frameIndex,VmaAllocation allocation)15350 void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
15351 VmaAllocation allocation)
15352 {
15353 CallParams callParams;
15354 GetBasicParams(callParams);
15355
15356 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15357 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
15358 allocation);
15359 Flush();
15360 }
15361
RecordFlushAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15362 void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
15363 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15364 {
15365 CallParams callParams;
15366 GetBasicParams(callParams);
15367
15368 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15369 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15370 allocation,
15371 offset,
15372 size);
15373 Flush();
15374 }
15375
RecordInvalidateAllocation(uint32_t frameIndex,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)15376 void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
15377 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
15378 {
15379 CallParams callParams;
15380 GetBasicParams(callParams);
15381
15382 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15383 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
15384 allocation,
15385 offset,
15386 size);
15387 Flush();
15388 }
15389
RecordCreateBuffer(uint32_t frameIndex,const VkBufferCreateInfo & bufCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15390 void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
15391 const VkBufferCreateInfo& bufCreateInfo,
15392 const VmaAllocationCreateInfo& allocCreateInfo,
15393 VmaAllocation allocation)
15394 {
15395 CallParams callParams;
15396 GetBasicParams(callParams);
15397
15398 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15399 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15400 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,
15401 bufCreateInfo.flags,
15402 bufCreateInfo.size,
15403 bufCreateInfo.usage,
15404 bufCreateInfo.sharingMode,
15405 allocCreateInfo.flags,
15406 allocCreateInfo.usage,
15407 allocCreateInfo.requiredFlags,
15408 allocCreateInfo.preferredFlags,
15409 allocCreateInfo.memoryTypeBits,
15410 allocCreateInfo.pool,
15411 allocation,
15412 userDataStr.GetString());
15413 Flush();
15414 }
15415
RecordCreateImage(uint32_t frameIndex,const VkImageCreateInfo & imageCreateInfo,const VmaAllocationCreateInfo & allocCreateInfo,VmaAllocation allocation)15416 void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
15417 const VkImageCreateInfo& imageCreateInfo,
15418 const VmaAllocationCreateInfo& allocCreateInfo,
15419 VmaAllocation allocation)
15420 {
15421 CallParams callParams;
15422 GetBasicParams(callParams);
15423
15424 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15425 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
15426 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,
15427 imageCreateInfo.flags,
15428 imageCreateInfo.imageType,
15429 imageCreateInfo.format,
15430 imageCreateInfo.extent.width,
15431 imageCreateInfo.extent.height,
15432 imageCreateInfo.extent.depth,
15433 imageCreateInfo.mipLevels,
15434 imageCreateInfo.arrayLayers,
15435 imageCreateInfo.samples,
15436 imageCreateInfo.tiling,
15437 imageCreateInfo.usage,
15438 imageCreateInfo.sharingMode,
15439 imageCreateInfo.initialLayout,
15440 allocCreateInfo.flags,
15441 allocCreateInfo.usage,
15442 allocCreateInfo.requiredFlags,
15443 allocCreateInfo.preferredFlags,
15444 allocCreateInfo.memoryTypeBits,
15445 allocCreateInfo.pool,
15446 allocation,
15447 userDataStr.GetString());
15448 Flush();
15449 }
15450
RecordDestroyBuffer(uint32_t frameIndex,VmaAllocation allocation)15451 void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
15452 VmaAllocation allocation)
15453 {
15454 CallParams callParams;
15455 GetBasicParams(callParams);
15456
15457 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15458 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
15459 allocation);
15460 Flush();
15461 }
15462
RecordDestroyImage(uint32_t frameIndex,VmaAllocation allocation)15463 void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
15464 VmaAllocation allocation)
15465 {
15466 CallParams callParams;
15467 GetBasicParams(callParams);
15468
15469 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15470 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
15471 allocation);
15472 Flush();
15473 }
15474
RecordTouchAllocation(uint32_t frameIndex,VmaAllocation allocation)15475 void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
15476 VmaAllocation allocation)
15477 {
15478 CallParams callParams;
15479 GetBasicParams(callParams);
15480
15481 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15482 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
15483 allocation);
15484 Flush();
15485 }
15486
RecordGetAllocationInfo(uint32_t frameIndex,VmaAllocation allocation)15487 void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
15488 VmaAllocation allocation)
15489 {
15490 CallParams callParams;
15491 GetBasicParams(callParams);
15492
15493 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15494 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
15495 allocation);
15496 Flush();
15497 }
15498
RecordMakePoolAllocationsLost(uint32_t frameIndex,VmaPool pool)15499 void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
15500 VmaPool pool)
15501 {
15502 CallParams callParams;
15503 GetBasicParams(callParams);
15504
15505 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15506 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
15507 pool);
15508 Flush();
15509 }
15510
RecordDefragmentationBegin(uint32_t frameIndex,const VmaDefragmentationInfo2 & info,VmaDefragmentationContext ctx)15511 void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
15512 const VmaDefragmentationInfo2& info,
15513 VmaDefragmentationContext ctx)
15514 {
15515 CallParams callParams;
15516 GetBasicParams(callParams);
15517
15518 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15519 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
15520 info.flags);
15521 PrintPointerList(info.allocationCount, info.pAllocations);
15522 fprintf(m_File, ",");
15523 PrintPointerList(info.poolCount, info.pPools);
15524 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
15525 info.maxCpuBytesToMove,
15526 info.maxCpuAllocationsToMove,
15527 info.maxGpuBytesToMove,
15528 info.maxGpuAllocationsToMove,
15529 info.commandBuffer,
15530 ctx);
15531 Flush();
15532 }
15533
RecordDefragmentationEnd(uint32_t frameIndex,VmaDefragmentationContext ctx)15534 void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
15535 VmaDefragmentationContext ctx)
15536 {
15537 CallParams callParams;
15538 GetBasicParams(callParams);
15539
15540 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15541 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
15542 ctx);
15543 Flush();
15544 }
15545
RecordSetPoolName(uint32_t frameIndex,VmaPool pool,const char * name)15546 void VmaRecorder::RecordSetPoolName(uint32_t frameIndex,
15547 VmaPool pool,
15548 const char* name)
15549 {
15550 CallParams callParams;
15551 GetBasicParams(callParams);
15552
15553 VmaMutexLock lock(m_FileMutex, m_UseMutex);
15554 fprintf(m_File, "%u,%.3f,%u,vmaSetPoolName,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
15555 pool, name != VMA_NULL ? name : "");
15556 Flush();
15557 }
15558
UserDataString(VmaAllocationCreateFlags allocFlags,const void * pUserData)15559 VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
15560 {
15561 if(pUserData != VMA_NULL)
15562 {
15563 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
15564 {
15565 m_Str = (const char*)pUserData;
15566 }
15567 else
15568 {
15569 // If VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is not specified, convert the string's memory address to a string and store it.
15570 snprintf(m_PtrStr, 17, "%p", pUserData);
15571 m_Str = m_PtrStr;
15572 }
15573 }
15574 else
15575 {
15576 m_Str = "";
15577 }
15578 }
15579
WriteConfiguration(const VkPhysicalDeviceProperties & devProps,const VkPhysicalDeviceMemoryProperties & memProps,uint32_t vulkanApiVersion,bool dedicatedAllocationExtensionEnabled,bool bindMemory2ExtensionEnabled,bool memoryBudgetExtensionEnabled,bool deviceCoherentMemoryExtensionEnabled)15580 void VmaRecorder::WriteConfiguration(
15581 const VkPhysicalDeviceProperties& devProps,
15582 const VkPhysicalDeviceMemoryProperties& memProps,
15583 uint32_t vulkanApiVersion,
15584 bool dedicatedAllocationExtensionEnabled,
15585 bool bindMemory2ExtensionEnabled,
15586 bool memoryBudgetExtensionEnabled,
15587 bool deviceCoherentMemoryExtensionEnabled)
15588 {
15589 fprintf(m_File, "Config,Begin\n");
15590
15591 fprintf(m_File, "VulkanApiVersion,%u,%u\n", VK_VERSION_MAJOR(vulkanApiVersion), VK_VERSION_MINOR(vulkanApiVersion));
15592
15593 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
15594 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
15595 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
15596 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
15597 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
15598 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
15599
15600 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
15601 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
15602 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
15603
15604 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
15605 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
15606 {
15607 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
15608 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
15609 }
15610 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
15611 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
15612 {
15613 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
15614 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
15615 }
15616
15617 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
15618 fprintf(m_File, "Extension,VK_KHR_bind_memory2,%u\n", bindMemory2ExtensionEnabled ? 1 : 0);
15619 fprintf(m_File, "Extension,VK_EXT_memory_budget,%u\n", memoryBudgetExtensionEnabled ? 1 : 0);
15620 fprintf(m_File, "Extension,VK_AMD_device_coherent_memory,%u\n", deviceCoherentMemoryExtensionEnabled ? 1 : 0);
15621
15622 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
15623 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
15624 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
15625 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
15626 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
15627 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
15628 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
15629 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
15630 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15631
15632 fprintf(m_File, "Config,End\n");
15633 }
15634
GetBasicParams(CallParams & outParams)15635 void VmaRecorder::GetBasicParams(CallParams& outParams)
15636 {
15637 #if defined(_WIN32)
15638 outParams.threadId = GetCurrentThreadId();
15639 #else
15640 // Use C++11 features to get thread id and convert it to uint32_t.
15641 // There is room for optimization since sstream is quite slow.
15642 // Is there a better way to convert std::this_thread::get_id() to uint32_t?
15643 std::thread::id thread_id = std::this_thread::get_id();
15644 stringstream thread_id_to_string_converter;
15645 thread_id_to_string_converter << thread_id;
15646 string thread_id_as_string = thread_id_to_string_converter.str();
15647 outParams.threadId = static_cast<uint32_t>(std::stoi(thread_id_as_string.c_str()));
15648 #endif
15649
15650 auto current_time = std::chrono::high_resolution_clock::now();
15651
15652 outParams.time = std::chrono::duration<double, std::chrono::seconds::period>(current_time - m_RecordingStartTime).count();
15653 }
15654
PrintPointerList(uint64_t count,const VmaAllocation * pItems)15655 void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
15656 {
15657 if(count)
15658 {
15659 fprintf(m_File, "%p", pItems[0]);
15660 for(uint64_t i = 1; i < count; ++i)
15661 {
15662 fprintf(m_File, " %p", pItems[i]);
15663 }
15664 }
15665 }
15666
Flush()15667 void VmaRecorder::Flush()
15668 {
15669 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
15670 {
15671 fflush(m_File);
15672 }
15673 }
15674
15675 #endif // #if VMA_RECORDING_ENABLED
15676
15677 ////////////////////////////////////////////////////////////////////////////////
15678 // VmaAllocationObjectAllocator
15679
VmaAllocationObjectAllocator(const VkAllocationCallbacks * pAllocationCallbacks)15680 VmaAllocationObjectAllocator::VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) :
15681 m_Allocator(pAllocationCallbacks, 1024)
15682 {
15683 }
15684
Allocate(Types...args)15685 template<typename... Types> VmaAllocation VmaAllocationObjectAllocator::Allocate(Types... args)
15686 {
15687 VmaMutexLock mutexLock(m_Mutex);
15688 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
15689 }
15690
Free(VmaAllocation hAlloc)15691 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
15692 {
15693 VmaMutexLock mutexLock(m_Mutex);
15694 m_Allocator.Free(hAlloc);
15695 }
15696
15697 ////////////////////////////////////////////////////////////////////////////////
15698 // VmaAllocator_T
15699
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)15700 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
15701 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
15702 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
15703 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
15704 m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
15705 m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
15706 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
15707 m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
15708 m_hDevice(pCreateInfo->device),
15709 m_hInstance(pCreateInfo->instance),
15710 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
15711 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
15712 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
15713 m_AllocationObjectAllocator(&m_AllocationCallbacks),
15714 m_HeapSizeLimitMask(0),
15715 m_PreferredLargeHeapBlockSize(0),
15716 m_PhysicalDevice(pCreateInfo->physicalDevice),
15717 m_CurrentFrameIndex(0),
15718 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
15719 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
15720 m_NextPoolId(0),
15721 m_GlobalMemoryTypeBits(UINT32_MAX)
15722 #if VMA_RECORDING_ENABLED
15723 ,m_pRecorder(VMA_NULL)
15724 #endif
15725 {
15726 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15727 {
15728 m_UseKhrDedicatedAllocation = false;
15729 m_UseKhrBindMemory2 = false;
15730 }
15731
15732 if(VMA_DEBUG_DETECT_CORRUPTION)
15733 {
15734 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
15735 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
15736 }
15737
15738 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
15739
15740 if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
15741 {
15742 #if !(VMA_DEDICATED_ALLOCATION)
15743 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
15744 {
15745 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
15746 }
15747 #endif
15748 #if !(VMA_BIND_MEMORY2)
15749 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
15750 {
15751 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
15752 }
15753 #endif
15754 }
15755 #if !(VMA_MEMORY_BUDGET)
15756 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
15757 {
15758 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
15759 }
15760 #endif
15761 #if !(VMA_BUFFER_DEVICE_ADDRESS)
15762 if(m_UseKhrBufferDeviceAddress)
15763 {
15764 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.");
15765 }
15766 #endif
15767 #if VMA_VULKAN_VERSION < 1002000
15768 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
15769 {
15770 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
15771 }
15772 #endif
15773 #if VMA_VULKAN_VERSION < 1001000
15774 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15775 {
15776 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
15777 }
15778 #endif
15779
15780 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
15781 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
15782 memset(&m_MemProps, 0, sizeof(m_MemProps));
15783
15784 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
15785 memset(&m_pReservedBlockVectors, 0, sizeof(m_pReservedBlockVectors));
15786 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
15787 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
15788
15789 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
15790 {
15791 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
15792 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
15793 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
15794 }
15795
15796 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
15797
15798 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
15799 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
15800
15801 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
15802 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
15803 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
15804 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
15805
15806 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
15807 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
15808
15809 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
15810
15811 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
15812 {
15813 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15814 {
15815 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
15816 if(limit != VK_WHOLE_SIZE)
15817 {
15818 m_HeapSizeLimitMask |= 1u << heapIndex;
15819 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
15820 {
15821 m_MemProps.memoryHeaps[heapIndex].size = limit;
15822 }
15823 }
15824 }
15825 }
15826
15827 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15828 {
15829 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
15830
15831 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15832 this,
15833 VK_NULL_HANDLE, // hParentPool
15834 memTypeIndex,
15835 preferredBlockSize,
15836 0,
15837 pCreateInfo->maxBlockCount,
15838 GetBufferImageGranularity(),
15839 pCreateInfo->frameInUseCount,
15840 false, // explicitBlockSize
15841 false); // linearAlgorithm
15842 m_pReservedBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
15843 this,
15844 VK_NULL_HANDLE, // hParentPool
15845 memTypeIndex,
15846 preferredBlockSize,
15847 0,
15848 1, // max block count 1
15849 GetBufferImageGranularity(),
15850 pCreateInfo->frameInUseCount,
15851 false, // explicitBlockSize
15852 false); // linearAlgorithm
15853 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
15854 // becase minBlockCount is 0.
15855 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
15856
15857 }
15858 }
15859
Init(const VmaAllocatorCreateInfo * pCreateInfo)15860 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
15861 {
15862 VkResult res = VK_SUCCESS;
15863
15864 if(pCreateInfo->pRecordSettings != VMA_NULL &&
15865 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
15866 {
15867 #if VMA_RECORDING_ENABLED
15868 m_pRecorder = vma_new(this, VmaRecorder)();
15869 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
15870 if(res != VK_SUCCESS)
15871 {
15872 return res;
15873 }
15874 m_pRecorder->WriteConfiguration(
15875 m_PhysicalDeviceProperties,
15876 m_MemProps,
15877 m_VulkanApiVersion,
15878 m_UseKhrDedicatedAllocation,
15879 m_UseKhrBindMemory2,
15880 m_UseExtMemoryBudget,
15881 m_UseAmdDeviceCoherentMemory);
15882 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
15883 #else
15884 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
15885 return VK_ERROR_FEATURE_NOT_PRESENT;
15886 #endif
15887 }
15888
15889 #if VMA_MEMORY_BUDGET
15890 if(m_UseExtMemoryBudget)
15891 {
15892 UpdateVulkanBudget();
15893 }
15894 #endif // #if VMA_MEMORY_BUDGET
15895
15896 return res;
15897 }
15898
~VmaAllocator_T()15899 VmaAllocator_T::~VmaAllocator_T()
15900 {
15901 #if VMA_RECORDING_ENABLED
15902 if(m_pRecorder != VMA_NULL)
15903 {
15904 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
15905 vma_delete(this, m_pRecorder);
15906 }
15907 #endif
15908
15909 VMA_ASSERT(m_Pools.empty());
15910
15911 for(size_t i = GetMemoryTypeCount(); i--; )
15912 {
15913 if(m_pDedicatedAllocations[i] != VMA_NULL && !m_pDedicatedAllocations[i]->empty())
15914 {
15915 VMA_ASSERT(0 && "Unfreed dedicated allocations found.");
15916 }
15917
15918 vma_delete(this, m_pDedicatedAllocations[i]);
15919 vma_delete(this, m_pBlockVectors[i]);
15920 vma_delete(this, m_pReservedBlockVectors[i]);
15921 }
15922 }
15923
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)15924 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
15925 {
15926 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15927 ImportVulkanFunctions_Static();
15928 #endif
15929
15930 if(pVulkanFunctions != VMA_NULL)
15931 {
15932 ImportVulkanFunctions_Custom(pVulkanFunctions);
15933 }
15934
15935 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
15936 ImportVulkanFunctions_Dynamic();
15937 #endif
15938
15939 ValidateVulkanFunctions();
15940 }
15941
15942 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15943
ImportVulkanFunctions_Static()15944 void VmaAllocator_T::ImportVulkanFunctions_Static()
15945 {
15946 // Vulkan 1.0
15947 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
15948 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
15949 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
15950 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
15951 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
15952 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
15953 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
15954 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
15955 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
15956 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
15957 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
15958 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
15959 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
15960 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
15961 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
15962 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
15963 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
15964
15965 // Vulkan 1.1
15966 #if VMA_VULKAN_VERSION >= 1001000
15967 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
15968 {
15969 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
15970 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
15971 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
15972 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
15973 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
15974 }
15975 #endif
15976 }
15977
15978 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
15979
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)15980 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
15981 {
15982 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
15983
15984 #define VMA_COPY_IF_NOT_NULL(funcName) \
15985 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
15986
15987 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
15988 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
15989 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
15990 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
15991 VMA_COPY_IF_NOT_NULL(vkMapMemory);
15992 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
15993 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
15994 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
15995 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
15996 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
15997 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
15998 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
15999 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
16000 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
16001 VMA_COPY_IF_NOT_NULL(vkCreateImage);
16002 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
16003 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
16004
16005 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16006 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
16007 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
16008 #endif
16009
16010 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16011 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
16012 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
16013 #endif
16014
16015 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16016 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
16017 #endif
16018
16019 #undef VMA_COPY_IF_NOT_NULL
16020 }
16021
16022 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16023
ImportVulkanFunctions_Dynamic()16024 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
16025 {
16026 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
16027 if(m_VulkanFunctions.memberName == VMA_NULL) \
16028 m_VulkanFunctions.memberName = \
16029 (functionPointerType)vkGetInstanceProcAddr(m_hInstance, functionNameString);
16030 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
16031 if(m_VulkanFunctions.memberName == VMA_NULL) \
16032 m_VulkanFunctions.memberName = \
16033 (functionPointerType)vkGetDeviceProcAddr(m_hDevice, functionNameString);
16034
16035 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
16036 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
16037 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
16038 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
16039 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
16040 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
16041 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
16042 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
16043 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
16044 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
16045 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
16046 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
16047 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
16048 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
16049 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
16050 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
16051 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
16052
16053 #if VMA_DEDICATED_ALLOCATION
16054 if(m_UseKhrDedicatedAllocation)
16055 {
16056 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
16057 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
16058 }
16059 #endif
16060
16061 #if VMA_BIND_MEMORY2
16062 if(m_UseKhrBindMemory2)
16063 {
16064 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
16065 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
16066 }
16067 #endif // #if VMA_BIND_MEMORY2
16068
16069 #if VMA_MEMORY_BUDGET
16070 if(m_UseExtMemoryBudget && m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
16071 {
16072 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
16073 }
16074 #endif // #if VMA_MEMORY_BUDGET
16075
16076 #undef VMA_FETCH_DEVICE_FUNC
16077 #undef VMA_FETCH_INSTANCE_FUNC
16078 }
16079
16080 #endif // #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
16081
ValidateVulkanFunctions()16082 void VmaAllocator_T::ValidateVulkanFunctions()
16083 {
16084 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
16085 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
16086 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
16087 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
16088 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
16089 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
16090 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
16091 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
16092 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
16093 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
16094 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
16095 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
16096 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
16097 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
16098 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
16099 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
16100 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
16101
16102 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16103 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
16104 {
16105 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
16106 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
16107 }
16108 #endif
16109
16110 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
16111 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
16112 {
16113 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
16114 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
16115 }
16116 #endif
16117
16118 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
16119 if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16120 {
16121 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
16122 }
16123 #endif
16124 }
16125
CalcPreferredBlockSize(uint32_t memTypeIndex)16126 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
16127 {
16128 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16129 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
16130 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
16131 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
16132 }
16133
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)16134 VkResult VmaAllocator_T::AllocateMemoryOfType(
16135 VkDeviceSize size,
16136 VkDeviceSize alignment,
16137 bool dedicatedAllocation,
16138 VkBuffer dedicatedBuffer,
16139 VkBufferUsageFlags dedicatedBufferUsage,
16140 VkImage dedicatedImage,
16141 const VmaAllocationCreateInfo& createInfo,
16142 uint32_t memTypeIndex,
16143 VmaSuballocationType suballocType,
16144 size_t allocationCount,
16145 VmaAllocation* pAllocations)
16146 {
16147 VMA_ASSERT(pAllocations != VMA_NULL);
16148 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
16149
16150 VmaAllocationCreateInfo finalCreateInfo = createInfo;
16151
16152 // If memory type is not HOST_VISIBLE, disable MAPPED.
16153 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16154 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16155 {
16156 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16157 }
16158 // If memory is lazily allocated, it should be always dedicated.
16159 if(finalCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
16160 {
16161 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16162 }
16163
16164 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
16165 VMA_ASSERT(blockVector);
16166
16167 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
16168 bool preferDedicatedMemory =
16169 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
16170 dedicatedAllocation ||
16171 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
16172 size > preferredBlockSize / 2;
16173
16174 if(preferDedicatedMemory &&
16175 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
16176 finalCreateInfo.pool == VK_NULL_HANDLE)
16177 {
16178 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
16179 }
16180
16181 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
16182 {
16183 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16184 {
16185 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16186 }
16187 else
16188 {
16189 return AllocateDedicatedMemory(
16190 size,
16191 suballocType,
16192 memTypeIndex,
16193 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16194 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16195 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16196 finalCreateInfo.pUserData,
16197 dedicatedBuffer,
16198 dedicatedBufferUsage,
16199 dedicatedImage,
16200 allocationCount,
16201 pAllocations);
16202 }
16203 }
16204 else
16205 {
16206 VkResult res = blockVector->Allocate(
16207 m_CurrentFrameIndex.load(),
16208 size,
16209 alignment,
16210 finalCreateInfo,
16211 suballocType,
16212 allocationCount,
16213 pAllocations);
16214 if(res == VK_SUCCESS)
16215 {
16216 return res;
16217 }
16218
16219 // 5. Try dedicated memory.
16220 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16221 {
16222 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16223 }
16224 else
16225 {
16226 res = AllocateDedicatedMemory(
16227 size,
16228 suballocType,
16229 memTypeIndex,
16230 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0,
16231 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
16232 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
16233 finalCreateInfo.pUserData,
16234 dedicatedBuffer,
16235 dedicatedBufferUsage,
16236 dedicatedImage,
16237 allocationCount,
16238 pAllocations);
16239 if(res == VK_SUCCESS)
16240 {
16241 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
16242 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
16243 return VK_SUCCESS;
16244 }
16245 else
16246 {
16247 // Everything failed: Return error code.
16248 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16249 return res;
16250 }
16251 }
16252 }
16253 }
16254
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)16255 VkResult VmaAllocator_T::AllocateDedicatedMemory(
16256 VkDeviceSize size,
16257 VmaSuballocationType suballocType,
16258 uint32_t memTypeIndex,
16259 bool withinBudget,
16260 bool map,
16261 bool isUserDataString,
16262 void* pUserData,
16263 VkBuffer dedicatedBuffer,
16264 VkBufferUsageFlags dedicatedBufferUsage,
16265 VkImage dedicatedImage,
16266 size_t allocationCount,
16267 VmaAllocation* pAllocations)
16268 {
16269 VMA_ASSERT(allocationCount > 0 && pAllocations);
16270
16271 if(withinBudget)
16272 {
16273 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16274 VmaBudget heapBudget = {};
16275 GetBudget(&heapBudget, heapIndex, 1);
16276 if(heapBudget.usage + size * allocationCount > heapBudget.budget)
16277 {
16278 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16279 }
16280 }
16281
16282 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
16283 allocInfo.memoryTypeIndex = memTypeIndex;
16284 allocInfo.allocationSize = size;
16285
16286 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16287 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
16288 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16289 {
16290 if(dedicatedBuffer != VK_NULL_HANDLE)
16291 {
16292 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
16293 dedicatedAllocInfo.buffer = dedicatedBuffer;
16294 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16295 }
16296 else if(dedicatedImage != VK_NULL_HANDLE)
16297 {
16298 dedicatedAllocInfo.image = dedicatedImage;
16299 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
16300 }
16301 }
16302 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16303
16304 #if VMA_BUFFER_DEVICE_ADDRESS
16305 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
16306 if(m_UseKhrBufferDeviceAddress)
16307 {
16308 bool canContainBufferWithDeviceAddress = true;
16309 if(dedicatedBuffer != VK_NULL_HANDLE)
16310 {
16311 canContainBufferWithDeviceAddress = dedicatedBufferUsage == UINT32_MAX || // Usage flags unknown
16312 (dedicatedBufferUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
16313 }
16314 else if(dedicatedImage != VK_NULL_HANDLE)
16315 {
16316 canContainBufferWithDeviceAddress = false;
16317 }
16318 if(canContainBufferWithDeviceAddress)
16319 {
16320 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
16321 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
16322 }
16323 }
16324 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
16325
16326 size_t allocIndex;
16327 VkResult res = VK_SUCCESS;
16328 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16329 {
16330 res = AllocateDedicatedMemoryPage(
16331 size,
16332 suballocType,
16333 memTypeIndex,
16334 allocInfo,
16335 map,
16336 isUserDataString,
16337 pUserData,
16338 pAllocations + allocIndex);
16339 if(res != VK_SUCCESS)
16340 {
16341 break;
16342 }
16343 }
16344
16345 if(res == VK_SUCCESS)
16346 {
16347 // Register them in m_pDedicatedAllocations.
16348 {
16349 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16350 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
16351 VMA_ASSERT(pDedicatedAllocations);
16352 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
16353 {
16354 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
16355 }
16356 }
16357
16358 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
16359 }
16360 else
16361 {
16362 // Free all already created allocations.
16363 while(allocIndex--)
16364 {
16365 VmaAllocation currAlloc = pAllocations[allocIndex];
16366 VkDeviceMemory hMemory = currAlloc->GetMemory();
16367
16368 /*
16369 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
16370 before vkFreeMemory.
16371
16372 if(currAlloc->GetMappedData() != VMA_NULL)
16373 {
16374 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
16375 }
16376 */
16377
16378 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
16379 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
16380 currAlloc->SetUserData(this, VMA_NULL);
16381 m_AllocationObjectAllocator.Free(currAlloc);
16382 }
16383
16384 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16385 }
16386
16387 return res;
16388 }
16389
AllocateDedicatedMemoryPage(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,void * pUserData,VmaAllocation * pAllocation)16390 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
16391 VkDeviceSize size,
16392 VmaSuballocationType suballocType,
16393 uint32_t memTypeIndex,
16394 const VkMemoryAllocateInfo& allocInfo,
16395 bool map,
16396 bool isUserDataString,
16397 void* pUserData,
16398 VmaAllocation* pAllocation)
16399 {
16400 VkDeviceMemory hMemory = VK_NULL_HANDLE;
16401 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
16402 if(res < 0)
16403 {
16404 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
16405 return res;
16406 }
16407
16408 void* pMappedData = VMA_NULL;
16409 if(map)
16410 {
16411 res = (*m_VulkanFunctions.vkMapMemory)(
16412 m_hDevice,
16413 hMemory,
16414 0,
16415 VK_WHOLE_SIZE,
16416 0,
16417 &pMappedData);
16418 if(res < 0)
16419 {
16420 VMA_DEBUG_LOG(" vkMapMemory FAILED");
16421 FreeVulkanMemory(memTypeIndex, size, hMemory);
16422 return res;
16423 }
16424 }
16425
16426 *pAllocation = m_AllocationObjectAllocator.Allocate(m_CurrentFrameIndex.load(), isUserDataString);
16427 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
16428 (*pAllocation)->SetUserData(this, pUserData);
16429 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
16430 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16431 {
16432 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
16433 }
16434
16435 return VK_SUCCESS;
16436 }
16437
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16438 void VmaAllocator_T::GetBufferMemoryRequirements(
16439 VkBuffer hBuffer,
16440 VkMemoryRequirements& memReq,
16441 bool& requiresDedicatedAllocation,
16442 bool& prefersDedicatedAllocation) const
16443 {
16444 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16445 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16446 {
16447 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
16448 memReqInfo.buffer = hBuffer;
16449
16450 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16451
16452 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16453 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16454
16455 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16456
16457 memReq = memReq2.memoryRequirements;
16458 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16459 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16460 }
16461 else
16462 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16463 {
16464 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
16465 requiresDedicatedAllocation = false;
16466 prefersDedicatedAllocation = false;
16467 }
16468 }
16469
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)16470 void VmaAllocator_T::GetImageMemoryRequirements(
16471 VkImage hImage,
16472 VkMemoryRequirements& memReq,
16473 bool& requiresDedicatedAllocation,
16474 bool& prefersDedicatedAllocation) const
16475 {
16476 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16477 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
16478 {
16479 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
16480 memReqInfo.image = hImage;
16481
16482 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
16483
16484 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
16485 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
16486
16487 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
16488
16489 memReq = memReq2.memoryRequirements;
16490 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
16491 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
16492 }
16493 else
16494 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
16495 {
16496 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
16497 requiresDedicatedAllocation = false;
16498 prefersDedicatedAllocation = false;
16499 }
16500 }
16501
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkBufferUsageFlags dedicatedBufferUsage,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)16502 VkResult VmaAllocator_T::AllocateMemory(
16503 const VkMemoryRequirements& vkMemReq,
16504 bool requiresDedicatedAllocation,
16505 bool prefersDedicatedAllocation,
16506 VkBuffer dedicatedBuffer,
16507 VkBufferUsageFlags dedicatedBufferUsage,
16508 VkImage dedicatedImage,
16509 const VmaAllocationCreateInfo& createInfo,
16510 VmaSuballocationType suballocType,
16511 size_t allocationCount,
16512 VmaAllocation* pAllocations)
16513 {
16514 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16515
16516 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
16517
16518 if(vkMemReq.size == 0)
16519 {
16520 return VK_ERROR_VALIDATION_FAILED_EXT;
16521 }
16522 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
16523 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16524 {
16525 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
16526 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16527 }
16528 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16529 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
16530 {
16531 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
16532 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16533 }
16534 if(requiresDedicatedAllocation)
16535 {
16536 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
16537 {
16538 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
16539 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16540 }
16541 if(createInfo.pool != VK_NULL_HANDLE)
16542 {
16543 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
16544 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16545 }
16546 }
16547 if((createInfo.pool != VK_NULL_HANDLE) &&
16548 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
16549 {
16550 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
16551 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16552 }
16553
16554 if(createInfo.pool != VK_NULL_HANDLE)
16555 {
16556 const VkDeviceSize alignmentForPool = VMA_MAX(
16557 vkMemReq.alignment,
16558 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
16559
16560 VmaAllocationCreateInfo createInfoForPool = createInfo;
16561 // If memory type is not HOST_VISIBLE, disable MAPPED.
16562 if((createInfoForPool.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
16563 (m_MemProps.memoryTypes[createInfo.pool->m_BlockVector.GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
16564 {
16565 createInfoForPool.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
16566 }
16567
16568 return createInfo.pool->m_BlockVector.Allocate(
16569 m_CurrentFrameIndex.load(),
16570 vkMemReq.size,
16571 alignmentForPool,
16572 createInfoForPool,
16573 suballocType,
16574 allocationCount,
16575 pAllocations);
16576 }
16577 else
16578 {
16579 // Bit mask of memory Vulkan types acceptable for this allocation.
16580 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16581 uint32_t memTypeIndex = UINT32_MAX;
16582 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16583 if(res == VK_SUCCESS)
16584 {
16585 VkDeviceSize alignmentForMemType = VMA_MAX(
16586 vkMemReq.alignment,
16587 GetMemoryTypeMinAlignment(memTypeIndex));
16588
16589 res = AllocateMemoryOfType(
16590 vkMemReq.size,
16591 alignmentForMemType,
16592 requiresDedicatedAllocation || prefersDedicatedAllocation,
16593 dedicatedBuffer,
16594 dedicatedBufferUsage,
16595 dedicatedImage,
16596 createInfo,
16597 memTypeIndex,
16598 suballocType,
16599 allocationCount,
16600 pAllocations);
16601 // Succeeded on first try.
16602 if(res == VK_SUCCESS)
16603 {
16604 return res;
16605 }
16606 // Allocation from this memory type failed. Try other compatible memory types.
16607 else
16608 {
16609 for(;;)
16610 {
16611 // Remove old memTypeIndex from list of possibilities.
16612 memoryTypeBits &= ~(1u << memTypeIndex);
16613 // Find alternative memTypeIndex.
16614 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16615 if(res == VK_SUCCESS)
16616 {
16617 alignmentForMemType = VMA_MAX(
16618 vkMemReq.alignment,
16619 GetMemoryTypeMinAlignment(memTypeIndex));
16620
16621 res = AllocateMemoryOfType(
16622 vkMemReq.size,
16623 alignmentForMemType,
16624 requiresDedicatedAllocation || prefersDedicatedAllocation,
16625 dedicatedBuffer,
16626 dedicatedBufferUsage,
16627 dedicatedImage,
16628 createInfo,
16629 memTypeIndex,
16630 suballocType,
16631 allocationCount,
16632 pAllocations);
16633 // Allocation from this alternative memory type succeeded.
16634 if(res == VK_SUCCESS)
16635 {
16636 return res;
16637 }
16638 // else: Allocation from this memory type failed. Try next one - next loop iteration.
16639 }
16640 // No other matching memory type index could be found.
16641 else
16642 {
16643 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
16644 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
16645 }
16646 }
16647 }
16648 }
16649 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
16650 else
16651 return res;
16652 }
16653 }
16654
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)16655 void VmaAllocator_T::FreeMemory(
16656 size_t allocationCount,
16657 const VmaAllocation* pAllocations)
16658 {
16659 VMA_ASSERT(pAllocations);
16660
16661 for(size_t allocIndex = allocationCount; allocIndex--; )
16662 {
16663 VmaAllocation allocation = pAllocations[allocIndex];
16664
16665 if(allocation != VK_NULL_HANDLE)
16666 {
16667 if(TouchAllocation(allocation))
16668 {
16669 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16670 {
16671 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16672 }
16673
16674 switch(allocation->GetType())
16675 {
16676 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16677 {
16678 VmaBlockVector* pBlockVector = VMA_NULL;
16679 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16680 if(hPool != VK_NULL_HANDLE)
16681 {
16682 pBlockVector = &hPool->m_BlockVector;
16683 }
16684 else
16685 {
16686 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16687 pBlockVector = m_pBlockVectors[memTypeIndex];
16688 }
16689 pBlockVector->Free(allocation);
16690 }
16691 break;
16692 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
16693 FreeDedicatedMemory(allocation);
16694 break;
16695 default:
16696 VMA_ASSERT(0);
16697 }
16698 }
16699
16700 // Do this regardless of whether the allocation is lost. Lost allocations still account to Budget.AllocationBytes.
16701 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16702 allocation->SetUserData(this, VMA_NULL);
16703 m_AllocationObjectAllocator.Free(allocation);
16704 }
16705 }
16706 }
16707
16708 // 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)16709 VkResult VmaAllocator_T::AllocateReservedMemory(
16710 const VkMemoryRequirements& vkMemReq,
16711 bool requiresDedicatedAllocation,
16712 bool prefersDedicatedAllocation,
16713 VkBuffer dedicatedBuffer,
16714 VkBufferUsageFlags dedicatedBufferUsage, // UINT32_MAX when unknown.
16715 VkImage dedicatedImage,
16716 const VmaAllocationCreateInfo& createInfo,
16717 VmaSuballocationType suballocType,
16718 size_t allocationCount,
16719 VmaAllocation* pAllocations)
16720 {
16721 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
16722 if (allocationCount != 1) {
16723 return VK_NOT_READY;
16724 }
16725 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
16726 uint32_t memTypeIndex = UINT32_MAX;
16727 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
16728 if(res != VK_SUCCESS) {
16729 return res;
16730 }
16731 VkDeviceSize alignmentForMemType = VMA_MAX(
16732 vkMemReq.alignment,
16733 GetMemoryTypeMinAlignment(memTypeIndex));
16734 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16735 VMA_ASSERT(reservedBlockVector);
16736
16737 res = reservedBlockVector->AllocateReserved(
16738 m_CurrentFrameIndex.load(),
16739 vkMemReq.size,
16740 alignmentForMemType,
16741 createInfo,
16742 suballocType,
16743 pAllocations);
16744 return res;
16745 }
16746
FreeReservedMemory(size_t allocationCount,const VmaAllocation * pAllocations)16747 void VmaAllocator_T::FreeReservedMemory(
16748 size_t allocationCount,
16749 const VmaAllocation* pAllocations)
16750 {
16751 VMA_ASSERT(pAllocations);
16752
16753 for(size_t allocIndex = allocationCount; allocIndex--; )
16754 {
16755 VmaAllocation allocation = pAllocations[allocIndex];
16756
16757 if(allocation != VK_NULL_HANDLE)
16758 {
16759 if(TouchAllocation(allocation))
16760 {
16761 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
16762 {
16763 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
16764 }
16765
16766 switch(allocation->GetType())
16767 {
16768 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
16769 {
16770 VmaBlockVector* pBlockVector = VMA_NULL;
16771 VmaPool hPool = allocation->GetBlock()->GetParentPool();
16772 if(hPool != VK_NULL_HANDLE)
16773 {
16774 pBlockVector = &hPool->m_BlockVector;
16775 }
16776 else
16777 {
16778 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16779 pBlockVector = m_pReservedBlockVectors[memTypeIndex];
16780 }
16781 VMA_ASSERT(pBlockVector);
16782 pBlockVector->FreeReserved(allocation);
16783 }
16784 break;
16785 default:
16786 VMA_ASSERT(0);
16787 }
16788 }
16789 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
16790 }
16791 }
16792 }
16793
SwapReservedBlock(VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16794 VkResult VmaAllocator_T::SwapReservedBlock(
16795 VkImage image,
16796 const VmaAllocationCreateInfo* pCreateInfo,
16797 VmaAllocation* pAllocation,
16798 VmaAllocationInfo* pAllocationInfo)
16799 {
16800 VmaAllocation oldAllocation = *pAllocation;
16801 if (oldAllocation->GetType() != VmaAllocation_T::ALLOCATION_TYPE_BLOCK) {
16802 return VK_ERROR_UNKNOWN;
16803 }
16804 VmaPool hPool = oldAllocation->GetBlock()->GetParentPool();
16805 if(hPool != VK_NULL_HANDLE) {
16806 return VK_ERROR_UNKNOWN;
16807 }
16808 const uint32_t memTypeIndex = oldAllocation->GetMemoryTypeIndex();
16809 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16810 if (reservedBlockVector->IsEmpty()) {
16811 return VK_NOT_READY;
16812 }
16813 if (!reservedBlockVector->IsLastBlockBindComplete()) {
16814 return VK_INCOMPLETE;
16815 }
16816
16817 VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
16818 SwapLastBlock(blockVector, reservedBlockVector);
16819 return VK_SUCCESS;
16820 }
16821
GetPreAllocBlockSize()16822 uint32_t VmaAllocator_T::GetPreAllocBlockSize()
16823 {
16824 VmaBlockVector* reservedBlockVector = m_pReservedBlockVectors[0];
16825 VMA_ASSERT(reservedBlockVector);
16826 return reservedBlockVector->GetBlockCount();
16827 }
16828
ResizeAllocation(const VmaAllocation alloc,VkDeviceSize newSize)16829 VkResult VmaAllocator_T::ResizeAllocation(
16830 const VmaAllocation alloc,
16831 VkDeviceSize newSize)
16832 {
16833 // This function is deprecated and so it does nothing. It's left for backward compatibility.
16834 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
16835 {
16836 return VK_ERROR_VALIDATION_FAILED_EXT;
16837 }
16838 if(newSize == alloc->GetSize())
16839 {
16840 return VK_SUCCESS;
16841 }
16842 return VK_ERROR_OUT_OF_POOL_MEMORY;
16843 }
16844
CalculateStats(VmaStats * pStats)16845 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
16846 {
16847 // Initialize.
16848 InitStatInfo(pStats->total);
16849 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
16850 InitStatInfo(pStats->memoryType[i]);
16851 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
16852 InitStatInfo(pStats->memoryHeap[i]);
16853
16854 // Process default pools.
16855 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16856 {
16857 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16858 VMA_ASSERT(pBlockVector);
16859 pBlockVector->AddStats(pStats);
16860 }
16861
16862 // Process custom pools.
16863 {
16864 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
16865 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
16866 {
16867 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
16868 }
16869 }
16870
16871 // Process dedicated allocations.
16872 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
16873 {
16874 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
16875 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
16876 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
16877 VMA_ASSERT(pDedicatedAllocVector);
16878 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
16879 {
16880 VmaStatInfo allocationStatInfo;
16881 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
16882 VmaAddStatInfo(pStats->total, allocationStatInfo);
16883 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
16884 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
16885 }
16886 }
16887
16888 // Postprocess.
16889 VmaPostprocessCalcStatInfo(pStats->total);
16890 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
16891 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
16892 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
16893 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
16894 }
16895
FreeEmptyBlock()16896 void VmaAllocator_T::FreeEmptyBlock()
16897 {
16898 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
16899 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
16900 VMA_ASSERT(pBlockVector);
16901 pBlockVector->FreeEmptyBlock();
16902
16903 VmaBlockVector* const pReservedBlockVector = m_pReservedBlockVectors[memTypeIndex];
16904 VMA_ASSERT(pReservedBlockVector);
16905 pReservedBlockVector->FreeEmptyBlock();
16906 }
16907 }
16908
GetBudget(VmaBudget * outBudget,uint32_t firstHeap,uint32_t heapCount)16909 void VmaAllocator_T::GetBudget(VmaBudget* outBudget, uint32_t firstHeap, uint32_t heapCount)
16910 {
16911 #if VMA_MEMORY_BUDGET
16912 if(m_UseExtMemoryBudget)
16913 {
16914 if(m_Budget.m_OperationsSinceBudgetFetch < 30)
16915 {
16916 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
16917 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16918 {
16919 const uint32_t heapIndex = firstHeap + i;
16920
16921 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16922 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16923
16924 if(m_Budget.m_VulkanUsage[heapIndex] + outBudget->blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
16925 {
16926 outBudget->usage = m_Budget.m_VulkanUsage[heapIndex] +
16927 outBudget->blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
16928 }
16929 else
16930 {
16931 outBudget->usage = 0;
16932 }
16933
16934 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
16935 outBudget->budget = VMA_MIN(
16936 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
16937 }
16938 }
16939 else
16940 {
16941 UpdateVulkanBudget(); // Outside of mutex lock
16942 GetBudget(outBudget, firstHeap, heapCount); // Recursion
16943 }
16944 }
16945 else
16946 #endif
16947 {
16948 for(uint32_t i = 0; i < heapCount; ++i, ++outBudget)
16949 {
16950 const uint32_t heapIndex = firstHeap + i;
16951
16952 outBudget->blockBytes = m_Budget.m_BlockBytes[heapIndex];
16953 outBudget->allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
16954
16955 outBudget->usage = outBudget->blockBytes;
16956 outBudget->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
16957 }
16958 }
16959 }
16960
16961 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
16962
DefragmentationBegin(const VmaDefragmentationInfo2 & info,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)16963 VkResult VmaAllocator_T::DefragmentationBegin(
16964 const VmaDefragmentationInfo2& info,
16965 VmaDefragmentationStats* pStats,
16966 VmaDefragmentationContext* pContext)
16967 {
16968 if(info.pAllocationsChanged != VMA_NULL)
16969 {
16970 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
16971 }
16972
16973 *pContext = vma_new(this, VmaDefragmentationContext_T)(
16974 this, m_CurrentFrameIndex.load(), info.flags, pStats);
16975
16976 (*pContext)->AddPools(info.poolCount, info.pPools);
16977 (*pContext)->AddAllocations(
16978 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
16979
16980 VkResult res = (*pContext)->Defragment(
16981 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
16982 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
16983 info.commandBuffer, pStats, info.flags);
16984
16985 if(res != VK_NOT_READY)
16986 {
16987 vma_delete(this, *pContext);
16988 *pContext = VMA_NULL;
16989 }
16990
16991 return res;
16992 }
16993
DefragmentationEnd(VmaDefragmentationContext context)16994 VkResult VmaAllocator_T::DefragmentationEnd(
16995 VmaDefragmentationContext context)
16996 {
16997 vma_delete(this, context);
16998 return VK_SUCCESS;
16999 }
17000
DefragmentationPassBegin(VmaDefragmentationPassInfo * pInfo,VmaDefragmentationContext context)17001 VkResult VmaAllocator_T::DefragmentationPassBegin(
17002 VmaDefragmentationPassInfo* pInfo,
17003 VmaDefragmentationContext context)
17004 {
17005 return context->DefragmentPassBegin(pInfo);
17006 }
DefragmentationPassEnd(VmaDefragmentationContext context)17007 VkResult VmaAllocator_T::DefragmentationPassEnd(
17008 VmaDefragmentationContext context)
17009 {
17010 return context->DefragmentPassEnd();
17011
17012 }
17013
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)17014 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
17015 {
17016 if(hAllocation->CanBecomeLost())
17017 {
17018 /*
17019 Warning: This is a carefully designed algorithm.
17020 Do not modify unless you really know what you're doing :)
17021 */
17022 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17023 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17024 for(;;)
17025 {
17026 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17027 {
17028 pAllocationInfo->memoryType = UINT32_MAX;
17029 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
17030 pAllocationInfo->offset = 0;
17031 pAllocationInfo->size = hAllocation->GetSize();
17032 pAllocationInfo->pMappedData = VMA_NULL;
17033 pAllocationInfo->pUserData = hAllocation->GetUserData();
17034 return;
17035 }
17036 else if(localLastUseFrameIndex == localCurrFrameIndex)
17037 {
17038 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17039 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17040 pAllocationInfo->offset = hAllocation->GetOffset();
17041 pAllocationInfo->size = hAllocation->GetSize();
17042 pAllocationInfo->pMappedData = VMA_NULL;
17043 pAllocationInfo->pUserData = hAllocation->GetUserData();
17044 return;
17045 }
17046 else // Last use time earlier than current time.
17047 {
17048 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17049 {
17050 localLastUseFrameIndex = localCurrFrameIndex;
17051 }
17052 }
17053 }
17054 }
17055 else
17056 {
17057 #if VMA_STATS_STRING_ENABLED
17058 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17059 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17060 for(;;)
17061 {
17062 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17063 if(localLastUseFrameIndex == localCurrFrameIndex)
17064 {
17065 break;
17066 }
17067 else // Last use time earlier than current time.
17068 {
17069 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17070 {
17071 localLastUseFrameIndex = localCurrFrameIndex;
17072 }
17073 }
17074 }
17075 #endif
17076
17077 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
17078 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
17079 pAllocationInfo->offset = hAllocation->GetOffset();
17080 pAllocationInfo->size = hAllocation->GetSize();
17081 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
17082 pAllocationInfo->pUserData = hAllocation->GetUserData();
17083 }
17084 }
17085
TouchAllocation(VmaAllocation hAllocation)17086 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
17087 {
17088 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
17089 if(hAllocation->CanBecomeLost())
17090 {
17091 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17092 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17093 for(;;)
17094 {
17095 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
17096 {
17097 return false;
17098 }
17099 else if(localLastUseFrameIndex == localCurrFrameIndex)
17100 {
17101 return true;
17102 }
17103 else // Last use time earlier than current time.
17104 {
17105 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17106 {
17107 localLastUseFrameIndex = localCurrFrameIndex;
17108 }
17109 }
17110 }
17111 }
17112 else
17113 {
17114 #if VMA_STATS_STRING_ENABLED
17115 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
17116 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
17117 for(;;)
17118 {
17119 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
17120 if(localLastUseFrameIndex == localCurrFrameIndex)
17121 {
17122 break;
17123 }
17124 else // Last use time earlier than current time.
17125 {
17126 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
17127 {
17128 localLastUseFrameIndex = localCurrFrameIndex;
17129 }
17130 }
17131 }
17132 #endif
17133
17134 return true;
17135 }
17136 }
17137
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)17138 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
17139 {
17140 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
17141
17142 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
17143
17144 if(newCreateInfo.maxBlockCount == 0)
17145 {
17146 newCreateInfo.maxBlockCount = SIZE_MAX;
17147 }
17148 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
17149 {
17150 return VK_ERROR_INITIALIZATION_FAILED;
17151 }
17152 // Memory type index out of range or forbidden.
17153 if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
17154 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
17155 {
17156 return VK_ERROR_FEATURE_NOT_PRESENT;
17157 }
17158
17159 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
17160
17161 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
17162
17163 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
17164 if(res != VK_SUCCESS)
17165 {
17166 vma_delete(this, *pPool);
17167 *pPool = VMA_NULL;
17168 return res;
17169 }
17170
17171 // Add to m_Pools.
17172 {
17173 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17174 (*pPool)->SetId(m_NextPoolId++);
17175 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
17176 }
17177
17178 return VK_SUCCESS;
17179 }
17180
DestroyPool(VmaPool pool)17181 void VmaAllocator_T::DestroyPool(VmaPool pool)
17182 {
17183 // Remove from m_Pools.
17184 {
17185 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
17186 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
17187 VMA_ASSERT(success && "Pool not found in Allocator.");
17188 }
17189
17190 vma_delete(this, pool);
17191 }
17192
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)17193 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
17194 {
17195 pool->m_BlockVector.GetPoolStats(pPoolStats);
17196 }
17197
SetCurrentFrameIndex(uint32_t frameIndex)17198 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
17199 {
17200 m_CurrentFrameIndex.store(frameIndex);
17201
17202 #if VMA_MEMORY_BUDGET
17203 if(m_UseExtMemoryBudget)
17204 {
17205 UpdateVulkanBudget();
17206 }
17207 #endif // #if VMA_MEMORY_BUDGET
17208 }
17209
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)17210 void VmaAllocator_T::MakePoolAllocationsLost(
17211 VmaPool hPool,
17212 size_t* pLostAllocationCount)
17213 {
17214 hPool->m_BlockVector.MakePoolAllocationsLost(
17215 m_CurrentFrameIndex.load(),
17216 pLostAllocationCount);
17217 }
17218
CheckPoolCorruption(VmaPool hPool)17219 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
17220 {
17221 return hPool->m_BlockVector.CheckCorruption();
17222 }
17223
CheckCorruption(uint32_t memoryTypeBits)17224 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
17225 {
17226 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
17227
17228 // Process default pools.
17229 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17230 {
17231 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
17232 {
17233 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
17234 VMA_ASSERT(pBlockVector);
17235 VkResult localRes = pBlockVector->CheckCorruption();
17236 switch(localRes)
17237 {
17238 case VK_ERROR_FEATURE_NOT_PRESENT:
17239 break;
17240 case VK_SUCCESS:
17241 finalRes = VK_SUCCESS;
17242 break;
17243 default:
17244 return localRes;
17245 }
17246 }
17247 }
17248
17249 // Process custom pools.
17250 {
17251 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17252 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
17253 {
17254 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
17255 {
17256 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
17257 switch(localRes)
17258 {
17259 case VK_ERROR_FEATURE_NOT_PRESENT:
17260 break;
17261 case VK_SUCCESS:
17262 finalRes = VK_SUCCESS;
17263 break;
17264 default:
17265 return localRes;
17266 }
17267 }
17268 }
17269 }
17270
17271 return finalRes;
17272 }
17273
CreateLostAllocation(VmaAllocation * pAllocation)17274 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
17275 {
17276 *pAllocation = m_AllocationObjectAllocator.Allocate(VMA_FRAME_INDEX_LOST, false);
17277 (*pAllocation)->InitLost();
17278 }
17279
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)17280 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
17281 {
17282 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
17283
17284 // HeapSizeLimit is in effect for this heap.
17285 if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
17286 {
17287 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
17288 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
17289 for(;;)
17290 {
17291 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
17292 if(blockBytesAfterAllocation > heapSize)
17293 {
17294 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
17295 }
17296 if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
17297 {
17298 break;
17299 }
17300 }
17301 }
17302 else
17303 {
17304 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
17305 }
17306
17307 // VULKAN CALL vkAllocateMemory.
17308 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
17309
17310 if(res == VK_SUCCESS)
17311 {
17312 #if VMA_MEMORY_BUDGET
17313 ++m_Budget.m_OperationsSinceBudgetFetch;
17314 #endif
17315
17316 // Informative callback.
17317 if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
17318 {
17319 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
17320 }
17321 }
17322 else
17323 {
17324 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
17325 }
17326
17327 return res;
17328 }
17329
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)17330 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
17331 {
17332 // Informative callback.
17333 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
17334 {
17335 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
17336 }
17337
17338 // VULKAN CALL vkFreeMemory.
17339 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
17340
17341 m_Budget.m_BlockBytes[MemoryTypeIndexToHeapIndex(memoryType)] -= size;
17342 }
17343
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)17344 VkResult VmaAllocator_T::BindVulkanBuffer(
17345 VkDeviceMemory memory,
17346 VkDeviceSize memoryOffset,
17347 VkBuffer buffer,
17348 const void* pNext)
17349 {
17350 if(pNext != VMA_NULL)
17351 {
17352 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17353 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17354 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
17355 {
17356 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
17357 bindBufferMemoryInfo.pNext = pNext;
17358 bindBufferMemoryInfo.buffer = buffer;
17359 bindBufferMemoryInfo.memory = memory;
17360 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17361 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17362 }
17363 else
17364 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17365 {
17366 return VK_ERROR_EXTENSION_NOT_PRESENT;
17367 }
17368 }
17369 else
17370 {
17371 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
17372 }
17373 }
17374
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)17375 VkResult VmaAllocator_T::BindVulkanImage(
17376 VkDeviceMemory memory,
17377 VkDeviceSize memoryOffset,
17378 VkImage image,
17379 const void* pNext)
17380 {
17381 if(pNext != VMA_NULL)
17382 {
17383 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
17384 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
17385 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
17386 {
17387 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
17388 bindBufferMemoryInfo.pNext = pNext;
17389 bindBufferMemoryInfo.image = image;
17390 bindBufferMemoryInfo.memory = memory;
17391 bindBufferMemoryInfo.memoryOffset = memoryOffset;
17392 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
17393 }
17394 else
17395 #endif // #if VMA_BIND_MEMORY2
17396 {
17397 return VK_ERROR_EXTENSION_NOT_PRESENT;
17398 }
17399 }
17400 else
17401 {
17402 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
17403 }
17404 }
17405
Map(VmaAllocation hAllocation,void ** ppData)17406 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
17407 {
17408 if(hAllocation->CanBecomeLost())
17409 {
17410 return VK_ERROR_MEMORY_MAP_FAILED;
17411 }
17412
17413 switch(hAllocation->GetType())
17414 {
17415 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17416 {
17417 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17418 char *pBytes = VMA_NULL;
17419 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
17420 if(res == VK_SUCCESS)
17421 {
17422 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
17423 hAllocation->BlockAllocMap();
17424 }
17425 return res;
17426 }
17427 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17428 return hAllocation->DedicatedAllocMap(this, ppData);
17429 default:
17430 VMA_ASSERT(0);
17431 return VK_ERROR_MEMORY_MAP_FAILED;
17432 }
17433 }
17434
Unmap(VmaAllocation hAllocation)17435 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
17436 {
17437 switch(hAllocation->GetType())
17438 {
17439 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17440 {
17441 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17442 hAllocation->BlockAllocUnmap();
17443 pBlock->Unmap(this, 1);
17444 }
17445 break;
17446 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17447 hAllocation->DedicatedAllocUnmap(this);
17448 break;
17449 default:
17450 VMA_ASSERT(0);
17451 }
17452 }
17453
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)17454 VkResult VmaAllocator_T::BindBufferMemory(
17455 VmaAllocation hAllocation,
17456 VkDeviceSize allocationLocalOffset,
17457 VkBuffer hBuffer,
17458 const void* pNext)
17459 {
17460 VkResult res = VK_SUCCESS;
17461 switch(hAllocation->GetType())
17462 {
17463 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17464 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
17465 break;
17466 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17467 {
17468 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
17469 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
17470 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
17471 break;
17472 }
17473 default:
17474 VMA_ASSERT(0);
17475 }
17476 return res;
17477 }
17478
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)17479 VkResult VmaAllocator_T::BindImageMemory(
17480 VmaAllocation hAllocation,
17481 VkDeviceSize allocationLocalOffset,
17482 VkImage hImage,
17483 const void* pNext)
17484 {
17485 VkResult res = VK_SUCCESS;
17486 switch(hAllocation->GetType())
17487 {
17488 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17489 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
17490 break;
17491 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17492 {
17493 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
17494 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
17495 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
17496 if (res == VK_SUCCESS) {
17497 pBlock->SetBindCompleteFlag(true);
17498 }
17499 break;
17500 }
17501 default:
17502 VMA_ASSERT(0);
17503 }
17504 return res;
17505 }
17506
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)17507 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
17508 VmaAllocation hAllocation,
17509 VkDeviceSize offset, VkDeviceSize size,
17510 VMA_CACHE_OPERATION op)
17511 {
17512 VkResult res = VK_SUCCESS;
17513
17514 VkMappedMemoryRange memRange = {};
17515 if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
17516 {
17517 switch(op)
17518 {
17519 case VMA_CACHE_FLUSH:
17520 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
17521 break;
17522 case VMA_CACHE_INVALIDATE:
17523 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
17524 break;
17525 default:
17526 VMA_ASSERT(0);
17527 }
17528 }
17529 // else: Just ignore this call.
17530 return res;
17531 }
17532
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)17533 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
17534 uint32_t allocationCount,
17535 const VmaAllocation* allocations,
17536 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
17537 VMA_CACHE_OPERATION op)
17538 {
17539 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
17540 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
17541 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
17542
17543 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
17544 {
17545 const VmaAllocation alloc = allocations[allocIndex];
17546 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
17547 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
17548 VkMappedMemoryRange newRange;
17549 if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
17550 {
17551 ranges.push_back(newRange);
17552 }
17553 }
17554
17555 VkResult res = VK_SUCCESS;
17556 if(!ranges.empty())
17557 {
17558 switch(op)
17559 {
17560 case VMA_CACHE_FLUSH:
17561 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17562 break;
17563 case VMA_CACHE_INVALIDATE:
17564 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
17565 break;
17566 default:
17567 VMA_ASSERT(0);
17568 }
17569 }
17570 // else: Just ignore this call.
17571 return res;
17572 }
17573
FreeDedicatedMemory(const VmaAllocation allocation)17574 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
17575 {
17576 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
17577
17578 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17579 {
17580 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17581 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
17582 VMA_ASSERT(pDedicatedAllocations);
17583 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
17584 VMA_ASSERT(success);
17585 }
17586
17587 VkDeviceMemory hMemory = allocation->GetMemory();
17588
17589 /*
17590 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
17591 before vkFreeMemory.
17592
17593 if(allocation->GetMappedData() != VMA_NULL)
17594 {
17595 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
17596 }
17597 */
17598
17599 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
17600
17601 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
17602 }
17603
CalculateGpuDefragmentationMemoryTypeBits()17604 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
17605 {
17606 VkBufferCreateInfo dummyBufCreateInfo;
17607 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
17608
17609 uint32_t memoryTypeBits = 0;
17610
17611 // Create buffer.
17612 VkBuffer buf = VK_NULL_HANDLE;
17613 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
17614 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
17615 if(res == VK_SUCCESS)
17616 {
17617 // Query for supported memory types.
17618 VkMemoryRequirements memReq;
17619 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
17620 memoryTypeBits = memReq.memoryTypeBits;
17621
17622 // Destroy buffer.
17623 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
17624 }
17625
17626 return memoryTypeBits;
17627 }
17628
CalculateGlobalMemoryTypeBits()17629 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
17630 {
17631 // Make sure memory information is already fetched.
17632 VMA_ASSERT(GetMemoryTypeCount() > 0);
17633
17634 uint32_t memoryTypeBits = UINT32_MAX;
17635
17636 if(!m_UseAmdDeviceCoherentMemory)
17637 {
17638 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
17639 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17640 {
17641 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
17642 {
17643 memoryTypeBits &= ~(1u << memTypeIndex);
17644 }
17645 }
17646 }
17647
17648 return memoryTypeBits;
17649 }
17650
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)17651 bool VmaAllocator_T::GetFlushOrInvalidateRange(
17652 VmaAllocation allocation,
17653 VkDeviceSize offset, VkDeviceSize size,
17654 VkMappedMemoryRange& outRange) const
17655 {
17656 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
17657 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
17658 {
17659 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
17660 const VkDeviceSize allocationSize = allocation->GetSize();
17661 VMA_ASSERT(offset <= allocationSize);
17662
17663 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
17664 outRange.pNext = VMA_NULL;
17665 outRange.memory = allocation->GetMemory();
17666
17667 switch(allocation->GetType())
17668 {
17669 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
17670 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17671 if(size == VK_WHOLE_SIZE)
17672 {
17673 outRange.size = allocationSize - outRange.offset;
17674 }
17675 else
17676 {
17677 VMA_ASSERT(offset + size <= allocationSize);
17678 outRange.size = VMA_MIN(
17679 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
17680 allocationSize - outRange.offset);
17681 }
17682 break;
17683 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
17684 {
17685 // 1. Still within this allocation.
17686 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
17687 if(size == VK_WHOLE_SIZE)
17688 {
17689 size = allocationSize - offset;
17690 }
17691 else
17692 {
17693 VMA_ASSERT(offset + size <= allocationSize);
17694 }
17695 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
17696
17697 // 2. Adjust to whole block.
17698 const VkDeviceSize allocationOffset = allocation->GetOffset();
17699 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
17700 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
17701 outRange.offset += allocationOffset;
17702 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
17703
17704 break;
17705 }
17706 default:
17707 VMA_ASSERT(0);
17708 }
17709 return true;
17710 }
17711 return false;
17712 }
17713
17714 #if VMA_MEMORY_BUDGET
17715
UpdateVulkanBudget()17716 void VmaAllocator_T::UpdateVulkanBudget()
17717 {
17718 VMA_ASSERT(m_UseExtMemoryBudget);
17719
17720 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
17721
17722 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
17723 VmaPnextChainPushFront(&memProps, &budgetProps);
17724
17725 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
17726
17727 {
17728 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
17729
17730 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
17731 {
17732 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
17733 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
17734 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
17735
17736 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
17737 if(m_Budget.m_VulkanBudget[heapIndex] == 0)
17738 {
17739 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
17740 }
17741 else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
17742 {
17743 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
17744 }
17745 if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
17746 {
17747 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
17748 }
17749 }
17750 m_Budget.m_OperationsSinceBudgetFetch = 0;
17751 }
17752 }
17753
17754 #endif // #if VMA_MEMORY_BUDGET
17755
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)17756 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
17757 {
17758 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
17759 !hAllocation->CanBecomeLost() &&
17760 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
17761 {
17762 void* pData = VMA_NULL;
17763 VkResult res = Map(hAllocation, &pData);
17764 if(res == VK_SUCCESS)
17765 {
17766 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
17767 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
17768 Unmap(hAllocation);
17769 }
17770 else
17771 {
17772 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
17773 }
17774 }
17775 }
17776
GetGpuDefragmentationMemoryTypeBits()17777 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
17778 {
17779 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
17780 if(memoryTypeBits == UINT32_MAX)
17781 {
17782 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
17783 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
17784 }
17785 return memoryTypeBits;
17786 }
17787
17788 #if VMA_STATS_STRING_ENABLED
17789
PrintDetailedMap(VmaJsonWriter & json)17790 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
17791 {
17792 bool dedicatedAllocationsStarted = false;
17793 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17794 {
17795 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
17796 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
17797 VMA_ASSERT(pDedicatedAllocVector);
17798 if(pDedicatedAllocVector->empty() == false)
17799 {
17800 if(dedicatedAllocationsStarted == false)
17801 {
17802 dedicatedAllocationsStarted = true;
17803 json.WriteString("DedicatedAllocations");
17804 json.BeginObject();
17805 }
17806
17807 json.BeginString("Type ");
17808 json.ContinueString(memTypeIndex);
17809 json.EndString();
17810
17811 json.BeginArray();
17812
17813 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
17814 {
17815 json.BeginObject(true);
17816 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
17817 hAlloc->PrintParameters(json);
17818 json.EndObject();
17819 }
17820
17821 json.EndArray();
17822 }
17823 }
17824 if(dedicatedAllocationsStarted)
17825 {
17826 json.EndObject();
17827 }
17828
17829 {
17830 bool allocationsStarted = false;
17831 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
17832 {
17833 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
17834 {
17835 if(allocationsStarted == false)
17836 {
17837 allocationsStarted = true;
17838 json.WriteString("DefaultPools");
17839 json.BeginObject();
17840 }
17841
17842 json.BeginString("Type ");
17843 json.ContinueString(memTypeIndex);
17844 json.EndString();
17845
17846 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
17847 }
17848 }
17849 if(allocationsStarted)
17850 {
17851 json.EndObject();
17852 }
17853 }
17854
17855 // Custom pools
17856 {
17857 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
17858 const size_t poolCount = m_Pools.size();
17859 if(poolCount > 0)
17860 {
17861 json.WriteString("Pools");
17862 json.BeginObject();
17863 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
17864 {
17865 json.BeginString();
17866 json.ContinueString(m_Pools[poolIndex]->GetId());
17867 json.EndString();
17868
17869 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
17870 }
17871 json.EndObject();
17872 }
17873 }
17874 }
17875
17876 #endif // #if VMA_STATS_STRING_ENABLED
17877
17878 ////////////////////////////////////////////////////////////////////////////////
17879 // Public interface
17880
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)17881 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
17882 const VmaAllocatorCreateInfo* pCreateInfo,
17883 VmaAllocator* pAllocator)
17884 {
17885 VMA_ASSERT(pCreateInfo && pAllocator);
17886 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
17887 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 2));
17888 VMA_DEBUG_LOG("vmaCreateAllocator");
17889 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
17890 return (*pAllocator)->Init(pCreateInfo);
17891 }
17892
vmaDestroyAllocator(VmaAllocator allocator)17893 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
17894 VmaAllocator allocator)
17895 {
17896 if(allocator != VK_NULL_HANDLE)
17897 {
17898 VMA_DEBUG_LOG("vmaDestroyAllocator");
17899 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
17900 vma_delete(&allocationCallbacks, allocator);
17901 }
17902 }
17903
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)17904 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
17905 {
17906 VMA_ASSERT(allocator && pAllocatorInfo);
17907 pAllocatorInfo->instance = allocator->m_hInstance;
17908 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
17909 pAllocatorInfo->device = allocator->m_hDevice;
17910 }
17911
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)17912 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
17913 VmaAllocator allocator,
17914 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
17915 {
17916 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
17917 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
17918 }
17919
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)17920 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
17921 VmaAllocator allocator,
17922 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
17923 {
17924 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
17925 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
17926 }
17927
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)17928 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
17929 VmaAllocator allocator,
17930 uint32_t memoryTypeIndex,
17931 VkMemoryPropertyFlags* pFlags)
17932 {
17933 VMA_ASSERT(allocator && pFlags);
17934 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
17935 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
17936 }
17937
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)17938 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
17939 VmaAllocator allocator,
17940 uint32_t frameIndex)
17941 {
17942 VMA_ASSERT(allocator);
17943 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
17944
17945 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17946
17947 allocator->SetCurrentFrameIndex(frameIndex);
17948 }
17949
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)17950 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStats(
17951 VmaAllocator allocator,
17952 VmaStats* pStats)
17953 {
17954 VMA_ASSERT(allocator && pStats);
17955 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17956 allocator->CalculateStats(pStats);
17957 }
17958
vmaFreeEmptyBlock(VmaAllocator allocator)17959 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
17960 VmaAllocator allocator)
17961 {
17962 VMA_ASSERT(allocator);
17963 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17964 allocator->FreeEmptyBlock();
17965 }
17966
vmaGetBudget(VmaAllocator allocator,VmaBudget * pBudget)17967 VMA_CALL_PRE void VMA_CALL_POST vmaGetBudget(
17968 VmaAllocator allocator,
17969 VmaBudget* pBudget)
17970 {
17971 VMA_ASSERT(allocator && pBudget);
17972 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17973 allocator->GetBudget(pBudget, 0, allocator->GetMemoryHeapCount());
17974 }
17975
17976 #if VMA_STATS_STRING_ENABLED
17977
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)17978 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
17979 VmaAllocator allocator,
17980 char** ppStatsString,
17981 VkBool32 detailedMap)
17982 {
17983 VMA_ASSERT(allocator && ppStatsString);
17984 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17985
17986 VmaStringBuilder sb(allocator);
17987 {
17988 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
17989 json.BeginObject();
17990
17991 VmaBudget budget[VK_MAX_MEMORY_HEAPS];
17992 allocator->GetBudget(budget, 0, allocator->GetMemoryHeapCount());
17993
17994 VmaStats stats;
17995 allocator->CalculateStats(&stats);
17996
17997 json.WriteString("Total");
17998 VmaPrintStatInfo(json, stats.total);
17999
18000 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
18001 {
18002 json.BeginString("Heap ");
18003 json.ContinueString(heapIndex);
18004 json.EndString();
18005 json.BeginObject();
18006
18007 json.WriteString("Size");
18008 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
18009
18010 json.WriteString("Flags");
18011 json.BeginArray(true);
18012 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
18013 {
18014 json.WriteString("DEVICE_LOCAL");
18015 }
18016 json.EndArray();
18017
18018 json.WriteString("Budget");
18019 json.BeginObject();
18020 {
18021 json.WriteString("BlockBytes");
18022 json.WriteNumber(budget[heapIndex].blockBytes);
18023 json.WriteString("AllocationBytes");
18024 json.WriteNumber(budget[heapIndex].allocationBytes);
18025 json.WriteString("Usage");
18026 json.WriteNumber(budget[heapIndex].usage);
18027 json.WriteString("Budget");
18028 json.WriteNumber(budget[heapIndex].budget);
18029 }
18030 json.EndObject();
18031
18032 if(stats.memoryHeap[heapIndex].blockCount > 0)
18033 {
18034 json.WriteString("Stats");
18035 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
18036 }
18037
18038 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
18039 {
18040 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
18041 {
18042 json.BeginString("Type ");
18043 json.ContinueString(typeIndex);
18044 json.EndString();
18045
18046 json.BeginObject();
18047
18048 json.WriteString("Flags");
18049 json.BeginArray(true);
18050 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
18051 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
18052 {
18053 json.WriteString("DEVICE_LOCAL");
18054 }
18055 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
18056 {
18057 json.WriteString("HOST_VISIBLE");
18058 }
18059 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
18060 {
18061 json.WriteString("HOST_COHERENT");
18062 }
18063 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
18064 {
18065 json.WriteString("HOST_CACHED");
18066 }
18067 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
18068 {
18069 json.WriteString("LAZILY_ALLOCATED");
18070 }
18071 if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
18072 {
18073 json.WriteString(" PROTECTED");
18074 }
18075 if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
18076 {
18077 json.WriteString(" DEVICE_COHERENT");
18078 }
18079 if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
18080 {
18081 json.WriteString(" DEVICE_UNCACHED");
18082 }
18083 json.EndArray();
18084
18085 if(stats.memoryType[typeIndex].blockCount > 0)
18086 {
18087 json.WriteString("Stats");
18088 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
18089 }
18090
18091 json.EndObject();
18092 }
18093 }
18094
18095 json.EndObject();
18096 }
18097 if(detailedMap == VK_TRUE)
18098 {
18099 allocator->PrintDetailedMap(json);
18100 }
18101
18102 json.EndObject();
18103 }
18104
18105 const size_t len = sb.GetLength();
18106 char* const pChars = vma_new_array(allocator, char, len + 1);
18107 if(len > 0)
18108 {
18109 memcpy(pChars, sb.GetData(), len);
18110 }
18111 pChars[len] = '\0';
18112 *ppStatsString = pChars;
18113 }
18114
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)18115 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
18116 VmaAllocator allocator,
18117 char* pStatsString)
18118 {
18119 if(pStatsString != VMA_NULL)
18120 {
18121 VMA_ASSERT(allocator);
18122 size_t len = strlen(pStatsString);
18123 vma_delete_array(allocator, pStatsString, len + 1);
18124 }
18125 }
18126
18127 #endif // #if VMA_STATS_STRING_ENABLED
18128
18129 /*
18130 This function is not protected by any mutex because it just reads immutable data.
18131 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18132 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
18133 VmaAllocator allocator,
18134 uint32_t memoryTypeBits,
18135 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18136 uint32_t* pMemoryTypeIndex)
18137 {
18138 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18139 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18140 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18141
18142 memoryTypeBits &= allocator->GetGlobalMemoryTypeBits();
18143
18144 if(pAllocationCreateInfo->memoryTypeBits != 0)
18145 {
18146 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
18147 }
18148
18149 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
18150 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
18151 uint32_t notPreferredFlags = 0;
18152
18153 // Convert usage to requiredFlags and preferredFlags.
18154 switch(pAllocationCreateInfo->usage)
18155 {
18156 case VMA_MEMORY_USAGE_UNKNOWN:
18157 break;
18158 case VMA_MEMORY_USAGE_GPU_ONLY:
18159 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
18160 {
18161 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18162 }
18163 break;
18164 case VMA_MEMORY_USAGE_CPU_ONLY:
18165 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
18166 break;
18167 case VMA_MEMORY_USAGE_CPU_TO_GPU:
18168 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
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_GPU_TO_CPU:
18175 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
18176 preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
18177 break;
18178 case VMA_MEMORY_USAGE_CPU_COPY:
18179 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18180 break;
18181 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
18182 requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
18183 break;
18184 default:
18185 VMA_ASSERT(0);
18186 break;
18187 }
18188
18189 // Avoid DEVICE_COHERENT unless explicitly requested.
18190 if(((pAllocationCreateInfo->requiredFlags | pAllocationCreateInfo->preferredFlags) &
18191 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
18192 {
18193 notPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY;
18194 }
18195
18196 *pMemoryTypeIndex = UINT32_MAX;
18197 uint32_t minCost = UINT32_MAX;
18198 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
18199 memTypeIndex < allocator->GetMemoryTypeCount();
18200 ++memTypeIndex, memTypeBit <<= 1)
18201 {
18202 // This memory type is acceptable according to memoryTypeBits bitmask.
18203 if((memTypeBit & memoryTypeBits) != 0)
18204 {
18205 const VkMemoryPropertyFlags currFlags =
18206 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
18207 // This memory type contains requiredFlags.
18208 if((requiredFlags & ~currFlags) == 0)
18209 {
18210 // Calculate cost as number of bits from preferredFlags not present in this memory type.
18211 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags) +
18212 VmaCountBitsSet(currFlags & notPreferredFlags);
18213 // Remember memory type with lowest cost.
18214 if(currCost < minCost)
18215 {
18216 *pMemoryTypeIndex = memTypeIndex;
18217 if(currCost == 0)
18218 {
18219 return VK_SUCCESS;
18220 }
18221 minCost = currCost;
18222 }
18223 }
18224 }
18225 }
18226 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
18227 }
18228
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18229 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
18230 VmaAllocator allocator,
18231 const VkBufferCreateInfo* pBufferCreateInfo,
18232 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18233 uint32_t* pMemoryTypeIndex)
18234 {
18235 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18236 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
18237 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18238 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18239
18240 const VkDevice hDev = allocator->m_hDevice;
18241 VkBuffer hBuffer = VK_NULL_HANDLE;
18242 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
18243 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
18244 if(res == VK_SUCCESS)
18245 {
18246 VkMemoryRequirements memReq = {};
18247 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
18248 hDev, hBuffer, &memReq);
18249
18250 res = vmaFindMemoryTypeIndex(
18251 allocator,
18252 memReq.memoryTypeBits,
18253 pAllocationCreateInfo,
18254 pMemoryTypeIndex);
18255
18256 allocator->GetVulkanFunctions().vkDestroyBuffer(
18257 hDev, hBuffer, allocator->GetAllocationCallbacks());
18258 }
18259 return res;
18260 }
18261
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)18262 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
18263 VmaAllocator allocator,
18264 const VkImageCreateInfo* pImageCreateInfo,
18265 const VmaAllocationCreateInfo* pAllocationCreateInfo,
18266 uint32_t* pMemoryTypeIndex)
18267 {
18268 VMA_ASSERT(allocator != VK_NULL_HANDLE);
18269 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
18270 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
18271 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
18272
18273 const VkDevice hDev = allocator->m_hDevice;
18274 VkImage hImage = VK_NULL_HANDLE;
18275 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
18276 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
18277 if(res == VK_SUCCESS)
18278 {
18279 VkMemoryRequirements memReq = {};
18280 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
18281 hDev, hImage, &memReq);
18282
18283 res = vmaFindMemoryTypeIndex(
18284 allocator,
18285 memReq.memoryTypeBits,
18286 pAllocationCreateInfo,
18287 pMemoryTypeIndex);
18288
18289 allocator->GetVulkanFunctions().vkDestroyImage(
18290 hDev, hImage, allocator->GetAllocationCallbacks());
18291 }
18292 return res;
18293 }
18294
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)18295 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
18296 VmaAllocator allocator,
18297 const VmaPoolCreateInfo* pCreateInfo,
18298 VmaPool* pPool)
18299 {
18300 VMA_ASSERT(allocator && pCreateInfo && pPool);
18301
18302 VMA_DEBUG_LOG("vmaCreatePool");
18303
18304 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18305
18306 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
18307
18308 #if VMA_RECORDING_ENABLED
18309 if(allocator->GetRecorder() != VMA_NULL)
18310 {
18311 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
18312 }
18313 #endif
18314
18315 return res;
18316 }
18317
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)18318 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
18319 VmaAllocator allocator,
18320 VmaPool pool)
18321 {
18322 VMA_ASSERT(allocator);
18323
18324 if(pool == VK_NULL_HANDLE)
18325 {
18326 return;
18327 }
18328
18329 VMA_DEBUG_LOG("vmaDestroyPool");
18330
18331 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18332
18333 #if VMA_RECORDING_ENABLED
18334 if(allocator->GetRecorder() != VMA_NULL)
18335 {
18336 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
18337 }
18338 #endif
18339
18340 allocator->DestroyPool(pool);
18341 }
18342
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)18343 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStats(
18344 VmaAllocator allocator,
18345 VmaPool pool,
18346 VmaPoolStats* pPoolStats)
18347 {
18348 VMA_ASSERT(allocator && pool && pPoolStats);
18349
18350 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18351
18352 allocator->GetPoolStats(pool, pPoolStats);
18353 }
18354
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)18355 VMA_CALL_PRE void VMA_CALL_POST vmaMakePoolAllocationsLost(
18356 VmaAllocator allocator,
18357 VmaPool pool,
18358 size_t* pLostAllocationCount)
18359 {
18360 VMA_ASSERT(allocator && pool);
18361
18362 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18363
18364 #if VMA_RECORDING_ENABLED
18365 if(allocator->GetRecorder() != VMA_NULL)
18366 {
18367 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
18368 }
18369 #endif
18370
18371 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
18372 }
18373
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)18374 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
18375 {
18376 VMA_ASSERT(allocator && pool);
18377
18378 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18379
18380 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
18381
18382 return allocator->CheckPoolCorruption(pool);
18383 }
18384
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)18385 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
18386 VmaAllocator allocator,
18387 VmaPool pool,
18388 const char** ppName)
18389 {
18390 VMA_ASSERT(allocator && pool && ppName);
18391
18392 VMA_DEBUG_LOG("vmaGetPoolName");
18393
18394 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18395
18396 *ppName = pool->GetName();
18397 }
18398
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)18399 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
18400 VmaAllocator allocator,
18401 VmaPool pool,
18402 const char* pName)
18403 {
18404 VMA_ASSERT(allocator && pool);
18405
18406 VMA_DEBUG_LOG("vmaSetPoolName");
18407
18408 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18409
18410 pool->SetName(pName);
18411
18412 #if VMA_RECORDING_ENABLED
18413 if(allocator->GetRecorder() != VMA_NULL)
18414 {
18415 allocator->GetRecorder()->RecordSetPoolName(allocator->GetCurrentFrameIndex(), pool, pName);
18416 }
18417 #endif
18418 }
18419
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18420 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
18421 VmaAllocator allocator,
18422 const VkMemoryRequirements* pVkMemoryRequirements,
18423 const VmaAllocationCreateInfo* pCreateInfo,
18424 VmaAllocation* pAllocation,
18425 VmaAllocationInfo* pAllocationInfo)
18426 {
18427 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
18428
18429 VMA_DEBUG_LOG("vmaAllocateMemory");
18430
18431 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18432
18433 VkResult result = allocator->AllocateMemory(
18434 *pVkMemoryRequirements,
18435 false, // requiresDedicatedAllocation
18436 false, // prefersDedicatedAllocation
18437 VK_NULL_HANDLE, // dedicatedBuffer
18438 UINT32_MAX, // dedicatedBufferUsage
18439 VK_NULL_HANDLE, // dedicatedImage
18440 *pCreateInfo,
18441 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18442 1, // allocationCount
18443 pAllocation);
18444
18445 #if VMA_RECORDING_ENABLED
18446 if(allocator->GetRecorder() != VMA_NULL)
18447 {
18448 allocator->GetRecorder()->RecordAllocateMemory(
18449 allocator->GetCurrentFrameIndex(),
18450 *pVkMemoryRequirements,
18451 *pCreateInfo,
18452 *pAllocation);
18453 }
18454 #endif
18455
18456 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18457 {
18458 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18459 }
18460
18461 return result;
18462 }
18463
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)18464 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
18465 VmaAllocator allocator,
18466 const VkMemoryRequirements* pVkMemoryRequirements,
18467 const VmaAllocationCreateInfo* pCreateInfo,
18468 size_t allocationCount,
18469 VmaAllocation* pAllocations,
18470 VmaAllocationInfo* pAllocationInfo)
18471 {
18472 if(allocationCount == 0)
18473 {
18474 return VK_SUCCESS;
18475 }
18476
18477 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
18478
18479 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
18480
18481 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18482
18483 VkResult result = allocator->AllocateMemory(
18484 *pVkMemoryRequirements,
18485 false, // requiresDedicatedAllocation
18486 false, // prefersDedicatedAllocation
18487 VK_NULL_HANDLE, // dedicatedBuffer
18488 UINT32_MAX, // dedicatedBufferUsage
18489 VK_NULL_HANDLE, // dedicatedImage
18490 *pCreateInfo,
18491 VMA_SUBALLOCATION_TYPE_UNKNOWN,
18492 allocationCount,
18493 pAllocations);
18494
18495 #if VMA_RECORDING_ENABLED
18496 if(allocator->GetRecorder() != VMA_NULL)
18497 {
18498 allocator->GetRecorder()->RecordAllocateMemoryPages(
18499 allocator->GetCurrentFrameIndex(),
18500 *pVkMemoryRequirements,
18501 *pCreateInfo,
18502 (uint64_t)allocationCount,
18503 pAllocations);
18504 }
18505 #endif
18506
18507 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
18508 {
18509 for(size_t i = 0; i < allocationCount; ++i)
18510 {
18511 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
18512 }
18513 }
18514
18515 return result;
18516 }
18517
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18518 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
18519 VmaAllocator allocator,
18520 VkBuffer buffer,
18521 const VmaAllocationCreateInfo* pCreateInfo,
18522 VmaAllocation* pAllocation,
18523 VmaAllocationInfo* pAllocationInfo)
18524 {
18525 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18526
18527 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
18528
18529 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18530
18531 VkMemoryRequirements vkMemReq = {};
18532 bool requiresDedicatedAllocation = false;
18533 bool prefersDedicatedAllocation = false;
18534 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
18535 requiresDedicatedAllocation,
18536 prefersDedicatedAllocation);
18537
18538 VkResult result = allocator->AllocateMemory(
18539 vkMemReq,
18540 requiresDedicatedAllocation,
18541 prefersDedicatedAllocation,
18542 buffer, // dedicatedBuffer
18543 UINT32_MAX, // dedicatedBufferUsage
18544 VK_NULL_HANDLE, // dedicatedImage
18545 *pCreateInfo,
18546 VMA_SUBALLOCATION_TYPE_BUFFER,
18547 1, // allocationCount
18548 pAllocation);
18549
18550 #if VMA_RECORDING_ENABLED
18551 if(allocator->GetRecorder() != VMA_NULL)
18552 {
18553 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
18554 allocator->GetCurrentFrameIndex(),
18555 vkMemReq,
18556 requiresDedicatedAllocation,
18557 prefersDedicatedAllocation,
18558 *pCreateInfo,
18559 *pAllocation);
18560 }
18561 #endif
18562
18563 if(pAllocationInfo && result == VK_SUCCESS)
18564 {
18565 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18566 }
18567
18568 return result;
18569 }
18570
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18571 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
18572 VmaAllocator allocator,
18573 VkImage image,
18574 const VmaAllocationCreateInfo* pCreateInfo,
18575 VmaAllocation* pAllocation,
18576 VmaAllocationInfo* pAllocationInfo)
18577 {
18578 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18579
18580 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
18581
18582 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18583
18584 VkMemoryRequirements vkMemReq = {};
18585 bool requiresDedicatedAllocation = false;
18586 bool prefersDedicatedAllocation = false;
18587 allocator->GetImageMemoryRequirements(image, vkMemReq,
18588 requiresDedicatedAllocation, prefersDedicatedAllocation);
18589
18590 VkResult result = allocator->AllocateMemory(
18591 vkMemReq,
18592 requiresDedicatedAllocation,
18593 prefersDedicatedAllocation,
18594 VK_NULL_HANDLE, // dedicatedBuffer
18595 UINT32_MAX, // dedicatedBufferUsage
18596 image, // dedicatedImage
18597 *pCreateInfo,
18598 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18599 1, // allocationCount
18600 pAllocation);
18601
18602 #if VMA_RECORDING_ENABLED
18603 if(allocator->GetRecorder() != VMA_NULL)
18604 {
18605 allocator->GetRecorder()->RecordAllocateMemoryForImage(
18606 allocator->GetCurrentFrameIndex(),
18607 vkMemReq,
18608 requiresDedicatedAllocation,
18609 prefersDedicatedAllocation,
18610 *pCreateInfo,
18611 *pAllocation);
18612 }
18613 #endif
18614
18615 if(pAllocationInfo && result == VK_SUCCESS)
18616 {
18617 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18618 }
18619
18620 return result;
18621 }
18622
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)18623 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
18624 VmaAllocator allocator,
18625 VmaAllocation allocation)
18626 {
18627 VMA_ASSERT(allocator);
18628
18629 if(allocation == VK_NULL_HANDLE)
18630 {
18631 return;
18632 }
18633
18634 VMA_DEBUG_LOG("vmaFreeMemory");
18635
18636 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18637
18638 #if VMA_RECORDING_ENABLED
18639 if(allocator->GetRecorder() != VMA_NULL)
18640 {
18641 allocator->GetRecorder()->RecordFreeMemory(
18642 allocator->GetCurrentFrameIndex(),
18643 allocation);
18644 }
18645 #endif
18646
18647 allocator->FreeMemory(
18648 1, // allocationCount
18649 &allocation);
18650 }
18651
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)18652 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
18653 VmaAllocator allocator,
18654 size_t allocationCount,
18655 const VmaAllocation* pAllocations)
18656 {
18657 if(allocationCount == 0)
18658 {
18659 return;
18660 }
18661
18662 VMA_ASSERT(allocator);
18663
18664 VMA_DEBUG_LOG("vmaFreeMemoryPages");
18665
18666 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18667
18668 #if VMA_RECORDING_ENABLED
18669 if(allocator->GetRecorder() != VMA_NULL)
18670 {
18671 allocator->GetRecorder()->RecordFreeMemoryPages(
18672 allocator->GetCurrentFrameIndex(),
18673 (uint64_t)allocationCount,
18674 pAllocations);
18675 }
18676 #endif
18677
18678 allocator->FreeMemory(allocationCount, pAllocations);
18679 }
18680
18681 // 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)18682 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateReservedMemoryForImage(
18683 VmaAllocator VMA_NOT_NULL allocator,
18684 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
18685 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
18686 VmaAllocation VMA_NULLABLE * VMA_NOT_NULL pAllocation,
18687 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo)
18688 {
18689 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18690 VMA_DEBUG_LOG("vmaAllocateReservedMemoryForImage");
18691 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18692
18693 VkMemoryRequirements vkMemReq = {};
18694 bool requiresDedicatedAllocation = false;
18695 bool prefersDedicatedAllocation = false;
18696 allocator->GetImageMemoryRequirements(image, vkMemReq,
18697 requiresDedicatedAllocation, prefersDedicatedAllocation);
18698
18699 VkResult result = allocator->AllocateReservedMemory(
18700 vkMemReq,
18701 requiresDedicatedAllocation,
18702 prefersDedicatedAllocation,
18703 VK_NULL_HANDLE, // dedicatedBuffer
18704 UINT32_MAX, // dedicatedBufferUsage
18705 image, // dedicatedImage
18706 *pCreateInfo,
18707 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
18708 1, // allocationCount
18709 pAllocation);
18710
18711 if(pAllocationInfo && result == VK_SUCCESS)
18712 {
18713 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
18714 }
18715
18716 return result;
18717 }
18718
vmaGetNewBlockStats(VmaAllocation allocation,bool * pStats)18719 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetNewBlockStats(
18720 VmaAllocation allocation,
18721 bool* pStats)
18722 {
18723 VMA_ASSERT(allocation);
18724 VMA_DEBUG_LOG("vmaGetNewBlockStats");
18725 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18726
18727 if (pStats != NULL) {
18728 *pStats = allocation->IsNewBlockFlag();
18729 }
18730 return VK_SUCCESS;
18731 }
18732
vmaClearNewBlockStats(VmaAllocation allocation)18733 VMA_CALL_PRE VkResult VMA_CALL_POST vmaClearNewBlockStats(
18734 VmaAllocation allocation)
18735 {
18736 VMA_ASSERT(allocation);
18737 VMA_DEBUG_LOG("vmaClearNewBlockStats");
18738 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18739
18740 allocation->ClearNewBlockFlag();
18741 return VK_SUCCESS;
18742 }
18743
vmaSwapReservedBlock(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)18744 VMA_CALL_PRE VkResult VMA_CALL_POST vmaSwapReservedBlock(
18745 VmaAllocator allocator,
18746 VkImage image,
18747 const VmaAllocationCreateInfo* pCreateInfo,
18748 VmaAllocation* pAllocation,
18749 VmaAllocationInfo* pAllocationInfo)
18750 {
18751 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
18752 VMA_DEBUG_LOG("vmaSwapReservedBlock");
18753 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18754
18755 return allocator->SwapReservedBlock(image, pCreateInfo, pAllocation, pAllocationInfo);
18756 }
18757
vmaFreeReservedMemory(VmaAllocator allocator,VmaAllocation allocation)18758 VMA_CALL_PRE void VMA_CALL_POST vmaFreeReservedMemory(
18759 VmaAllocator allocator,
18760 VmaAllocation allocation)
18761 {
18762 VMA_ASSERT(allocator);
18763 if (allocation == VK_NULL_HANDLE)
18764 {
18765 return;
18766 }
18767 VMA_DEBUG_LOG("vmaFreeReservedMemory");
18768 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18769
18770 allocator->FreeReservedMemory(
18771 1, // allocationCount
18772 &allocation);
18773 }
18774
vmaGetPreAllocBlockSize(VmaAllocator allocator,uint32_t * pStats)18775 VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetPreAllocBlockSize(VmaAllocator allocator, uint32_t* pStats)
18776 {
18777 VMA_ASSERT(allocator);
18778 VMA_DEBUG_LOG("vmaGetPreAllocBlockSize");
18779
18780 if (pStats != NULL) {
18781 *pStats = allocator->GetPreAllocBlockSize();
18782 }
18783 return VK_SUCCESS;
18784 }
18785
vmaResizeAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize newSize)18786 VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeAllocation(
18787 VmaAllocator allocator,
18788 VmaAllocation allocation,
18789 VkDeviceSize newSize)
18790 {
18791 VMA_ASSERT(allocator && allocation);
18792
18793 VMA_DEBUG_LOG("vmaResizeAllocation");
18794
18795 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18796
18797 return allocator->ResizeAllocation(allocation, newSize);
18798 }
18799
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)18800 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
18801 VmaAllocator allocator,
18802 VmaAllocation allocation,
18803 VmaAllocationInfo* pAllocationInfo)
18804 {
18805 VMA_ASSERT(allocator && allocation && pAllocationInfo);
18806
18807 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18808
18809 #if VMA_RECORDING_ENABLED
18810 if(allocator->GetRecorder() != VMA_NULL)
18811 {
18812 allocator->GetRecorder()->RecordGetAllocationInfo(
18813 allocator->GetCurrentFrameIndex(),
18814 allocation);
18815 }
18816 #endif
18817
18818 allocator->GetAllocationInfo(allocation, pAllocationInfo);
18819 }
18820
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)18821 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaTouchAllocation(
18822 VmaAllocator allocator,
18823 VmaAllocation allocation)
18824 {
18825 VMA_ASSERT(allocator && allocation);
18826
18827 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18828
18829 #if VMA_RECORDING_ENABLED
18830 if(allocator->GetRecorder() != VMA_NULL)
18831 {
18832 allocator->GetRecorder()->RecordTouchAllocation(
18833 allocator->GetCurrentFrameIndex(),
18834 allocation);
18835 }
18836 #endif
18837
18838 return allocator->TouchAllocation(allocation);
18839 }
18840
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)18841 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
18842 VmaAllocator allocator,
18843 VmaAllocation allocation,
18844 void* pUserData)
18845 {
18846 VMA_ASSERT(allocator && allocation);
18847
18848 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18849
18850 allocation->SetUserData(allocator, pUserData);
18851
18852 #if VMA_RECORDING_ENABLED
18853 if(allocator->GetRecorder() != VMA_NULL)
18854 {
18855 allocator->GetRecorder()->RecordSetAllocationUserData(
18856 allocator->GetCurrentFrameIndex(),
18857 allocation,
18858 pUserData);
18859 }
18860 #endif
18861 }
18862
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)18863 VMA_CALL_PRE void VMA_CALL_POST vmaCreateLostAllocation(
18864 VmaAllocator allocator,
18865 VmaAllocation* pAllocation)
18866 {
18867 VMA_ASSERT(allocator && pAllocation);
18868
18869 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
18870
18871 allocator->CreateLostAllocation(pAllocation);
18872
18873 #if VMA_RECORDING_ENABLED
18874 if(allocator->GetRecorder() != VMA_NULL)
18875 {
18876 allocator->GetRecorder()->RecordCreateLostAllocation(
18877 allocator->GetCurrentFrameIndex(),
18878 *pAllocation);
18879 }
18880 #endif
18881 }
18882
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)18883 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
18884 VmaAllocator allocator,
18885 VmaAllocation allocation,
18886 void** ppData)
18887 {
18888 VMA_ASSERT(allocator && allocation && ppData);
18889
18890 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18891
18892 VkResult res = allocator->Map(allocation, ppData);
18893
18894 #if VMA_RECORDING_ENABLED
18895 if(allocator->GetRecorder() != VMA_NULL)
18896 {
18897 allocator->GetRecorder()->RecordMapMemory(
18898 allocator->GetCurrentFrameIndex(),
18899 allocation);
18900 }
18901 #endif
18902
18903 return res;
18904 }
18905
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)18906 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
18907 VmaAllocator allocator,
18908 VmaAllocation allocation)
18909 {
18910 VMA_ASSERT(allocator && allocation);
18911
18912 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18913
18914 #if VMA_RECORDING_ENABLED
18915 if(allocator->GetRecorder() != VMA_NULL)
18916 {
18917 allocator->GetRecorder()->RecordUnmapMemory(
18918 allocator->GetCurrentFrameIndex(),
18919 allocation);
18920 }
18921 #endif
18922
18923 allocator->Unmap(allocation);
18924 }
18925
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18926 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18927 {
18928 VMA_ASSERT(allocator && allocation);
18929
18930 VMA_DEBUG_LOG("vmaFlushAllocation");
18931
18932 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18933
18934 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
18935
18936 #if VMA_RECORDING_ENABLED
18937 if(allocator->GetRecorder() != VMA_NULL)
18938 {
18939 allocator->GetRecorder()->RecordFlushAllocation(
18940 allocator->GetCurrentFrameIndex(),
18941 allocation, offset, size);
18942 }
18943 #endif
18944
18945 return res;
18946 }
18947
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)18948 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
18949 {
18950 VMA_ASSERT(allocator && allocation);
18951
18952 VMA_DEBUG_LOG("vmaInvalidateAllocation");
18953
18954 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18955
18956 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
18957
18958 #if VMA_RECORDING_ENABLED
18959 if(allocator->GetRecorder() != VMA_NULL)
18960 {
18961 allocator->GetRecorder()->RecordInvalidateAllocation(
18962 allocator->GetCurrentFrameIndex(),
18963 allocation, offset, size);
18964 }
18965 #endif
18966
18967 return res;
18968 }
18969
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)18970 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
18971 VmaAllocator allocator,
18972 uint32_t allocationCount,
18973 const VmaAllocation* allocations,
18974 const VkDeviceSize* offsets,
18975 const VkDeviceSize* sizes)
18976 {
18977 VMA_ASSERT(allocator);
18978
18979 if(allocationCount == 0)
18980 {
18981 return VK_SUCCESS;
18982 }
18983
18984 VMA_ASSERT(allocations);
18985
18986 VMA_DEBUG_LOG("vmaFlushAllocations");
18987
18988 VMA_DEBUG_GLOBAL_MUTEX_LOCK
18989
18990 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
18991
18992 #if VMA_RECORDING_ENABLED
18993 if(allocator->GetRecorder() != VMA_NULL)
18994 {
18995 //TODO
18996 }
18997 #endif
18998
18999 return res;
19000 }
19001
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)19002 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
19003 VmaAllocator allocator,
19004 uint32_t allocationCount,
19005 const VmaAllocation* allocations,
19006 const VkDeviceSize* offsets,
19007 const VkDeviceSize* sizes)
19008 {
19009 VMA_ASSERT(allocator);
19010
19011 if(allocationCount == 0)
19012 {
19013 return VK_SUCCESS;
19014 }
19015
19016 VMA_ASSERT(allocations);
19017
19018 VMA_DEBUG_LOG("vmaInvalidateAllocations");
19019
19020 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19021
19022 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
19023
19024 #if VMA_RECORDING_ENABLED
19025 if(allocator->GetRecorder() != VMA_NULL)
19026 {
19027 //TODO
19028 }
19029 #endif
19030
19031 return res;
19032 }
19033
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)19034 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
19035 {
19036 VMA_ASSERT(allocator);
19037
19038 VMA_DEBUG_LOG("vmaCheckCorruption");
19039
19040 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19041
19042 return allocator->CheckCorruption(memoryTypeBits);
19043 }
19044
vmaDefragment(VmaAllocator allocator,const VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)19045 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragment(
19046 VmaAllocator allocator,
19047 const VmaAllocation* pAllocations,
19048 size_t allocationCount,
19049 VkBool32* pAllocationsChanged,
19050 const VmaDefragmentationInfo *pDefragmentationInfo,
19051 VmaDefragmentationStats* pDefragmentationStats)
19052 {
19053 // Deprecated interface, reimplemented using new one.
19054
19055 VmaDefragmentationInfo2 info2 = {};
19056 info2.allocationCount = (uint32_t)allocationCount;
19057 info2.pAllocations = pAllocations;
19058 info2.pAllocationsChanged = pAllocationsChanged;
19059 if(pDefragmentationInfo != VMA_NULL)
19060 {
19061 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
19062 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
19063 }
19064 else
19065 {
19066 info2.maxCpuAllocationsToMove = UINT32_MAX;
19067 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
19068 }
19069 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
19070
19071 VmaDefragmentationContext ctx;
19072 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
19073 if(res == VK_NOT_READY)
19074 {
19075 res = vmaDefragmentationEnd( allocator, ctx);
19076 }
19077 return res;
19078 }
19079
vmaDefragmentationBegin(VmaAllocator allocator,const VmaDefragmentationInfo2 * pInfo,VmaDefragmentationStats * pStats,VmaDefragmentationContext * pContext)19080 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationBegin(
19081 VmaAllocator allocator,
19082 const VmaDefragmentationInfo2* pInfo,
19083 VmaDefragmentationStats* pStats,
19084 VmaDefragmentationContext *pContext)
19085 {
19086 VMA_ASSERT(allocator && pInfo && pContext);
19087
19088 // Degenerate case: Nothing to defragment.
19089 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
19090 {
19091 return VK_SUCCESS;
19092 }
19093
19094 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
19095 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
19096 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
19097 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
19098
19099 VMA_DEBUG_LOG("vmaDefragmentationBegin");
19100
19101 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19102
19103 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
19104
19105 #if VMA_RECORDING_ENABLED
19106 if(allocator->GetRecorder() != VMA_NULL)
19107 {
19108 allocator->GetRecorder()->RecordDefragmentationBegin(
19109 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
19110 }
19111 #endif
19112
19113 return res;
19114 }
19115
vmaDefragmentationEnd(VmaAllocator allocator,VmaDefragmentationContext context)19116 VMA_CALL_PRE VkResult VMA_CALL_POST vmaDefragmentationEnd(
19117 VmaAllocator allocator,
19118 VmaDefragmentationContext context)
19119 {
19120 VMA_ASSERT(allocator);
19121
19122 VMA_DEBUG_LOG("vmaDefragmentationEnd");
19123
19124 if(context != VK_NULL_HANDLE)
19125 {
19126 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19127
19128 #if VMA_RECORDING_ENABLED
19129 if(allocator->GetRecorder() != VMA_NULL)
19130 {
19131 allocator->GetRecorder()->RecordDefragmentationEnd(
19132 allocator->GetCurrentFrameIndex(), context);
19133 }
19134 #endif
19135
19136 return allocator->DefragmentationEnd(context);
19137 }
19138 else
19139 {
19140 return VK_SUCCESS;
19141 }
19142 }
19143
vmaBeginDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationPassInfo * pInfo)19144 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
19145 VmaAllocator allocator,
19146 VmaDefragmentationContext context,
19147 VmaDefragmentationPassInfo* pInfo
19148 )
19149 {
19150 VMA_ASSERT(allocator);
19151 VMA_ASSERT(pInfo);
19152 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->moveCount, pInfo->pMoves));
19153
19154 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
19155
19156 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19157
19158 if(context == VK_NULL_HANDLE)
19159 {
19160 pInfo->moveCount = 0;
19161 return VK_SUCCESS;
19162 }
19163
19164 return allocator->DefragmentationPassBegin(pInfo, context);
19165 }
vmaEndDefragmentationPass(VmaAllocator allocator,VmaDefragmentationContext context)19166 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
19167 VmaAllocator allocator,
19168 VmaDefragmentationContext context)
19169 {
19170 VMA_ASSERT(allocator);
19171
19172 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
19173 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19174
19175 if(context == VK_NULL_HANDLE)
19176 return VK_SUCCESS;
19177
19178 return allocator->DefragmentationPassEnd(context);
19179 }
19180
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)19181 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
19182 VmaAllocator allocator,
19183 VmaAllocation allocation,
19184 VkBuffer buffer)
19185 {
19186 VMA_ASSERT(allocator && allocation && buffer);
19187
19188 VMA_DEBUG_LOG("vmaBindBufferMemory");
19189
19190 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19191
19192 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
19193 }
19194
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)19195 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
19196 VmaAllocator allocator,
19197 VmaAllocation allocation,
19198 VkDeviceSize allocationLocalOffset,
19199 VkBuffer buffer,
19200 const void* pNext)
19201 {
19202 VMA_ASSERT(allocator && allocation && buffer);
19203
19204 VMA_DEBUG_LOG("vmaBindBufferMemory2");
19205
19206 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19207
19208 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
19209 }
19210
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)19211 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
19212 VmaAllocator allocator,
19213 VmaAllocation allocation,
19214 VkImage image)
19215 {
19216 VMA_ASSERT(allocator && allocation && image);
19217
19218 VMA_DEBUG_LOG("vmaBindImageMemory");
19219
19220 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19221
19222 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
19223 }
19224
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)19225 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
19226 VmaAllocator allocator,
19227 VmaAllocation allocation,
19228 VkDeviceSize allocationLocalOffset,
19229 VkImage image,
19230 const void* pNext)
19231 {
19232 VMA_ASSERT(allocator && allocation && image);
19233
19234 VMA_DEBUG_LOG("vmaBindImageMemory2");
19235
19236 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19237
19238 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
19239 }
19240
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19241 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
19242 VmaAllocator allocator,
19243 const VkBufferCreateInfo* pBufferCreateInfo,
19244 const VmaAllocationCreateInfo* pAllocationCreateInfo,
19245 VkBuffer* pBuffer,
19246 VmaAllocation* pAllocation,
19247 VmaAllocationInfo* pAllocationInfo)
19248 {
19249 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
19250
19251 if(pBufferCreateInfo->size == 0)
19252 {
19253 return VK_ERROR_VALIDATION_FAILED_EXT;
19254 }
19255 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
19256 !allocator->m_UseKhrBufferDeviceAddress)
19257 {
19258 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.");
19259 return VK_ERROR_VALIDATION_FAILED_EXT;
19260 }
19261
19262 VMA_DEBUG_LOG("vmaCreateBuffer");
19263
19264 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19265
19266 *pBuffer = VK_NULL_HANDLE;
19267 *pAllocation = VK_NULL_HANDLE;
19268
19269 // 1. Create VkBuffer.
19270 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
19271 allocator->m_hDevice,
19272 pBufferCreateInfo,
19273 allocator->GetAllocationCallbacks(),
19274 pBuffer);
19275 if(res >= 0)
19276 {
19277 // 2. vkGetBufferMemoryRequirements.
19278 VkMemoryRequirements vkMemReq = {};
19279 bool requiresDedicatedAllocation = false;
19280 bool prefersDedicatedAllocation = false;
19281 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
19282 requiresDedicatedAllocation, prefersDedicatedAllocation);
19283
19284 // 3. Allocate memory using allocator.
19285 res = allocator->AllocateMemory(
19286 vkMemReq,
19287 requiresDedicatedAllocation,
19288 prefersDedicatedAllocation,
19289 *pBuffer, // dedicatedBuffer
19290 pBufferCreateInfo->usage, // dedicatedBufferUsage
19291 VK_NULL_HANDLE, // dedicatedImage
19292 *pAllocationCreateInfo,
19293 VMA_SUBALLOCATION_TYPE_BUFFER,
19294 1, // allocationCount
19295 pAllocation);
19296
19297 #if VMA_RECORDING_ENABLED
19298 if(allocator->GetRecorder() != VMA_NULL)
19299 {
19300 allocator->GetRecorder()->RecordCreateBuffer(
19301 allocator->GetCurrentFrameIndex(),
19302 *pBufferCreateInfo,
19303 *pAllocationCreateInfo,
19304 *pAllocation);
19305 }
19306 #endif
19307
19308 if(res >= 0)
19309 {
19310 // 3. Bind buffer with memory.
19311 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19312 {
19313 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
19314 }
19315 if(res >= 0)
19316 {
19317 // All steps succeeded.
19318 #if VMA_STATS_STRING_ENABLED
19319 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
19320 #endif
19321 if(pAllocationInfo != VMA_NULL)
19322 {
19323 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19324 }
19325
19326 return VK_SUCCESS;
19327 }
19328 allocator->FreeMemory(
19329 1, // allocationCount
19330 pAllocation);
19331 *pAllocation = VK_NULL_HANDLE;
19332 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19333 *pBuffer = VK_NULL_HANDLE;
19334 return res;
19335 }
19336 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
19337 *pBuffer = VK_NULL_HANDLE;
19338 return res;
19339 }
19340 return res;
19341 }
19342
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)19343 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
19344 VmaAllocator allocator,
19345 VkBuffer buffer,
19346 VmaAllocation allocation)
19347 {
19348 VMA_ASSERT(allocator);
19349
19350 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19351 {
19352 return;
19353 }
19354
19355 VMA_DEBUG_LOG("vmaDestroyBuffer");
19356
19357 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19358
19359 #if VMA_RECORDING_ENABLED
19360 if(allocator->GetRecorder() != VMA_NULL)
19361 {
19362 allocator->GetRecorder()->RecordDestroyBuffer(
19363 allocator->GetCurrentFrameIndex(),
19364 allocation);
19365 }
19366 #endif
19367
19368 if(buffer != VK_NULL_HANDLE)
19369 {
19370 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
19371 }
19372
19373 if(allocation != VK_NULL_HANDLE)
19374 {
19375 allocator->FreeMemory(
19376 1, // allocationCount
19377 &allocation);
19378 }
19379 }
19380
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)19381 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
19382 VmaAllocator allocator,
19383 const VkImageCreateInfo* pImageCreateInfo,
19384 const VmaAllocationCreateInfo* pAllocationCreateInfo,
19385 VkImage* pImage,
19386 VmaAllocation* pAllocation,
19387 VmaAllocationInfo* pAllocationInfo)
19388 {
19389 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
19390
19391 if(pImageCreateInfo->extent.width == 0 ||
19392 pImageCreateInfo->extent.height == 0 ||
19393 pImageCreateInfo->extent.depth == 0 ||
19394 pImageCreateInfo->mipLevels == 0 ||
19395 pImageCreateInfo->arrayLayers == 0)
19396 {
19397 return VK_ERROR_VALIDATION_FAILED_EXT;
19398 }
19399
19400 VMA_DEBUG_LOG("vmaCreateImage");
19401
19402 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19403
19404 *pImage = VK_NULL_HANDLE;
19405 *pAllocation = VK_NULL_HANDLE;
19406
19407 // 1. Create VkImage.
19408 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19409 allocator->m_hDevice,
19410 pImageCreateInfo,
19411 allocator->GetAllocationCallbacks(),
19412 pImage);
19413 if(res >= 0)
19414 {
19415 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
19416 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
19417 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
19418
19419 // 2. Allocate memory using allocator.
19420 VkMemoryRequirements vkMemReq = {};
19421 bool requiresDedicatedAllocation = false;
19422 bool prefersDedicatedAllocation = false;
19423 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
19424 requiresDedicatedAllocation, prefersDedicatedAllocation);
19425
19426 res = allocator->AllocateMemory(
19427 vkMemReq,
19428 requiresDedicatedAllocation,
19429 prefersDedicatedAllocation,
19430 VK_NULL_HANDLE, // dedicatedBuffer
19431 UINT32_MAX, // dedicatedBufferUsage
19432 *pImage, // dedicatedImage
19433 *pAllocationCreateInfo,
19434 suballocType,
19435 1, // allocationCount
19436 pAllocation);
19437
19438 #if VMA_RECORDING_ENABLED
19439 if(allocator->GetRecorder() != VMA_NULL)
19440 {
19441 allocator->GetRecorder()->RecordCreateImage(
19442 allocator->GetCurrentFrameIndex(),
19443 *pImageCreateInfo,
19444 *pAllocationCreateInfo,
19445 *pAllocation);
19446 }
19447 #endif
19448
19449 if(res >= 0)
19450 {
19451 // 3. Bind image with memory.
19452 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
19453 {
19454 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
19455 }
19456 if(res >= 0)
19457 {
19458 // All steps succeeded.
19459 #if VMA_STATS_STRING_ENABLED
19460 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
19461 #endif
19462 if(pAllocationInfo != VMA_NULL)
19463 {
19464 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
19465 }
19466
19467 return VK_SUCCESS;
19468 }
19469 allocator->FreeMemory(
19470 1, // allocationCount
19471 pAllocation);
19472 *pAllocation = VK_NULL_HANDLE;
19473 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19474 *pImage = VK_NULL_HANDLE;
19475 return res;
19476 }
19477 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
19478 *pImage = VK_NULL_HANDLE;
19479 return res;
19480 }
19481 return res;
19482 }
19483
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)19484 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
19485 VmaAllocator allocator,
19486 VkImage image,
19487 VmaAllocation allocation)
19488 {
19489 VMA_ASSERT(allocator);
19490
19491 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
19492 {
19493 return;
19494 }
19495
19496 VMA_DEBUG_LOG("vmaDestroyImage");
19497
19498 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19499
19500 #if VMA_RECORDING_ENABLED
19501 if(allocator->GetRecorder() != VMA_NULL)
19502 {
19503 allocator->GetRecorder()->RecordDestroyImage(
19504 allocator->GetCurrentFrameIndex(),
19505 allocation);
19506 }
19507 #endif
19508
19509 if(image != VK_NULL_HANDLE)
19510 {
19511 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19512 }
19513 if(allocation != VK_NULL_HANDLE)
19514 {
19515 allocator->FreeMemory(
19516 1, // allocationCount
19517 &allocation);
19518 }
19519 }
19520
19521 // OH ISSUE: VMA preAlloc
vmaCreateFakeImage(VmaAllocator allocator,VkImage * pImage)19522 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateFakeImage(
19523 VmaAllocator allocator,
19524 VkImage* pImage)
19525 {
19526 VMA_ASSERT(allocator && pImage);
19527 VMA_DEBUG_LOG("vmaCreateFakeImage");
19528 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19529
19530 *pImage = VK_NULL_HANDLE;
19531 const VkImageCreateInfo imageCreateInfo = {
19532 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
19533 nullptr, // pNext
19534 0, // VkImageCreateFlags
19535 VK_IMAGE_TYPE_2D, // VkImageType
19536 VK_FORMAT_R8G8B8A8_UNORM, // VkFormat
19537 { 10, 10, 1 }, // VkExtent3D
19538 1, // mipLevels
19539 1, // arrayLayers
19540 VK_SAMPLE_COUNT_1_BIT, // samples
19541 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling
19542 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // VkImageUsageFlags
19543 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode
19544 0, // queueFamilyCount
19545 nullptr, // pQueueFamilyIndices
19546 VK_IMAGE_LAYOUT_UNDEFINED // initialLayout
19547 };
19548
19549 // 1. Create VkImage.
19550 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
19551 allocator->m_hDevice,
19552 &imageCreateInfo,
19553 nullptr,
19554 pImage);
19555 return res;
19556 }
19557
vmaDestroyFakeImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image)19558 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyFakeImage(
19559 VmaAllocator VMA_NOT_NULL allocator,
19560 VkImage VMA_NULLABLE_NON_DISPATCHABLE image)
19561 {
19562 VMA_ASSERT(allocator);
19563 if(image == VK_NULL_HANDLE)
19564 {
19565 return;
19566 }
19567 VMA_DEBUG_LOG("vmaDestroyFakeImage");
19568 VMA_DEBUG_GLOBAL_MUTEX_LOCK
19569
19570 if(image != VK_NULL_HANDLE)
19571 {
19572 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
19573 }
19574 }
19575
19576 #endif // #ifdef VMA_IMPLEMENTATION
19577