blob: adda3a83a731bc2dcd0c71a8a6bbbd1fd74cf8e3 [file] [log] [blame]
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06001//
2// Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
Tony-LunarG390319b2019-03-18 15:54:16 -060023
24//
25// Source: https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
26// THIS FILE HAS BEEN CHANGED FROM THE ORIGINAL VERSION
27//
28// Change Log:
29// 3/27/19 - Make changes to suppress warnings from GCC
30//
31
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060032#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
33#define AMD_VULKAN_MEMORY_ALLOCATOR_H
34
35#ifdef __cplusplus
36extern "C" {
37#endif
38
39/** \mainpage Vulkan Memory Allocator
40
41<b>Version 2.2.0</b> (2018-12-13)
42
43Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
44License: MIT
45
46Documentation of all members: vk_mem_alloc.h
47
48\section main_table_of_contents Table of contents
49
50- <b>User guide</b>
51 - \subpage quick_start
52 - [Project setup](@ref quick_start_project_setup)
53 - [Initialization](@ref quick_start_initialization)
54 - [Resource allocation](@ref quick_start_resource_allocation)
55 - \subpage choosing_memory_type
56 - [Usage](@ref choosing_memory_type_usage)
57 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
58 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
59 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
60 - \subpage memory_mapping
61 - [Mapping functions](@ref memory_mapping_mapping_functions)
62 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
63 - [Cache control](@ref memory_mapping_cache_control)
64 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
65 - \subpage custom_memory_pools
66 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
67 - [Linear allocation algorithm](@ref linear_algorithm)
68 - [Free-at-once](@ref linear_algorithm_free_at_once)
69 - [Stack](@ref linear_algorithm_stack)
70 - [Double stack](@ref linear_algorithm_double_stack)
71 - [Ring buffer](@ref linear_algorithm_ring_buffer)
72 - [Buddy allocation algorithm](@ref buddy_algorithm)
73 - \subpage defragmentation
74 - [Defragmenting CPU memory](@ref defragmentation_cpu)
75 - [Defragmenting GPU memory](@ref defragmentation_gpu)
76 - [Additional notes](@ref defragmentation_additional_notes)
77 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
78 - \subpage lost_allocations
79 - \subpage statistics
80 - [Numeric statistics](@ref statistics_numeric_statistics)
81 - [JSON dump](@ref statistics_json_dump)
82 - \subpage allocation_annotation
83 - [Allocation user data](@ref allocation_user_data)
84 - [Allocation names](@ref allocation_names)
85 - \subpage debugging_memory_usage
86 - [Memory initialization](@ref debugging_memory_usage_initialization)
87 - [Margins](@ref debugging_memory_usage_margins)
88 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
89 - \subpage record_and_replay
90- \subpage usage_patterns
91 - [Simple patterns](@ref usage_patterns_simple)
92 - [Advanced patterns](@ref usage_patterns_advanced)
93- \subpage configuration
94 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
95 - [Custom host memory allocator](@ref custom_memory_allocator)
96 - [Device memory allocation callbacks](@ref allocation_callbacks)
97 - [Device heap memory limit](@ref heap_memory_limit)
98 - \subpage vk_khr_dedicated_allocation
99- \subpage general_considerations
100 - [Thread safety](@ref general_considerations_thread_safety)
101 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
102 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
103 - [Features not supported](@ref general_considerations_features_not_supported)
104
105\section main_see_also See also
106
107- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
108- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
109
110
111
112
113\page quick_start Quick start
114
115\section quick_start_project_setup Project setup
116
117Vulkan Memory Allocator comes in form of a single header file.
118You don't need to build it as a separate library project.
119You can add this file directly to your project and submit it to code repository next to your other source files.
120
121"Single header" doesn't mean that everything is contained in C/C++ declarations,
122like it tends to be in case of inline functions or C++ templates.
123It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
124If you don't do it properly, you will get linker errors.
125
126To do it properly:
127
128-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
129 This includes declarations of all members of the library.
130-# In exacly one CPP file define following macro before this include.
131 It enables also internal definitions.
132
133\code
134#define VMA_IMPLEMENTATION
135#include "vk_mem_alloc.h"
136\endcode
137
138It may be a good idea to create dedicated CPP file just for this purpose.
139
140Note on language: This library is written in C++, but has C-compatible interface.
141Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
142implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
143
144Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
145includes `<windows.h>` on Windows. If you need some specific macros defined
146before including these headers (like `WIN32_LEAN_AND_MEAN` or
147`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
148them before every `#include` of this library.
149
150
151\section quick_start_initialization Initialization
152
153At program startup:
154
155-# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
156-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
157 calling vmaCreateAllocator().
158
159\code
160VmaAllocatorCreateInfo allocatorInfo = {};
161allocatorInfo.physicalDevice = physicalDevice;
162allocatorInfo.device = device;
163
164VmaAllocator allocator;
165vmaCreateAllocator(&allocatorInfo, &allocator);
166\endcode
167
168\section quick_start_resource_allocation Resource allocation
169
170When you want to create a buffer or image:
171
172-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
173-# Fill VmaAllocationCreateInfo structure.
174-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
175 already allocated and bound to it.
176
177\code
178VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
179bufferInfo.size = 65536;
180bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
181
182VmaAllocationCreateInfo allocInfo = {};
183allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
184
185VkBuffer buffer;
186VmaAllocation allocation;
187vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
188\endcode
189
190Don't forget to destroy your objects when no longer needed:
191
192\code
193vmaDestroyBuffer(allocator, buffer, allocation);
194vmaDestroyAllocator(allocator);
195\endcode
196
197
198\page choosing_memory_type Choosing memory type
199
200Physical devices in Vulkan support various combinations of memory heaps and
201types. Help with choosing correct and optimal memory type for your specific
202resource is one of the key features of this library. You can use it by filling
203appropriate members of VmaAllocationCreateInfo structure, as described below.
204You can also combine multiple methods.
205
206-# If you just want to find memory type index that meets your requirements, you
207 can use function vmaFindMemoryTypeIndex().
208-# If you want to allocate a region of device memory without association with any
209 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
210 this function is not recommended and usually not needed.
211-# If you already have a buffer or an image created, you want to allocate memory
212 for it and then you will bind it yourself, you can use function
213 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
214 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
215-# If you want to create a buffer or an image, allocate memory for it and bind
216 them together, all in one call, you can use function vmaCreateBuffer(),
217 vmaCreateImage(). This is the recommended way to use this library.
218
219When using 3. or 4., the library internally queries Vulkan for memory types
220supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
221and uses only one of these types.
222
223If no memory type can be found that meets all the requirements, these functions
224return `VK_ERROR_FEATURE_NOT_PRESENT`.
225
226You can leave VmaAllocationCreateInfo structure completely filled with zeros.
227It means no requirements are specified for memory type.
228It is valid, although not very useful.
229
230\section choosing_memory_type_usage Usage
231
232The easiest way to specify memory requirements is to fill member
233VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
234It defines high level, common usage types.
235For more details, see description of this enum.
236
237For example, if you want to create a uniform buffer that will be filled using
238transfer only once or infrequently and used for rendering every frame, you can
239do it using following code:
240
241\code
242VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
243bufferInfo.size = 65536;
244bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
245
246VmaAllocationCreateInfo allocInfo = {};
247allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
248
249VkBuffer buffer;
250VmaAllocation allocation;
251vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
252\endcode
253
254\section choosing_memory_type_required_preferred_flags Required and preferred flags
255
256You can specify more detailed requirements by filling members
257VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
258with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
259if you want to create a buffer that will be persistently mapped on host (so it
260must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
261use following code:
262
263\code
264VmaAllocationCreateInfo allocInfo = {};
265allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
266allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
267allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
268
269VkBuffer buffer;
270VmaAllocation allocation;
271vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
272\endcode
273
274A memory type is chosen that has all the required flags and as many preferred
275flags set as possible.
276
277If you use VmaAllocationCreateInfo::usage, it is just internally converted to
278a set of required and preferred flags.
279
280\section choosing_memory_type_explicit_memory_types Explicit memory types
281
282If you inspected memory types available on the physical device and you have
283a preference for memory types that you want to use, you can fill member
284VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
285means that a memory type with that index is allowed to be used for the
286allocation. Special value 0, just like `UINT32_MAX`, means there are no
287restrictions to memory type index.
288
289Please note that this member is NOT just a memory type index.
290Still you can use it to choose just one, specific memory type.
291For example, if you already determined that your buffer should be created in
292memory type 2, use following code:
293
294\code
295uint32_t memoryTypeIndex = 2;
296
297VmaAllocationCreateInfo allocInfo = {};
298allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
299
300VkBuffer buffer;
301VmaAllocation allocation;
302vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
303\endcode
304
305\section choosing_memory_type_custom_memory_pools Custom memory pools
306
307If you allocate from custom memory pool, all the ways of specifying memory
308requirements described above are not applicable and the aforementioned members
309of VmaAllocationCreateInfo structure are ignored. Memory type is selected
310explicitly when creating the pool and then used to make all the allocations from
311that pool. For further details, see \ref custom_memory_pools.
312
313
314\page memory_mapping Memory mapping
315
316To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
317to be able to read from it or write to it in CPU code.
318Mapping is possible only of memory allocated from a memory type that has
319`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
320Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
321You can use them directly with memory allocated by this library,
322but it is not recommended because of following issue:
323Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
324This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
325Because of this, Vulkan Memory Allocator provides following facilities:
326
327\section memory_mapping_mapping_functions Mapping functions
328
329The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
330They are safer and more convenient to use than standard Vulkan functions.
331You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
332You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
333The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
334For further details, see description of vmaMapMemory() function.
335Example:
336
337\code
338// Having these objects initialized:
339
340struct ConstantBuffer
341{
342 ...
343};
344ConstantBuffer constantBufferData;
345
346VmaAllocator allocator;
347VkBuffer constantBuffer;
348VmaAllocation constantBufferAllocation;
349
350// You can map and fill your buffer using following code:
351
352void* mappedData;
353vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
354memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
355vmaUnmapMemory(allocator, constantBufferAllocation);
356\endcode
357
358When mapping, you may see a warning from Vulkan validation layer similar to this one:
359
360<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>
361
362It happens because the library maps entire `VkDeviceMemory` block, where different
363types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
364You can safely ignore it if you are sure you access only memory of the intended
365object that you wanted to map.
366
367
368\section memory_mapping_persistently_mapped_memory Persistently mapped memory
369
370Kepping your memory persistently mapped is generally OK in Vulkan.
371You don't need to unmap it before using its data on the GPU.
372The library provides a special feature designed for that:
373Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
374VmaAllocationCreateInfo::flags stay mapped all the time,
375so you can just access CPU pointer to it any time
376without a need to call any "map" or "unmap" function.
377Example:
378
379\code
380VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
381bufCreateInfo.size = sizeof(ConstantBuffer);
382bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
383
384VmaAllocationCreateInfo allocCreateInfo = {};
385allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
386allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
387
388VkBuffer buf;
389VmaAllocation alloc;
390VmaAllocationInfo allocInfo;
391vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
392
393// Buffer is already mapped. You can access its memory.
394memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
395\endcode
396
397There are some exceptions though, when you should consider mapping memory only for a short period of time:
398
399- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
400 device is discrete AMD GPU,
401 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
402 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
403 then whenever a memory block allocated from this memory type stays mapped
404 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
405 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
406 matter if that particular memory block is actually used by the command buffer
407 being submitted.
408- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
409 which requires unmapping before GPU can see updated texture.
410- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
411
412\section memory_mapping_cache_control Cache control
413
414Memory in Vulkan doesn't need to be unmapped before using it on GPU,
415but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
416you need to manually invalidate cache before reading of mapped pointer
417and flush cache after writing to mapped pointer.
418Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
419`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
420functions that refer to given allocation object: vmaFlushAllocation(),
421vmaInvalidateAllocation().
422
423Regions of memory specified for flush/invalidate must be aligned to
424`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
425In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
426within blocks are aligned to this value, so their offsets are always multiply of
427`nonCoherentAtomSize` and two different allocations never share same "line" of this size.
428
429Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
430
431Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
432currently provide `HOST_COHERENT` flag on all memory types that are
433`HOST_VISIBLE`, so on this platform you may not need to bother.
434
435\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
436
437It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
438despite it wasn't explicitly requested.
439For example, application may work on integrated graphics with unified memory (like Intel) or
440allocation from video memory might have failed, so the library chose system memory as fallback.
441
442You can detect this case and map such allocation to access its memory on CPU directly,
443instead of launching a transfer operation.
444In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
445and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
446
447\code
448VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
449bufCreateInfo.size = sizeof(ConstantBuffer);
450bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
451
452VmaAllocationCreateInfo allocCreateInfo = {};
453allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
454allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
455
456VkBuffer buf;
457VmaAllocation alloc;
458VmaAllocationInfo allocInfo;
459vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
460
461VkMemoryPropertyFlags memFlags;
462vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
463if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
464{
465 // Allocation ended up in mappable memory. You can map it and access it directly.
466 void* mappedData;
467 vmaMapMemory(allocator, alloc, &mappedData);
468 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
469 vmaUnmapMemory(allocator, alloc);
470}
471else
472{
473 // Allocation ended up in non-mappable memory.
474 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
475}
476\endcode
477
478You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
479that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
480If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
481If not, the flag is just ignored.
482Example:
483
484\code
485VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
486bufCreateInfo.size = sizeof(ConstantBuffer);
487bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
488
489VmaAllocationCreateInfo allocCreateInfo = {};
490allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
491allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
492
493VkBuffer buf;
494VmaAllocation alloc;
495VmaAllocationInfo allocInfo;
496vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
497
498if(allocInfo.pUserData != nullptr)
499{
500 // Allocation ended up in mappable memory.
501 // It's persistently mapped. You can access it directly.
502 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
503}
504else
505{
506 // Allocation ended up in non-mappable memory.
507 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
508}
509\endcode
510
511
512\page custom_memory_pools Custom memory pools
513
514A memory pool contains a number of `VkDeviceMemory` blocks.
515The library automatically creates and manages default pool for each memory type available on the device.
516Default memory pool automatically grows in size.
517Size of allocated blocks is also variable and managed automatically.
518
519You can create custom pool and allocate memory out of it.
520It can be useful if you want to:
521
522- Keep certain kind of allocations separate from others.
523- Enforce particular, fixed size of Vulkan memory blocks.
524- Limit maximum amount of Vulkan memory allocated for that pool.
525- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
526
527To use custom memory pools:
528
529-# Fill VmaPoolCreateInfo structure.
530-# Call vmaCreatePool() to obtain #VmaPool handle.
531-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
532 You don't need to specify any other parameters of this structure, like `usage`.
533
534Example:
535
536\code
537// Create a pool that can have at most 2 blocks, 128 MiB each.
538VmaPoolCreateInfo poolCreateInfo = {};
539poolCreateInfo.memoryTypeIndex = ...
540poolCreateInfo.blockSize = 128ull * 1024 * 1024;
541poolCreateInfo.maxBlockCount = 2;
542
543VmaPool pool;
544vmaCreatePool(allocator, &poolCreateInfo, &pool);
545
546// Allocate a buffer out of it.
547VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
548bufCreateInfo.size = 1024;
549bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
550
551VmaAllocationCreateInfo allocCreateInfo = {};
552allocCreateInfo.pool = pool;
553
554VkBuffer buf;
555VmaAllocation alloc;
556VmaAllocationInfo allocInfo;
557vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
558\endcode
559
560You have to free all allocations made from this pool before destroying it.
561
562\code
563vmaDestroyBuffer(allocator, buf, alloc);
564vmaDestroyPool(allocator, pool);
565\endcode
566
567\section custom_memory_pools_MemTypeIndex Choosing memory type index
568
569When creating a pool, you must explicitly specify memory type index.
570To find the one suitable for your buffers or images, you can use helper functions
571vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
572You need to provide structures with example parameters of buffers or images
573that you are going to create in that pool.
574
575\code
576VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
577exampleBufCreateInfo.size = 1024; // Whatever.
578exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
579
580VmaAllocationCreateInfo allocCreateInfo = {};
581allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
582
583uint32_t memTypeIndex;
584vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
585
586VmaPoolCreateInfo poolCreateInfo = {};
587poolCreateInfo.memoryTypeIndex = memTypeIndex;
588// ...
589\endcode
590
591When creating buffers/images allocated in that pool, provide following parameters:
592
593- `VkBufferCreateInfo`: Prefer to pass same parameters as above.
594 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
595 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
596 or the other way around.
597- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
598 Other members are ignored anyway.
599
600\section linear_algorithm Linear allocation algorithm
601
602Each Vulkan memory block managed by this library has accompanying metadata that
603keeps track of used and unused regions. By default, the metadata structure and
604algorithm tries to find best place for new allocations among free regions to
605optimize memory usage. This way you can allocate and free objects in any order.
606
607![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
608
609Sometimes there is a need to use simpler, linear allocation algorithm. You can
610create custom pool that uses such algorithm by adding flag
611#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
612#VmaPool object. Then an alternative metadata management is used. It always
613creates new allocations after last one and doesn't reuse free regions after
614allocations freed in the middle. It results in better allocation performance and
615less memory consumed by metadata.
616
617![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
618
619With this one flag, you can create a custom pool that can be used in many ways:
620free-at-once, stack, double stack, and ring buffer. See below for details.
621
622\subsection linear_algorithm_free_at_once Free-at-once
623
624In a pool that uses linear algorithm, you still need to free all the allocations
625individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
626them in any order. New allocations are always made after last one - free space
627in the middle is not reused. However, when you release all the allocation and
628the pool becomes empty, allocation starts from the beginning again. This way you
629can use linear algorithm to speed up creation of allocations that you are going
630to release all at once.
631
632![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
633
634This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
635value that allows multiple memory blocks.
636
637\subsection linear_algorithm_stack Stack
638
639When you free an allocation that was created last, its space can be reused.
640Thanks to this, if you always release allocations in the order opposite to their
641creation (LIFO - Last In First Out), you can achieve behavior of a stack.
642
643![Stack](../gfx/Linear_allocator_4_stack.png)
644
645This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
646value that allows multiple memory blocks.
647
648\subsection linear_algorithm_double_stack Double stack
649
650The space reserved by a custom pool with linear algorithm may be used by two
651stacks:
652
653- First, default one, growing up from offset 0.
654- Second, "upper" one, growing down from the end towards lower offsets.
655
656To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
657to VmaAllocationCreateInfo::flags.
658
659![Double stack](../gfx/Linear_allocator_7_double_stack.png)
660
661Double stack is available only in pools with one memory block -
662VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
663
664When the two stacks' ends meet so there is not enough space between them for a
665new allocation, such allocation fails with usual
666`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
667
668\subsection linear_algorithm_ring_buffer Ring buffer
669
670When you free some allocations from the beginning and there is not enough free space
671for a new one at the end of a pool, allocator's "cursor" wraps around to the
672beginning and starts allocation there. Thanks to this, if you always release
673allocations in the same order as you created them (FIFO - First In First Out),
674you can achieve behavior of a ring buffer / queue.
675
676![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
677
678Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
679If there is not enough free space for a new allocation, but existing allocations
680from the front of the queue can become lost, they become lost and the allocation
681succeeds.
682
683![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
684
685Ring buffer is available only in pools with one memory block -
686VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
687
688\section buddy_algorithm Buddy allocation algorithm
689
690There is another allocation algorithm that can be used with custom pools, called
691"buddy". Its internal data structure is based on a tree of blocks, each having
692size that is a power of two and a half of its parent's size. When you want to
693allocate memory of certain size, a free node in the tree is located. If it's too
694large, it is recursively split into two halves (called "buddies"). However, if
695requested allocation size is not a power of two, the size of a tree node is
696aligned up to the nearest power of two and the remaining space is wasted. When
697two buddy nodes become free, they are merged back into one larger node.
698
699![Buddy allocator](../gfx/Buddy_allocator.png)
700
701The advantage of buddy allocation algorithm over default algorithm is faster
702allocation and deallocation, as well as smaller external fragmentation. The
703disadvantage is more wasted space (internal fragmentation).
704
705For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
706or other sources that describe this concept in general.
707
708To use buddy allocation algorithm with a custom pool, add flag
709#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
710#VmaPool object.
711
712Several limitations apply to pools that use buddy algorithm:
713
714- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
715 Otherwise, only largest power of two smaller than the size is used for
716 allocations. The remaining space always stays unused.
717- [Margins](@ref debugging_memory_usage_margins) and
718 [corruption detection](@ref debugging_memory_usage_corruption_detection)
719 don't work in such pools.
720- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
721 use them, but they never become lost. Support may be added in the future.
722- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
723 such pool.
724
725\page defragmentation Defragmentation
726
727Interleaved allocations and deallocations of many objects of varying size can
728cause fragmentation over time, which can lead to a situation where the library is unable
729to find a continuous range of free memory for a new allocation despite there is
730enough free space, just scattered across many small free ranges between existing
731allocations.
732
733To mitigate this problem, you can use defragmentation feature:
734structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
735Given set of allocations,
736this function can move them to compact used memory, ensure more continuous free
737space and possibly also free some `VkDeviceMemory` blocks.
738
739What the defragmentation does is:
740
741- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
742 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
743 VmaAllocationInfo::offset changes. You must query them again using
744 vmaGetAllocationInfo() if you need them.
745- Moves actual data in memory.
746
747What it doesn't do, so you need to do it yourself:
748
749- Recreate buffers and images that were bound to allocations that were defragmented and
750 bind them with their new places in memory.
751 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
752 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
753 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
754 destroy or create allocation objects!
755- Recreate views and update descriptors that point to these buffers and images.
756
757\section defragmentation_cpu Defragmenting CPU memory
758
759Following example demonstrates how you can run defragmentation on CPU.
760Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
761Others are ignored.
762
763The way it works is:
764
765- It temporarily maps entire memory blocks when necessary.
766- It moves data using `memmove()` function.
767
768\code
769// Given following variables already initialized:
770VkDevice device;
771VmaAllocator allocator;
772std::vector<VkBuffer> buffers;
773std::vector<VmaAllocation> allocations;
774
775
776const uint32_t allocCount = (uint32_t)allocations.size();
777std::vector<VkBool32> allocationsChanged(allocCount);
778
779VmaDefragmentationInfo2 defragInfo = {};
780defragInfo.allocationCount = allocCount;
781defragInfo.pAllocations = allocations.data();
782defragInfo.pAllocationsChanged = allocationsChanged.data();
783defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
784defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
785
786VmaDefragmentationContext defragCtx;
787vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
788vmaDefragmentationEnd(allocator, defragCtx);
789
790for(uint32_t i = 0; i < allocCount; ++i)
791{
792 if(allocationsChanged[i])
793 {
794 // Destroy buffer that is immutably bound to memory region which is no longer valid.
795 vkDestroyBuffer(device, buffers[i], nullptr);
796
797 // Create new buffer with same parameters.
798 VkBufferCreateInfo bufferInfo = ...;
799 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
800
801 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
802
803 // Bind new buffer to new memory region. Data contained in it is already moved.
804 VmaAllocationInfo allocInfo;
805 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
806 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
807 }
808}
809\endcode
810
811Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
812This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
813has been modified during defragmentation.
814You can pass null, but you then need to query every allocation passed to defragmentation
815for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
816
817If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
818you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
819instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
820to defragment all allocations in given pools.
821You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
822You can also combine both methods.
823
824\section defragmentation_gpu Defragmenting GPU memory
825
826It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
827To do that, you need to pass a command buffer that meets requirements as described in
828VmaDefragmentationInfo2::commandBuffer. The way it works is:
829
830- It creates temporary buffers and binds them to entire memory blocks when necessary.
831- It issues `vkCmdCopyBuffer()` to passed command buffer.
832
833Example:
834
835\code
836// Given following variables already initialized:
837VkDevice device;
838VmaAllocator allocator;
839VkCommandBuffer commandBuffer;
840std::vector<VkBuffer> buffers;
841std::vector<VmaAllocation> allocations;
842
843
844const uint32_t allocCount = (uint32_t)allocations.size();
845std::vector<VkBool32> allocationsChanged(allocCount);
846
847VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
848vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
849
850VmaDefragmentationInfo2 defragInfo = {};
851defragInfo.allocationCount = allocCount;
852defragInfo.pAllocations = allocations.data();
853defragInfo.pAllocationsChanged = allocationsChanged.data();
854defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
855defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
856defragInfo.commandBuffer = commandBuffer;
857
858VmaDefragmentationContext defragCtx;
859vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
860
861vkEndCommandBuffer(commandBuffer);
862
863// Submit commandBuffer.
864// Wait for a fence that ensures commandBuffer execution finished.
865
866vmaDefragmentationEnd(allocator, defragCtx);
867
868for(uint32_t i = 0; i < allocCount; ++i)
869{
870 if(allocationsChanged[i])
871 {
872 // Destroy buffer that is immutably bound to memory region which is no longer valid.
873 vkDestroyBuffer(device, buffers[i], nullptr);
874
875 // Create new buffer with same parameters.
876 VkBufferCreateInfo bufferInfo = ...;
877 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
878
879 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
880
881 // Bind new buffer to new memory region. Data contained in it is already moved.
882 VmaAllocationInfo allocInfo;
883 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
884 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
885 }
886}
887\endcode
888
889You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
890The library automatically chooses best method to defragment each memory pool.
891
892You may try not to block your entire program to wait until defragmentation finishes,
893but do it in the background, as long as you carefully fullfill requirements described
894in function vmaDefragmentationBegin().
895
896\section defragmentation_additional_notes Additional notes
897
898While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
899See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
900
901If you defragment allocations bound to images, these images should be created with
902`VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
903parameters and pointing to data copied to another memory region will interpret
904its contents consistently. Otherwise you may experience corrupted data on some
905implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
906
907If you defragment allocations bound to images, new images to be bound to new
908memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
909and then transitioned to their original layout from before defragmentation using
910an image memory barrier.
911
912Please don't expect memory to be fully compacted after defragmentation.
913Algorithms inside are based on some heuristics that try to maximize number of Vulkan
914memory blocks to make totally empty to release them, as well as to maximimze continuous
915empty space inside remaining blocks, while minimizing the number and size of allocations that
916need to be moved. Some fragmentation may still remain - this is normal.
917
918\section defragmentation_custom_algorithm Writing custom defragmentation algorithm
919
920If you want to implement your own, custom defragmentation algorithm,
921there is infrastructure prepared for that,
922but it is not exposed through the library API - you need to hack its source code.
923Here are steps needed to do this:
924
925-# Main thing you need to do is to define your own class derived from base abstract
926 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
927 See definition and comments of this class for details.
928-# Your code needs to interact with device memory block metadata.
929 If you need more access to its data than it's provided by its public interface,
930 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
931-# If you want to create a flag that would enable your algorithm or pass some additional
932 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
933 VmaDefragmentationInfo2::flags.
934-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
935 of your new class whenever needed.
936
937
938\page lost_allocations Lost allocations
939
940If your game oversubscribes video memory, if may work OK in previous-generation
941graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
942paged to system RAM. In Vulkan you can't do it because when you run out of
943memory, an allocation just fails. If you have more data (e.g. textures) that can
944fit into VRAM and you don't need it all at once, you may want to upload them to
945GPU on demand and "push out" ones that are not used for a long time to make room
946for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
947cache. Vulkan Memory Allocator can help you with that by supporting a concept of
948"lost allocations".
949
950To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
951flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
952such allocation in every new frame, you need to query it if it's not lost.
953To check it, call vmaTouchAllocation().
954If the allocation is lost, you should not use it or buffer/image bound to it.
955You mustn't forget to destroy this allocation and this buffer/image.
956vmaGetAllocationInfo() can also be used for checking status of the allocation.
957Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
958
959To create an allocation that can make some other allocations lost to make room
960for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
961usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
962#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
963
964Warning! Current implementation uses quite naive, brute force algorithm,
965which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
966flag quite slow. A new, more optimal algorithm and data structure to speed this
967up is planned for the future.
968
969<b>Q: When interleaving creation of new allocations with usage of existing ones,
970how do you make sure that an allocation won't become lost while it's used in the
971current frame?</b>
972
973It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
974status/parameters and checks whether it's not lost, but when it's not, it also
975atomically marks it as used in the current frame, which makes it impossible to
976become lost in that frame. It uses lockless algorithm, so it works fast and
977doesn't involve locking any internal mutex.
978
979<b>Q: What if my allocation may still be in use by the GPU when it's rendering a
980previous frame while I already submit new frame on the CPU?</b>
981
982You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
983become lost for a number of additional frames back from the current one by
984specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
985memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
986
987<b>Q: How do you inform the library when new frame starts?</b>
988
989You need to call function vmaSetCurrentFrameIndex().
990
991Example code:
992
993\code
994struct MyBuffer
995{
996 VkBuffer m_Buf = nullptr;
997 VmaAllocation m_Alloc = nullptr;
998
999 // Called when the buffer is really needed in the current frame.
1000 void EnsureBuffer();
1001};
1002
1003void MyBuffer::EnsureBuffer()
1004{
1005 // Buffer has been created.
1006 if(m_Buf != VK_NULL_HANDLE)
1007 {
1008 // Check if its allocation is not lost + mark it as used in current frame.
1009 if(vmaTouchAllocation(allocator, m_Alloc))
1010 {
1011 // It's all OK - safe to use m_Buf.
1012 return;
1013 }
1014 }
1015
1016 // Buffer not yet exists or lost - destroy and recreate it.
1017
1018 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1019
1020 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1021 bufCreateInfo.size = 1024;
1022 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1023
1024 VmaAllocationCreateInfo allocCreateInfo = {};
1025 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1026 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1027 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1028
1029 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1030}
1031\endcode
1032
1033When using lost allocations, you may see some Vulkan validation layer warnings
1034about overlapping regions of memory bound to different kinds of buffers and
1035images. This is still valid as long as you implement proper handling of lost
1036allocations (like in the example above) and don't use them.
1037
1038You can create an allocation that is already in lost state from the beginning using function
1039vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1040
1041You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1042in a specified custom pool to lost state.
1043Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1044cannot become lost.
1045
1046<b>Q: Can I touch allocation that cannot become lost?</b>
1047
1048Yes, although it has no visible effect.
1049Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1050also for allocations that cannot become lost, but the only way to observe it is to dump
1051internal allocator state using vmaBuildStatsString().
1052You can use this feature for debugging purposes to explicitly mark allocations that you use
1053in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1054
1055
1056\page statistics Statistics
1057
1058This library contains functions that return information about its internal state,
1059especially the amount of memory allocated from Vulkan.
1060Please keep in mind that these functions need to traverse all internal data structures
1061to gather these information, so they may be quite time-consuming.
1062Don't call them too often.
1063
1064\section statistics_numeric_statistics Numeric statistics
1065
1066You can query for overall statistics of the allocator using function vmaCalculateStats().
1067Information are returned using structure #VmaStats.
1068It contains #VmaStatInfo - number of allocated blocks, number of allocations
1069(occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1070number of bytes used and unused (but still allocated from Vulkan) and other information.
1071They are summed across memory heaps, memory types and total for whole allocator.
1072
1073You can query for statistics of a custom pool using function vmaGetPoolStats().
1074Information are returned using structure #VmaPoolStats.
1075
1076You can query for information about specific allocation using function vmaGetAllocationInfo().
1077It fill structure #VmaAllocationInfo.
1078
1079\section statistics_json_dump JSON dump
1080
1081You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1082The result is guaranteed to be correct JSON.
1083It uses ANSI encoding.
1084Any strings provided by user (see [Allocation names](@ref allocation_names))
1085are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1086this JSON string can be treated as using this encoding.
1087It must be freed using function vmaFreeStatsString().
1088
1089The format of this JSON string is not part of official documentation of the library,
1090but it will not change in backward-incompatible way without increasing library major version number
1091and appropriate mention in changelog.
1092
1093The JSON string contains all the data that can be obtained using vmaCalculateStats().
1094It can also contain detailed map of allocated memory blocks and their regions -
1095free and occupied by allocations.
1096This allows e.g. to visualize the memory or assess fragmentation.
1097
1098
1099\page allocation_annotation Allocation names and user data
1100
1101\section allocation_user_data Allocation user data
1102
1103You can annotate allocations with your own information, e.g. for debugging purposes.
1104To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1105an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1106some handle, index, key, ordinal number or any other value that would associate
1107the allocation with your custom metadata.
1108
1109\code
1110VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1111// Fill bufferInfo...
1112
1113MyBufferMetadata* pMetadata = CreateBufferMetadata();
1114
1115VmaAllocationCreateInfo allocCreateInfo = {};
1116allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1117allocCreateInfo.pUserData = pMetadata;
1118
1119VkBuffer buffer;
1120VmaAllocation allocation;
1121vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1122\endcode
1123
1124The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1125
1126\code
1127VmaAllocationInfo allocInfo;
1128vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1129MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1130\endcode
1131
1132It can also be changed using function vmaSetAllocationUserData().
1133
1134Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1135vmaBuildStatsString(), in hexadecimal form.
1136
1137\section allocation_names Allocation names
1138
1139There is alternative mode available where `pUserData` pointer is used to point to
1140a null-terminated string, giving a name to the allocation. To use this mode,
1141set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1142Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1143vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1144The library creates internal copy of the string, so the pointer you pass doesn't need
1145to be valid for whole lifetime of the allocation. You can free it after the call.
1146
1147\code
1148VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1149// Fill imageInfo...
1150
1151std::string imageName = "Texture: ";
1152imageName += fileName;
1153
1154VmaAllocationCreateInfo allocCreateInfo = {};
1155allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1156allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1157allocCreateInfo.pUserData = imageName.c_str();
1158
1159VkImage image;
1160VmaAllocation allocation;
1161vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1162\endcode
1163
1164The value of `pUserData` pointer of the allocation will be different than the one
1165you passed when setting allocation's name - pointing to a buffer managed
1166internally that holds copy of the string.
1167
1168\code
1169VmaAllocationInfo allocInfo;
1170vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1171const char* imageName = (const char*)allocInfo.pUserData;
1172printf("Image name: %s\n", imageName);
1173\endcode
1174
1175That string is also printed in JSON report created by vmaBuildStatsString().
1176
1177
1178\page debugging_memory_usage Debugging incorrect memory usage
1179
1180If you suspect a bug with memory usage, like usage of uninitialized memory or
1181memory being overwritten out of bounds of an allocation,
1182you can use debug features of this library to verify this.
1183
1184\section debugging_memory_usage_initialization Memory initialization
1185
1186If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1187you can enable automatic memory initialization to verify this.
1188To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1189
1190\code
1191#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1192#include "vk_mem_alloc.h"
1193\endcode
1194
1195It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1196Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1197Memory is automatically mapped and unmapped if necessary.
1198
1199If you find these values while debugging your program, good chances are that you incorrectly
1200read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1201
1202Memory initialization works only with memory types that are `HOST_VISIBLE`.
1203It works also with dedicated allocations.
1204It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1205as they cannot be mapped.
1206
1207\section debugging_memory_usage_margins Margins
1208
1209By default, allocations are laid out in memory blocks next to each other if possible
1210(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1211
1212![Allocations without margin](../gfx/Margins_1.png)
1213
1214Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1215number of bytes as a margin before and after every allocation.
1216
1217\code
1218#define VMA_DEBUG_MARGIN 16
1219#include "vk_mem_alloc.h"
1220\endcode
1221
1222![Allocations with margin](../gfx/Margins_2.png)
1223
1224If your bug goes away after enabling margins, it means it may be caused by memory
1225being overwritten outside of allocation boundaries. It is not 100% certain though.
1226Change in application behavior may also be caused by different order and distribution
1227of allocations across memory blocks after margins are applied.
1228
1229The margin is applied also before first and after last allocation in a block.
1230It may occur only once between two adjacent allocations.
1231
1232Margins work with all types of memory.
1233
1234Margin is applied only to allocations made out of memory blocks and not to dedicated
1235allocations, which have their own memory block of specific size.
1236It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1237or those automatically decided to put into dedicated allocations, e.g. due to its
1238large size or recommended by VK_KHR_dedicated_allocation extension.
1239Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1240
1241Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1242
1243Note that enabling margins increases memory usage and fragmentation.
1244
1245\section debugging_memory_usage_corruption_detection Corruption detection
1246
1247You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1248of contents of the margins.
1249
1250\code
1251#define VMA_DEBUG_MARGIN 16
1252#define VMA_DEBUG_DETECT_CORRUPTION 1
1253#include "vk_mem_alloc.h"
1254\endcode
1255
1256When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1257(it must be multiply of 4) before and after every allocation is filled with a magic number.
1258This idea is also know as "canary".
1259Memory is automatically mapped and unmapped if necessary.
1260
1261This number is validated automatically when the allocation is destroyed.
1262If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1263It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1264which indicates a serious bug.
1265
1266You can also explicitly request checking margins of all allocations in all memory blocks
1267that belong to specified memory types by using function vmaCheckCorruption(),
1268or in memory blocks that belong to specified custom pool, by using function
1269vmaCheckPoolCorruption().
1270
1271Margin validation (corruption detection) works only for memory types that are
1272`HOST_VISIBLE` and `HOST_COHERENT`.
1273
1274
1275\page record_and_replay Record and replay
1276
1277\section record_and_replay_introduction Introduction
1278
1279While using the library, sequence of calls to its functions together with their
1280parameters can be recorded to a file and later replayed using standalone player
1281application. It can be useful to:
1282
1283- Test correctness - check if same sequence of calls will not cause crash or
1284 failures on a target platform.
1285- Gather statistics - see number of allocations, peak memory usage, number of
1286 calls etc.
1287- Benchmark performance - see how much time it takes to replay the whole
1288 sequence.
1289
1290\section record_and_replay_usage Usage
1291
1292<b>To record sequence of calls to a file:</b> Fill in
1293VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1294object. File is opened and written during whole lifetime of the allocator.
1295
1296<b>To replay file:</b> Use VmaReplay - standalone command-line program.
1297Precompiled binary can be found in "bin" directory.
1298Its source can be found in "src/VmaReplay" directory.
1299Its project is generated by Premake.
1300Command line syntax is printed when the program is launched without parameters.
1301Basic usage:
1302
1303 VmaReplay.exe MyRecording.csv
1304
1305<b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1306It's a human-readable, text file in CSV format (Comma Separated Values).
1307
1308\section record_and_replay_additional_considerations Additional considerations
1309
1310- Replaying file that was recorded on a different GPU (with different parameters
1311 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1312 set of memory heaps and types) may give different performance and memory usage
1313 results, as well as issue some warnings and errors.
1314- Current implementation of recording in VMA, as well as VmaReplay application, is
1315 coded and tested only on Windows. Inclusion of recording code is driven by
1316 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1317 add. Contributions are welcomed.
1318- Currently calls to vmaDefragment() function are not recorded.
1319
1320
1321\page usage_patterns Recommended usage patterns
1322
1323See also slides from talk:
1324[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)
1325
1326
1327\section usage_patterns_simple Simple patterns
1328
1329\subsection usage_patterns_simple_render_targets Render targets
1330
1331<b>When:</b>
1332Any resources that you frequently write and read on GPU,
1333e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1334images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1335
1336<b>What to do:</b>
1337Create them in video memory that is fastest to access from GPU using
1338#VMA_MEMORY_USAGE_GPU_ONLY.
1339
1340Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1341and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1342especially if they are large or if you plan to destroy and recreate them e.g. when
1343display resolution changes.
1344Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1345
1346\subsection usage_patterns_simple_immutable_resources Immutable resources
1347
1348<b>When:</b>
1349Any resources that you fill on CPU only once (aka "immutable") or infrequently
1350and then read frequently on GPU,
1351e.g. textures, vertex and index buffers, constant buffers that don't change often.
1352
1353<b>What to do:</b>
1354Create them in video memory that is fastest to access from GPU using
1355#VMA_MEMORY_USAGE_GPU_ONLY.
1356
1357To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1358in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1359and submit a transfer from it to the GPU resource.
1360You can keep the staging copy if you need it for another upload transfer in the future.
1361If you don't, you can destroy it or reuse this buffer for uploading different resource
1362after the transfer finishes.
1363
1364Prefer to create just buffers in system memory rather than images, even for uploading textures.
1365Use `vkCmdCopyBufferToImage()`.
1366Dont use images with `VK_IMAGE_TILING_LINEAR`.
1367
1368\subsection usage_patterns_dynamic_resources Dynamic resources
1369
1370<b>When:</b>
1371Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1372written on CPU, read on GPU.
1373
1374<b>What to do:</b>
1375Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1376You can map it and write to it directly on CPU, as well as read from it on GPU.
1377
1378This is a more complex situation. Different solutions are possible,
1379and the best one depends on specific GPU type, but you can use this simple approach for the start.
1380Prefer to write to such resource sequentially (e.g. using `memcpy`).
1381Don't perform random access or any reads from it on CPU, as it may be very slow.
1382
1383\subsection usage_patterns_readback Readback
1384
1385<b>When:</b>
1386Resources that contain data written by GPU that you want to read back on CPU,
1387e.g. results of some computations.
1388
1389<b>What to do:</b>
1390Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1391You can write to them directly on GPU, as well as map and read them on CPU.
1392
1393\section usage_patterns_advanced Advanced patterns
1394
1395\subsection usage_patterns_integrated_graphics Detecting integrated graphics
1396
1397You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1398by detecting it in Vulkan.
1399To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1400`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1401When you find it, you can assume that memory is unified and all memory types are comparably fast
1402to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1403
1404You can then sum up sizes of all available memory heaps and treat them as useful for
1405your GPU resources, instead of only `DEVICE_LOCAL` ones.
1406You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1407directly instead of submitting explicit transfer (see below).
1408
1409\subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1410
1411For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1412
1413-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1414 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1415-# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1416 read it directly on GPU.
1417-# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1418 read it directly on GPU.
1419
1420Which solution is the most efficient depends on your resource and especially on the GPU.
1421It is best to measure it and then make the decision.
1422Some general recommendations:
1423
1424- On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1425 related to using a second copy and making transfer.
1426- For small resources (e.g. constant buffers) use (2).
1427 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1428 Even if the resource ends up in system memory, its data may be cached on GPU after first
1429 fetch over PCIe bus.
1430- For larger resources (e.g. textures), decide between (1) and (2).
1431 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1432 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1433
1434Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1435solutions are possible:
1436
1437-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1438 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1439-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1440 map it and read it on CPU.
1441
1442You should take some measurements to decide which option is faster in case of your specific
1443resource.
1444
1445If you don't want to specialize your code for specific types of GPUs, you can still make
1446an simple optimization for cases when your resource ends up in mappable memory to use it
1447directly in this case instead of creating CPU-side staging copy.
1448For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1449
1450
1451\page configuration Configuration
1452
1453Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1454before each include of this file or change directly in this file to provide
1455your own implementation of basic facilities like assert, `min()` and `max()` functions,
1456mutex, atomic etc.
1457The library uses its own implementation of containers by default, but you can switch to using
1458STL containers instead.
1459
1460\section config_Vulkan_functions Pointers to Vulkan functions
1461
1462The library uses Vulkan functions straight from the `vulkan.h` header by default.
1463If you want to provide your own pointers to these functions, e.g. fetched using
1464`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1465
1466-# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1467-# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1468
1469\section custom_memory_allocator Custom host memory allocator
1470
1471If you use custom allocator for CPU memory rather than default operator `new`
1472and `delete` from C++, you can make this library using your allocator as well
1473by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1474functions will be passed to Vulkan, as well as used by the library itself to
1475make any CPU-side allocations.
1476
1477\section allocation_callbacks Device memory allocation callbacks
1478
1479The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1480You can setup callbacks to be informed about these calls, e.g. for the purpose
1481of gathering some statistics. To do it, fill optional member
1482VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1483
1484\section heap_memory_limit Device heap memory limit
1485
1486If you want to test how your program behaves with limited amount of Vulkan device
1487memory available without switching your graphics card to one that really has
1488smaller VRAM, you can use a feature of this library intended for this purpose.
1489To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1490
1491
1492
1493\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1494
1495VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1496performance on some GPUs. It augments Vulkan API with possibility to query
1497driver whether it prefers particular buffer or image to have its own, dedicated
1498allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1499to do some internal optimizations.
1500
1501The extension is supported by this library. It will be used automatically when
1502enabled. To enable it:
1503
15041 . When creating Vulkan device, check if following 2 device extensions are
1505supported (call `vkEnumerateDeviceExtensionProperties()`).
1506If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1507
1508- VK_KHR_get_memory_requirements2
1509- VK_KHR_dedicated_allocation
1510
1511If you enabled these extensions:
1512
15132 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1514your #VmaAllocator`to inform the library that you enabled required extensions
1515and you want the library to use them.
1516
1517\code
1518allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1519
1520vmaCreateAllocator(&allocatorInfo, &allocator);
1521\endcode
1522
1523That's all. The extension will be automatically used whenever you create a
1524buffer using vmaCreateBuffer() or image using vmaCreateImage().
1525
1526When using the extension together with Vulkan Validation Layer, you will receive
1527warnings like this:
1528
1529 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1530
1531It is OK, you should just ignore it. It happens because you use function
1532`vkGetBufferMemoryRequirements2KHR()` instead of standard
1533`vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1534unaware of it.
1535
1536To learn more about this extension, see:
1537
1538- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1539- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1540
1541
1542
1543\page general_considerations General considerations
1544
1545\section general_considerations_thread_safety Thread safety
1546
1547- The library has no global state, so separate #VmaAllocator objects can be used
1548 independently.
1549 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1550- By default, all calls to functions that take #VmaAllocator as first parameter
1551 are safe to call from multiple threads simultaneously because they are
1552 synchronized internally when needed.
1553- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1554 flag, calls to functions that take such #VmaAllocator object must be
1555 synchronized externally.
1556- Access to a #VmaAllocation object must be externally synchronized. For example,
1557 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1558 threads at the same time if you pass the same #VmaAllocation object to these
1559 functions.
1560
1561\section general_considerations_validation_layer_warnings Validation layer warnings
1562
1563When using this library, you can meet following types of warnings issued by
1564Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1565to just ignore them.
1566
1567- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1568 - It happens when VK_KHR_dedicated_allocation extension is enabled.
1569 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1570- *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.*
1571 - It happens when you map a buffer or image, because the library maps entire
1572 `VkDeviceMemory` block, where different types of images and buffers may end
1573 up together, especially on GPUs with unified memory like Intel.
1574- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1575 - It happens when you use lost allocations, and a new image or buffer is
1576 created in place of an existing object that bacame lost.
1577 - It may happen also when you use [defragmentation](@ref defragmentation).
1578
1579\section general_considerations_allocation_algorithm Allocation algorithm
1580
1581The library uses following algorithm for allocation, in order:
1582
1583-# Try to find free range of memory in existing blocks.
1584-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1585-# If failed, try to create such block with size/2, size/4, size/8.
1586-# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1587 specified, try to find space in existing blocks, possilby making some other
1588 allocations lost.
1589-# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1590 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1591-# If failed, choose other memory type that meets the requirements specified in
1592 VmaAllocationCreateInfo and go to point 1.
1593-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1594
1595\section general_considerations_features_not_supported Features not supported
1596
1597Features deliberately excluded from the scope of this library:
1598
1599- Data transfer. Uploading (straming) and downloading data of buffers and images
1600 between CPU and GPU memory and related synchronization is responsibility of the user.
1601- Allocations for imported/exported external memory. They tend to require
1602 explicit memory type index and dedicated allocation anyway, so they don't
1603 interact with main features of this library. Such special purpose allocations
1604 should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1605- Recreation of buffers and images. Although the library has functions for
1606 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1607 recreate these objects yourself after defragmentation. That's because the big
1608 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1609 #VmaAllocation object.
1610- Handling CPU memory allocation failures. When dynamically creating small C++
1611 objects in CPU memory (not Vulkan memory), allocation failures are not checked
1612 and handled gracefully, because that would complicate code significantly and
1613 is usually not needed in desktop PC applications anyway.
1614- Code free of any compiler warnings. Maintaining the library to compile and
1615 work correctly on so many different platforms is hard enough. Being free of
1616 any warnings, on any version of any compiler, is simply not feasible.
1617- This is a C++ library with C interface.
1618 Bindings or ports to any other programming languages are welcomed as external projects and
1619 are not going to be included into this repository.
1620
1621*/
1622
1623/*
1624Define this macro to 0/1 to disable/enable support for recording functionality,
1625available through VmaAllocatorCreateInfo::pRecordSettings.
1626*/
1627#ifndef VMA_RECORDING_ENABLED
1628 #ifdef _WIN32
1629 #define VMA_RECORDING_ENABLED 1
1630 #else
1631 #define VMA_RECORDING_ENABLED 0
1632 #endif
1633#endif
1634
1635#ifndef NOMINMAX
1636 #define NOMINMAX // For windows.h
1637#endif
1638
1639#ifndef VULKAN_H_
1640 #include <vulkan/vulkan.h>
1641#endif
1642
1643#if VMA_RECORDING_ENABLED
1644 #include <windows.h>
1645#endif
1646
1647#if !defined(VMA_DEDICATED_ALLOCATION)
1648 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1649 #define VMA_DEDICATED_ALLOCATION 1
1650 #else
1651 #define VMA_DEDICATED_ALLOCATION 0
1652 #endif
1653#endif
1654
1655/** \struct VmaAllocator
1656\brief Represents main object of this library initialized.
1657
1658Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1659Call function vmaDestroyAllocator() to destroy it.
1660
1661It is recommended to create just one object of this type per `VkDevice` object,
1662right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1663*/
1664VK_DEFINE_HANDLE(VmaAllocator)
1665
1666/// Callback function called after successful vkAllocateMemory.
1667typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1668 VmaAllocator allocator,
1669 uint32_t memoryType,
1670 VkDeviceMemory memory,
1671 VkDeviceSize size);
1672/// Callback function called before vkFreeMemory.
1673typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1674 VmaAllocator allocator,
1675 uint32_t memoryType,
1676 VkDeviceMemory memory,
1677 VkDeviceSize size);
1678
1679/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1680
1681Provided for informative purpose, e.g. to gather statistics about number of
1682allocations or total amount of memory allocated in Vulkan.
1683
1684Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1685*/
1686typedef struct VmaDeviceMemoryCallbacks {
1687 /// Optional, can be null.
1688 PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1689 /// Optional, can be null.
1690 PFN_vmaFreeDeviceMemoryFunction pfnFree;
1691} VmaDeviceMemoryCallbacks;
1692
1693/// Flags for created #VmaAllocator.
1694typedef enum VmaAllocatorCreateFlagBits {
1695 /** \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.
1696
1697 Using this flag may increase performance because internal mutexes are not used.
1698 */
1699 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1700 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1701
1702 Using this extenion will automatically allocate dedicated blocks of memory for
1703 some buffers and images instead of suballocating place for them out of bigger
1704 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1705 flag) when it is recommended by the driver. It may improve performance on some
1706 GPUs.
1707
1708 You may set this flag only if you found out that following device extensions are
1709 supported, you enabled them while creating Vulkan device passed as
1710 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1711 library:
1712
1713 - VK_KHR_get_memory_requirements2
1714 - VK_KHR_dedicated_allocation
1715
1716When this flag is set, you can experience following warnings reported by Vulkan
1717validation layer. You can ignore them.
1718
1719> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1720 */
1721 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1722
1723 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1724} VmaAllocatorCreateFlagBits;
1725typedef VkFlags VmaAllocatorCreateFlags;
1726
1727/** \brief Pointers to some Vulkan functions - a subset used by the library.
1728
1729Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1730*/
1731typedef struct VmaVulkanFunctions {
1732 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1733 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1734 PFN_vkAllocateMemory vkAllocateMemory;
1735 PFN_vkFreeMemory vkFreeMemory;
1736 PFN_vkMapMemory vkMapMemory;
1737 PFN_vkUnmapMemory vkUnmapMemory;
1738 PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1739 PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1740 PFN_vkBindBufferMemory vkBindBufferMemory;
1741 PFN_vkBindImageMemory vkBindImageMemory;
1742 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1743 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1744 PFN_vkCreateBuffer vkCreateBuffer;
1745 PFN_vkDestroyBuffer vkDestroyBuffer;
1746 PFN_vkCreateImage vkCreateImage;
1747 PFN_vkDestroyImage vkDestroyImage;
1748 PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1749#if VMA_DEDICATED_ALLOCATION
1750 PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1751 PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1752#endif
1753} VmaVulkanFunctions;
1754
1755/// Flags to be used in VmaRecordSettings::flags.
1756typedef enum VmaRecordFlagBits {
1757 /** \brief Enables flush after recording every function call.
1758
1759 Enable it if you expect your application to crash, which may leave recording file truncated.
1760 It may degrade performance though.
1761 */
1762 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1763
1764 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1765} VmaRecordFlagBits;
1766typedef VkFlags VmaRecordFlags;
1767
1768/// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1769typedef struct VmaRecordSettings
1770{
1771 /// Flags for recording. Use #VmaRecordFlagBits enum.
1772 VmaRecordFlags flags;
1773 /** \brief Path to the file that should be written by the recording.
1774
1775 Suggested extension: "csv".
1776 If the file already exists, it will be overwritten.
1777 It will be opened for the whole time #VmaAllocator object is alive.
1778 If opening this file fails, creation of the whole allocator object fails.
1779 */
1780 const char* pFilePath;
1781} VmaRecordSettings;
1782
1783/// Description of a Allocator to be created.
1784typedef struct VmaAllocatorCreateInfo
1785{
1786 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1787 VmaAllocatorCreateFlags flags;
1788 /// Vulkan physical device.
1789 /** It must be valid throughout whole lifetime of created allocator. */
1790 VkPhysicalDevice physicalDevice;
1791 /// Vulkan device.
1792 /** It must be valid throughout whole lifetime of created allocator. */
1793 VkDevice device;
1794 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1795 /** Set to 0 to use default, which is currently 256 MiB. */
1796 VkDeviceSize preferredLargeHeapBlockSize;
1797 /// Custom CPU memory allocation callbacks. Optional.
1798 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1799 const VkAllocationCallbacks* pAllocationCallbacks;
1800 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1801 /** Optional, can be null. */
1802 const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1803 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1804
1805 This value is used only when you make allocations with
1806 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1807 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1808
1809 For example, if you double-buffer your command buffers, so resources used for
1810 rendering in previous frame may still be in use by the GPU at the moment you
1811 allocate resources needed for the current frame, set this value to 1.
1812
1813 If you want to allow any allocations other than used in the current frame to
1814 become lost, set this value to 0.
1815 */
1816 uint32_t frameInUseCount;
1817 /** \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.
1818
1819 If not NULL, it must be a pointer to an array of
1820 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1821 maximum number of bytes that can be allocated out of particular Vulkan memory
1822 heap.
1823
1824 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1825 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1826
1827 If there is a limit defined for a heap:
1828
1829 - If user tries to allocate more memory from that heap using this allocator,
1830 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1831 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1832 value of this limit will be reported instead when using vmaGetMemoryProperties().
1833
1834 Warning! Using this feature may not be equivalent to installing a GPU with
1835 smaller amount of memory, because graphics driver doesn't necessary fail new
1836 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1837 exceeded. It may return success and just silently migrate some device memory
1838 blocks to system RAM. This driver behavior can also be controlled using
1839 VK_AMD_memory_overallocation_behavior extension.
1840 */
1841 const VkDeviceSize* pHeapSizeLimit;
1842 /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1843
1844 If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1845 you can pass null as this member, because the library will fetch pointers to
1846 Vulkan functions internally in a static way, like:
1847
1848 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1849
1850 Fill this member if you want to provide your own pointers to Vulkan functions,
1851 e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1852 */
1853 const VmaVulkanFunctions* pVulkanFunctions;
1854 /** \brief Parameters for recording of VMA calls. Can be null.
1855
1856 If not null, it enables recording of calls to VMA functions to a file.
1857 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1858 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1859 */
1860 const VmaRecordSettings* pRecordSettings;
1861} VmaAllocatorCreateInfo;
1862
1863/// Creates Allocator object.
1864VkResult vmaCreateAllocator(
1865 const VmaAllocatorCreateInfo* pCreateInfo,
1866 VmaAllocator* pAllocator);
1867
1868/// Destroys allocator object.
1869void vmaDestroyAllocator(
1870 VmaAllocator allocator);
1871
1872/**
1873PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1874You can access it here, without fetching it again on your own.
1875*/
1876void vmaGetPhysicalDeviceProperties(
1877 VmaAllocator allocator,
1878 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1879
1880/**
1881PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1882You can access it here, without fetching it again on your own.
1883*/
1884void vmaGetMemoryProperties(
1885 VmaAllocator allocator,
1886 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1887
1888/**
1889\brief Given Memory Type Index, returns Property Flags of this memory type.
1890
1891This is just a convenience function. Same information can be obtained using
1892vmaGetMemoryProperties().
1893*/
1894void vmaGetMemoryTypeProperties(
1895 VmaAllocator allocator,
1896 uint32_t memoryTypeIndex,
1897 VkMemoryPropertyFlags* pFlags);
1898
1899/** \brief Sets index of the current frame.
1900
1901This function must be used if you make allocations with
1902#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1903#VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1904when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1905become lost in the current frame.
1906*/
1907void vmaSetCurrentFrameIndex(
1908 VmaAllocator allocator,
1909 uint32_t frameIndex);
1910
1911/** \brief Calculated statistics of memory usage in entire allocator.
1912*/
1913typedef struct VmaStatInfo
1914{
1915 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1916 uint32_t blockCount;
1917 /// Number of #VmaAllocation allocation objects allocated.
1918 uint32_t allocationCount;
1919 /// Number of free ranges of memory between allocations.
1920 uint32_t unusedRangeCount;
1921 /// Total number of bytes occupied by all allocations.
1922 VkDeviceSize usedBytes;
1923 /// Total number of bytes occupied by unused ranges.
1924 VkDeviceSize unusedBytes;
1925 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1926 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1927} VmaStatInfo;
1928
1929/// General statistics from current state of Allocator.
1930typedef struct VmaStats
1931{
1932 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1933 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1934 VmaStatInfo total;
1935} VmaStats;
1936
1937/// Retrieves statistics from current state of the Allocator.
1938void vmaCalculateStats(
1939 VmaAllocator allocator,
1940 VmaStats* pStats);
1941
1942#define VMA_STATS_STRING_ENABLED 1
1943
1944#if VMA_STATS_STRING_ENABLED
1945
1946/// Builds and returns statistics as string in JSON format.
1947/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1948*/
1949void vmaBuildStatsString(
1950 VmaAllocator allocator,
1951 char** ppStatsString,
1952 VkBool32 detailedMap);
1953
1954void vmaFreeStatsString(
1955 VmaAllocator allocator,
1956 char* pStatsString);
1957
1958#endif // #if VMA_STATS_STRING_ENABLED
1959
1960/** \struct VmaPool
1961\brief Represents custom memory pool
1962
1963Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1964Call function vmaDestroyPool() to destroy it.
1965
1966For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1967*/
1968VK_DEFINE_HANDLE(VmaPool)
1969
1970typedef enum VmaMemoryUsage
1971{
1972 /** No intended memory usage specified.
1973 Use other members of VmaAllocationCreateInfo to specify your requirements.
1974 */
1975 VMA_MEMORY_USAGE_UNKNOWN = 0,
1976 /** Memory will be used on device only, so fast access from the device is preferred.
1977 It usually means device-local GPU (video) memory.
1978 No need to be mappable on host.
1979 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1980
1981 Usage:
1982
1983 - Resources written and read by device, e.g. images used as attachments.
1984 - Resources transferred from host once (immutable) or infrequently and read by
1985 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1986 (constant) buffers, and majority of other types of resources used on GPU.
1987
1988 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1989 In such case, you are free to map it.
1990 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1991 */
1992 VMA_MEMORY_USAGE_GPU_ONLY = 1,
1993 /** Memory will be mappable on host.
1994 It usually means CPU (system) memory.
1995 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1996 CPU access is typically uncached. Writes may be write-combined.
1997 Resources created in this pool may still be accessible to the device, but access to them can be slow.
1998 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
1999
2000 Usage: Staging copy of resources used as transfer source.
2001 */
2002 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2003 /**
2004 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2005 CPU access is typically uncached. Writes may be write-combined.
2006
2007 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
2008 */
2009 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2010 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2011 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2012
2013 Usage:
2014
2015 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2016 - 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.
2017 */
2018 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2019 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2020} VmaMemoryUsage;
2021
2022/// Flags to be passed as VmaAllocationCreateInfo::flags.
2023typedef enum VmaAllocationCreateFlagBits {
2024 /** \brief Set this flag if the allocation should have its own memory block.
2025
2026 Use it for special, big resources, like fullscreen images used as attachments.
2027
2028 This flag must also be used for host visible resources that you want to map
2029 simultaneously because otherwise they might end up as regions of the same
2030 `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2031 simultaneously is illegal.
2032
2033 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2034 */
2035 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2036
2037 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2038
2039 If new allocation cannot be placed in any of the existing blocks, allocation
2040 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2041
2042 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2043 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2044
2045 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2046 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2047 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2048
2049 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2050
2051 Is it valid to use this flag for allocation made from memory type that is not
2052 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2053 useful if you need an allocation that is efficient to use on GPU
2054 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2055 support it (e.g. Intel GPU).
2056
2057 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2058 */
2059 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2060 /** Allocation created with this flag can become lost as a result of another
2061 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2062 must check it before use.
2063
2064 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2065 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2066
2067 For details about supporting lost allocations, see Lost Allocations
2068 chapter of User Guide on Main Page.
2069
2070 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2071 */
2072 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2073 /** While creating allocation using this flag, other allocations that were
2074 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2075
2076 For details about supporting lost allocations, see Lost Allocations
2077 chapter of User Guide on Main Page.
2078 */
2079 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2080 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2081 null-terminated string. Instead of copying pointer value, a local copy of the
2082 string is made and stored in allocation's `pUserData`. The string is automatically
2083 freed together with the allocation. It is also used in vmaBuildStatsString().
2084 */
2085 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2086 /** Allocation will be created from upper stack in a double stack pool.
2087
2088 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2089 */
2090 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2091
2092 /** Allocation strategy that chooses smallest possible free range for the
2093 allocation.
2094 */
2095 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2096 /** Allocation strategy that chooses biggest possible free range for the
2097 allocation.
2098 */
2099 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2100 /** Allocation strategy that chooses first suitable free range for the
2101 allocation.
2102
2103 "First" doesn't necessarily means the one with smallest offset in memory,
2104 but rather the one that is easiest and fastest to find.
2105 */
2106 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2107
2108 /** Allocation strategy that tries to minimize memory usage.
2109 */
2110 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2111 /** Allocation strategy that tries to minimize allocation time.
2112 */
2113 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2114 /** Allocation strategy that tries to minimize memory fragmentation.
2115 */
2116 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2117
2118 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2119 */
2120 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2121 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2122 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2123 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2124
2125 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2126} VmaAllocationCreateFlagBits;
2127typedef VkFlags VmaAllocationCreateFlags;
2128
2129typedef struct VmaAllocationCreateInfo
2130{
2131 /// Use #VmaAllocationCreateFlagBits enum.
2132 VmaAllocationCreateFlags flags;
2133 /** \brief Intended usage of memory.
2134
2135 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2136 If `pool` is not null, this member is ignored.
2137 */
2138 VmaMemoryUsage usage;
2139 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2140
2141 Leave 0 if you specify memory requirements in other way. \n
2142 If `pool` is not null, this member is ignored.*/
2143 VkMemoryPropertyFlags requiredFlags;
2144 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2145
2146 Set to 0 if no additional flags are prefered. \n
2147 If `pool` is not null, this member is ignored. */
2148 VkMemoryPropertyFlags preferredFlags;
2149 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2150
2151 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2152 it meets other requirements specified by this structure, with no further
2153 restrictions on memory type index. \n
2154 If `pool` is not null, this member is ignored.
2155 */
2156 uint32_t memoryTypeBits;
2157 /** \brief Pool that this allocation should be created in.
2158
2159 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2160 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2161 */
2162 VmaPool pool;
2163 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2164
2165 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2166 null or pointer to a null-terminated string. The string will be then copied to
2167 internal buffer, so it doesn't need to be valid after allocation call.
2168 */
2169 void* pUserData;
2170} VmaAllocationCreateInfo;
2171
2172/**
2173\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2174
2175This algorithm tries to find a memory type that:
2176
2177- Is allowed by memoryTypeBits.
2178- Contains all the flags from pAllocationCreateInfo->requiredFlags.
2179- Matches intended usage.
2180- Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2181
2182\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2183from this function or any other allocating function probably means that your
2184device doesn't support any memory type with requested features for the specific
2185type of resource you want to use it for. Please check parameters of your
2186resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2187*/
2188VkResult vmaFindMemoryTypeIndex(
2189 VmaAllocator allocator,
2190 uint32_t memoryTypeBits,
2191 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2192 uint32_t* pMemoryTypeIndex);
2193
2194/**
2195\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2196
2197It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2198It internally creates a temporary, dummy buffer that never has memory bound.
2199It is just a convenience function, equivalent to calling:
2200
2201- `vkCreateBuffer`
2202- `vkGetBufferMemoryRequirements`
2203- `vmaFindMemoryTypeIndex`
2204- `vkDestroyBuffer`
2205*/
2206VkResult vmaFindMemoryTypeIndexForBufferInfo(
2207 VmaAllocator allocator,
2208 const VkBufferCreateInfo* pBufferCreateInfo,
2209 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2210 uint32_t* pMemoryTypeIndex);
2211
2212/**
2213\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2214
2215It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2216It internally creates a temporary, dummy image that never has memory bound.
2217It is just a convenience function, equivalent to calling:
2218
2219- `vkCreateImage`
2220- `vkGetImageMemoryRequirements`
2221- `vmaFindMemoryTypeIndex`
2222- `vkDestroyImage`
2223*/
2224VkResult vmaFindMemoryTypeIndexForImageInfo(
2225 VmaAllocator allocator,
2226 const VkImageCreateInfo* pImageCreateInfo,
2227 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2228 uint32_t* pMemoryTypeIndex);
2229
2230/// Flags to be passed as VmaPoolCreateInfo::flags.
2231typedef enum VmaPoolCreateFlagBits {
2232 /** \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.
2233
2234 This is an optional optimization flag.
2235
2236 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2237 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2238 knows exact type of your allocations so it can handle Buffer-Image Granularity
2239 in the optimal way.
2240
2241 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2242 exact type of such allocations is not known, so allocator must be conservative
2243 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2244 (wasted memory). In that case, if you can make sure you always allocate only
2245 buffers and linear images or only optimal images out of this pool, use this flag
2246 to make allocator disregard Buffer-Image Granularity and so make allocations
2247 faster and more optimal.
2248 */
2249 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2250
2251 /** \brief Enables alternative, linear allocation algorithm in this pool.
2252
2253 Specify this flag to enable linear allocation algorithm, which always creates
2254 new allocations after last one and doesn't reuse space from allocations freed in
2255 between. It trades memory consumption for simplified algorithm and data
2256 structure, which has better performance and uses less memory for metadata.
2257
2258 By using this flag, you can achieve behavior of free-at-once, stack,
2259 ring buffer, and double stack. For details, see documentation chapter
2260 \ref linear_algorithm.
2261
2262 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2263
2264 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2265 */
2266 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2267
2268 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2269
2270 It operates on a tree of blocks, each having size that is a power of two and
2271 a half of its parent's size. Comparing to default algorithm, this one provides
2272 faster allocation and deallocation and decreased external fragmentation,
2273 at the expense of more memory wasted (internal fragmentation).
2274
2275 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2276 */
2277 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2278
2279 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2280 */
2281 VMA_POOL_CREATE_ALGORITHM_MASK =
2282 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2283 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2284
2285 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2286} VmaPoolCreateFlagBits;
2287typedef VkFlags VmaPoolCreateFlags;
2288
2289/** \brief Describes parameter of created #VmaPool.
2290*/
2291typedef struct VmaPoolCreateInfo {
2292 /** \brief Vulkan memory type index to allocate this pool from.
2293 */
2294 uint32_t memoryTypeIndex;
2295 /** \brief Use combination of #VmaPoolCreateFlagBits.
2296 */
2297 VmaPoolCreateFlags flags;
2298 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2299
2300 Specify nonzero to set explicit, constant size of memory blocks used by this
2301 pool.
2302
2303 Leave 0 to use default and let the library manage block sizes automatically.
2304 Sizes of particular blocks may vary.
2305 */
2306 VkDeviceSize blockSize;
2307 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2308
2309 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2310 */
2311 size_t minBlockCount;
2312 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2313
2314 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2315
2316 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2317 throughout whole lifetime of this pool.
2318 */
2319 size_t maxBlockCount;
2320 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2321
2322 This value is used only when you make allocations with
2323 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2324 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2325
2326 For example, if you double-buffer your command buffers, so resources used for
2327 rendering in previous frame may still be in use by the GPU at the moment you
2328 allocate resources needed for the current frame, set this value to 1.
2329
2330 If you want to allow any allocations other than used in the current frame to
2331 become lost, set this value to 0.
2332 */
2333 uint32_t frameInUseCount;
2334} VmaPoolCreateInfo;
2335
2336/** \brief Describes parameter of existing #VmaPool.
2337*/
2338typedef struct VmaPoolStats {
2339 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2340 */
2341 VkDeviceSize size;
2342 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2343 */
2344 VkDeviceSize unusedSize;
2345 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2346 */
2347 size_t allocationCount;
2348 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2349 */
2350 size_t unusedRangeCount;
2351 /** \brief Size of the largest continuous free memory region available for new allocation.
2352
2353 Making a new allocation of that size is not guaranteed to succeed because of
2354 possible additional margin required to respect alignment and buffer/image
2355 granularity.
2356 */
2357 VkDeviceSize unusedRangeSizeMax;
2358 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2359 */
2360 size_t blockCount;
2361} VmaPoolStats;
2362
2363/** \brief Allocates Vulkan device memory and creates #VmaPool object.
2364
2365@param allocator Allocator object.
2366@param pCreateInfo Parameters of pool to create.
2367@param[out] pPool Handle to created pool.
2368*/
2369VkResult vmaCreatePool(
2370 VmaAllocator allocator,
2371 const VmaPoolCreateInfo* pCreateInfo,
2372 VmaPool* pPool);
2373
2374/** \brief Destroys #VmaPool object and frees Vulkan device memory.
2375*/
2376void vmaDestroyPool(
2377 VmaAllocator allocator,
2378 VmaPool pool);
2379
2380/** \brief Retrieves statistics of existing #VmaPool object.
2381
2382@param allocator Allocator object.
2383@param pool Pool object.
2384@param[out] pPoolStats Statistics of specified pool.
2385*/
2386void vmaGetPoolStats(
2387 VmaAllocator allocator,
2388 VmaPool pool,
2389 VmaPoolStats* pPoolStats);
2390
2391/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2392
2393@param allocator Allocator object.
2394@param pool Pool.
2395@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2396*/
2397void vmaMakePoolAllocationsLost(
2398 VmaAllocator allocator,
2399 VmaPool pool,
2400 size_t* pLostAllocationCount);
2401
2402/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2403
2404Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2405`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2406`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2407
2408Possible return values:
2409
2410- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2411- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2412- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2413 `VMA_ASSERT` is also fired in that case.
2414- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2415*/
2416VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2417
2418/** \struct VmaAllocation
2419\brief Represents single memory allocation.
2420
2421It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2422plus unique offset.
2423
2424There are multiple ways to create such object.
2425You need to fill structure VmaAllocationCreateInfo.
2426For more information see [Choosing memory type](@ref choosing_memory_type).
2427
2428Although the library provides convenience functions that create Vulkan buffer or image,
2429allocate memory for it and bind them together,
2430binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2431Allocation object can exist without buffer/image bound,
2432binding can be done manually by the user, and destruction of it can be done
2433independently of destruction of the allocation.
2434
2435The object also remembers its size and some other information.
2436To retrieve this information, use function vmaGetAllocationInfo() and inspect
2437returned structure VmaAllocationInfo.
2438
2439Some kinds allocations can be in lost state.
2440For more information, see [Lost allocations](@ref lost_allocations).
2441*/
2442VK_DEFINE_HANDLE(VmaAllocation)
2443
2444/** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2445*/
2446typedef struct VmaAllocationInfo {
2447 /** \brief Memory type index that this allocation was allocated from.
2448
2449 It never changes.
2450 */
2451 uint32_t memoryType;
2452 /** \brief Handle to Vulkan memory object.
2453
2454 Same memory object can be shared by multiple allocations.
2455
2456 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2457
2458 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2459 */
2460 VkDeviceMemory deviceMemory;
2461 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2462
2463 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2464 */
2465 VkDeviceSize offset;
2466 /** \brief Size of this allocation, in bytes.
2467
2468 It never changes, unless allocation is lost.
2469 */
2470 VkDeviceSize size;
2471 /** \brief Pointer to the beginning of this allocation as mapped data.
2472
2473 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2474 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2475
2476 It can change after call to vmaMapMemory(), vmaUnmapMemory().
2477 It can also change after call to vmaDefragment() if this allocation is passed to the function.
2478 */
2479 void* pMappedData;
2480 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2481
2482 It can change after call to vmaSetAllocationUserData() for this allocation.
2483 */
2484 void* pUserData;
2485} VmaAllocationInfo;
2486
2487/** \brief General purpose memory allocation.
2488
2489@param[out] pAllocation Handle to allocated memory.
2490@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2491
2492You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2493
2494It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2495vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2496*/
2497VkResult vmaAllocateMemory(
2498 VmaAllocator allocator,
2499 const VkMemoryRequirements* pVkMemoryRequirements,
2500 const VmaAllocationCreateInfo* pCreateInfo,
2501 VmaAllocation* pAllocation,
2502 VmaAllocationInfo* pAllocationInfo);
2503
2504/** \brief General purpose memory allocation for multiple allocation objects at once.
2505
2506@param allocator Allocator object.
2507@param pVkMemoryRequirements Memory requirements for each allocation.
2508@param pCreateInfo Creation parameters for each alloction.
2509@param allocationCount Number of allocations to make.
2510@param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2511@param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2512
2513You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2514
2515Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2516It is just a general purpose allocation function able to make multiple allocations at once.
2517It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2518
2519All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2520If any allocation fails, all allocations already made within this function call are also freed, so that when
2521returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2522*/
2523VkResult vmaAllocateMemoryPages(
2524 VmaAllocator allocator,
2525 const VkMemoryRequirements* pVkMemoryRequirements,
2526 const VmaAllocationCreateInfo* pCreateInfo,
2527 size_t allocationCount,
2528 VmaAllocation* pAllocations,
2529 VmaAllocationInfo* pAllocationInfo);
2530
2531/**
2532@param[out] pAllocation Handle to allocated memory.
2533@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2534
2535You should free the memory using vmaFreeMemory().
2536*/
2537VkResult vmaAllocateMemoryForBuffer(
2538 VmaAllocator allocator,
2539 VkBuffer buffer,
2540 const VmaAllocationCreateInfo* pCreateInfo,
2541 VmaAllocation* pAllocation,
2542 VmaAllocationInfo* pAllocationInfo);
2543
2544/// Function similar to vmaAllocateMemoryForBuffer().
2545VkResult vmaAllocateMemoryForImage(
2546 VmaAllocator allocator,
2547 VkImage image,
2548 const VmaAllocationCreateInfo* pCreateInfo,
2549 VmaAllocation* pAllocation,
2550 VmaAllocationInfo* pAllocationInfo);
2551
2552/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2553
2554Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2555*/
2556void vmaFreeMemory(
2557 VmaAllocator allocator,
2558 VmaAllocation allocation);
2559
2560/** \brief Frees memory and destroys multiple allocations.
2561
2562Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2563It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2564vmaAllocateMemoryPages() and other functions.
2565It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2566
2567Allocations in `pAllocations` array can come from any memory pools and types.
2568Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2569*/
2570void vmaFreeMemoryPages(
2571 VmaAllocator allocator,
2572 size_t allocationCount,
2573 VmaAllocation* pAllocations);
2574
2575/** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2576
2577Tries to change allocation's size without moving or reallocating it.
2578You can both shrink and grow allocation size.
2579When growing, it succeeds only when the allocation belongs to a memory block with enough
2580free space after it.
2581
2582Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2583Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2584
2585After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2586All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2587
2588- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2589- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2590- Resizing dedicated allocations, as well as allocations created in pools that use linear
2591 or buddy algorithm, is not supported.
2592 The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2593 Support may be added in the future.
2594*/
2595VkResult vmaResizeAllocation(
2596 VmaAllocator allocator,
2597 VmaAllocation allocation,
2598 VkDeviceSize newSize);
2599
2600/** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2601
2602Current paramters of given allocation are returned in `pAllocationInfo`.
2603
2604This function also atomically "touches" allocation - marks it as used in current frame,
2605just like vmaTouchAllocation().
2606If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2607
2608Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2609you can avoid calling it too often.
2610
2611- You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2612 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2613 (e.g. due to defragmentation or allocation becoming lost).
2614- If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2615*/
2616void vmaGetAllocationInfo(
2617 VmaAllocator allocator,
2618 VmaAllocation allocation,
2619 VmaAllocationInfo* pAllocationInfo);
2620
2621/** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2622
2623If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2624this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2625It then also atomically "touches" the allocation - marks it as used in current frame,
2626so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2627
2628If the allocation is in lost state, the function returns `VK_FALSE`.
2629Memory of such allocation, as well as buffer or image bound to it, should not be used.
2630Lost allocation and the buffer/image still need to be destroyed.
2631
2632If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2633this function always returns `VK_TRUE`.
2634*/
2635VkBool32 vmaTouchAllocation(
2636 VmaAllocator allocator,
2637 VmaAllocation allocation);
2638
2639/** \brief Sets pUserData in given allocation to new value.
2640
2641If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2642pUserData must be either null, or pointer to a null-terminated string. The function
2643makes local copy of the string and sets it as allocation's `pUserData`. String
2644passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2645you can free it after this call. String previously pointed by allocation's
2646pUserData is freed from memory.
2647
2648If the flag was not used, the value of pointer `pUserData` is just copied to
2649allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2650as a pointer, ordinal number or some handle to you own data.
2651*/
2652void vmaSetAllocationUserData(
2653 VmaAllocator allocator,
2654 VmaAllocation allocation,
2655 void* pUserData);
2656
2657/** \brief Creates new allocation that is in lost state from the beginning.
2658
2659It can be useful if you need a dummy, non-null allocation.
2660
2661You still need to destroy created object using vmaFreeMemory().
2662
2663Returned allocation is not tied to any specific memory pool or memory type and
2664not bound to any image or buffer. It has size = 0. It cannot be turned into
2665a real, non-empty allocation.
2666*/
2667void vmaCreateLostAllocation(
2668 VmaAllocator allocator,
2669 VmaAllocation* pAllocation);
2670
2671/** \brief Maps memory represented by given allocation and returns pointer to it.
2672
2673Maps memory represented by given allocation to make it accessible to CPU code.
2674When succeeded, `*ppData` contains pointer to first byte of this memory.
2675If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2676correctly offseted to the beginning of region assigned to this particular
2677allocation.
2678
2679Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2680function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2681multiple times simultaneously, it is safe to call this function on allocations
2682assigned to the same memory block. Actual Vulkan memory will be mapped on first
2683mapping and unmapped on last unmapping.
2684
2685If the function succeeded, you must call vmaUnmapMemory() to unmap the
2686allocation when mapping is no longer needed or before freeing the allocation, at
2687the latest.
2688
2689It also safe to call this function multiple times on the same allocation. You
2690must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2691
2692It is also safe to call this function on allocation created with
2693#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2694You must still call vmaUnmapMemory() same number of times as you called
2695vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2696"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2697
2698This function fails when used on allocation made in memory type that is not
2699`HOST_VISIBLE`.
2700
2701This function always fails when called for allocation that was created with
2702#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2703mapped.
2704*/
2705VkResult vmaMapMemory(
2706 VmaAllocator allocator,
2707 VmaAllocation allocation,
2708 void** ppData);
2709
2710/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2711
2712For details, see description of vmaMapMemory().
2713*/
2714void vmaUnmapMemory(
2715 VmaAllocator allocator,
2716 VmaAllocation allocation);
2717
2718/** \brief Flushes memory of given allocation.
2719
2720Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2721
2722- `offset` must be relative to the beginning of allocation.
2723- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2724- `offset` and `size` don't have to be aligned.
2725 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2726- If `size` is 0, this call is ignored.
2727- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2728 this call is ignored.
2729*/
2730void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2731
2732/** \brief Invalidates memory of given allocation.
2733
2734Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2735
2736- `offset` must be relative to the beginning of allocation.
2737- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2738- `offset` and `size` don't have to be aligned.
2739 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2740- If `size` is 0, this call is ignored.
2741- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2742 this call is ignored.
2743*/
2744void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2745
2746/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2747
2748@param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2749
2750Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2751`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2752`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2753
2754Possible return values:
2755
2756- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2757- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2758- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2759 `VMA_ASSERT` is also fired in that case.
2760- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2761*/
2762VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2763
2764/** \struct VmaDefragmentationContext
2765\brief Represents Opaque object that represents started defragmentation process.
2766
2767Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2768Call function vmaDefragmentationEnd() to destroy it.
2769*/
2770VK_DEFINE_HANDLE(VmaDefragmentationContext)
2771
2772/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2773typedef enum VmaDefragmentationFlagBits {
2774 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2775} VmaDefragmentationFlagBits;
2776typedef VkFlags VmaDefragmentationFlags;
2777
2778/** \brief Parameters for defragmentation.
2779
2780To be used with function vmaDefragmentationBegin().
2781*/
2782typedef struct VmaDefragmentationInfo2 {
2783 /** \brief Reserved for future use. Should be 0.
2784 */
2785 VmaDefragmentationFlags flags;
2786 /** \brief Number of allocations in `pAllocations` array.
2787 */
2788 uint32_t allocationCount;
2789 /** \brief Pointer to array of allocations that can be defragmented.
2790
2791 The array should have `allocationCount` elements.
2792 The array should not contain nulls.
2793 Elements in the array should be unique - same allocation cannot occur twice.
2794 It is safe to pass allocations that are in the lost state - they are ignored.
2795 All allocations not present in this array are considered non-moveable during this defragmentation.
2796 */
2797 VmaAllocation* pAllocations;
2798 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2799
2800 The array should have `allocationCount` elements.
2801 You can pass null if you are not interested in this information.
2802 */
2803 VkBool32* pAllocationsChanged;
2804 /** \brief Numer of pools in `pPools` array.
2805 */
2806 uint32_t poolCount;
2807 /** \brief Either null or pointer to array of pools to be defragmented.
2808
2809 All the allocations in the specified pools can be moved during defragmentation
2810 and there is no way to check if they were really moved as in `pAllocationsChanged`,
2811 so you must query all the allocations in all these pools for new `VkDeviceMemory`
2812 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2813 and images bound to them.
2814
2815 The array should have `poolCount` elements.
2816 The array should not contain nulls.
2817 Elements in the array should be unique - same pool cannot occur twice.
2818
2819 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2820 It might be more efficient.
2821 */
2822 VmaPool* pPools;
2823 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2824
2825 `VK_WHOLE_SIZE` means no limit.
2826 */
2827 VkDeviceSize maxCpuBytesToMove;
2828 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2829
2830 `UINT32_MAX` means no limit.
2831 */
2832 uint32_t maxCpuAllocationsToMove;
2833 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2834
2835 `VK_WHOLE_SIZE` means no limit.
2836 */
2837 VkDeviceSize maxGpuBytesToMove;
2838 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2839
2840 `UINT32_MAX` means no limit.
2841 */
2842 uint32_t maxGpuAllocationsToMove;
2843 /** \brief Optional. Command buffer where GPU copy commands will be posted.
2844
2845 If not null, it must be a valid command buffer handle that supports Transfer queue type.
2846 It must be in the recording state and outside of a render pass instance.
2847 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2848
2849 Passing null means that only CPU defragmentation will be performed.
2850 */
2851 VkCommandBuffer commandBuffer;
2852} VmaDefragmentationInfo2;
2853
2854/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2855
2856\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2857*/
2858typedef struct VmaDefragmentationInfo {
2859 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2860
2861 Default is `VK_WHOLE_SIZE`, which means no limit.
2862 */
2863 VkDeviceSize maxBytesToMove;
2864 /** \brief Maximum number of allocations that can be moved to different place.
2865
2866 Default is `UINT32_MAX`, which means no limit.
2867 */
2868 uint32_t maxAllocationsToMove;
2869} VmaDefragmentationInfo;
2870
2871/** \brief Statistics returned by function vmaDefragment(). */
2872typedef struct VmaDefragmentationStats {
2873 /// Total number of bytes that have been copied while moving allocations to different places.
2874 VkDeviceSize bytesMoved;
2875 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2876 VkDeviceSize bytesFreed;
2877 /// Number of allocations that have been moved to different places.
2878 uint32_t allocationsMoved;
2879 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2880 uint32_t deviceMemoryBlocksFreed;
2881} VmaDefragmentationStats;
2882
2883/** \brief Begins defragmentation process.
2884
2885@param allocator Allocator object.
2886@param pInfo Structure filled with parameters of defragmentation.
2887@param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2888@param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2889@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.
2890
2891Use this function instead of old, deprecated vmaDefragment().
2892
2893Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2894
2895- You should not use any of allocations passed as `pInfo->pAllocations` or
2896 any allocations that belong to pools passed as `pInfo->pPools`,
2897 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2898 their data.
2899- Some mutexes protecting internal data structures may be locked, so trying to
2900 make or free any allocations, bind buffers or images, map memory, or launch
2901 another simultaneous defragmentation in between may cause stall (when done on
2902 another thread) or deadlock (when done on the same thread), unless you are
2903 100% sure that defragmented allocations are in different pools.
2904- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2905 They become valid after call to vmaDefragmentationEnd().
2906- If `pInfo->commandBuffer` is not null, you must submit that command buffer
2907 and make sure it finished execution before calling vmaDefragmentationEnd().
2908*/
2909VkResult vmaDefragmentationBegin(
2910 VmaAllocator allocator,
2911 const VmaDefragmentationInfo2* pInfo,
2912 VmaDefragmentationStats* pStats,
2913 VmaDefragmentationContext *pContext);
2914
2915/** \brief Ends defragmentation process.
2916
2917Use this function to finish defragmentation started by vmaDefragmentationBegin().
2918It is safe to pass `context == null`. The function then does nothing.
2919*/
2920VkResult vmaDefragmentationEnd(
2921 VmaAllocator allocator,
2922 VmaDefragmentationContext context);
2923
2924/** \brief Deprecated. Compacts memory by moving allocations.
2925
2926@param pAllocations Array of allocations that can be moved during this compation.
2927@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2928@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.
2929@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2930@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2931@return `VK_SUCCESS` if completed, negative error code in case of error.
2932
2933\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2934
2935This function works by moving allocations to different places (different
2936`VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2937usage. Only allocations that are in `pAllocations` array can be moved. All other
2938allocations are considered nonmovable in this call. Basic rules:
2939
2940- Only allocations made in memory types that have
2941 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2942 flags can be compacted. You may pass other allocations but it makes no sense -
2943 these will never be moved.
2944- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2945 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2946 passed to this function that come from such pools are ignored.
2947- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2948 created as dedicated allocations for any other reason are also ignored.
2949- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2950 flag can be compacted. If not persistently mapped, memory will be mapped
2951 temporarily inside this function if needed.
2952- You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2953
2954The function also frees empty `VkDeviceMemory` blocks.
2955
2956Warning: This function may be time-consuming, so you shouldn't call it too often
2957(like after every resource creation/destruction).
2958You can call it on special occasions (like when reloading a game level or
2959when you just destroyed a lot of objects). Calling it every frame may be OK, but
2960you should measure that on your platform.
2961
2962For more information, see [Defragmentation](@ref defragmentation) chapter.
2963*/
2964VkResult vmaDefragment(
2965 VmaAllocator allocator,
2966 VmaAllocation* pAllocations,
2967 size_t allocationCount,
2968 VkBool32* pAllocationsChanged,
2969 const VmaDefragmentationInfo *pDefragmentationInfo,
2970 VmaDefragmentationStats* pDefragmentationStats);
2971
2972/** \brief Binds buffer to allocation.
2973
2974Binds specified buffer to region of memory represented by specified allocation.
2975Gets `VkDeviceMemory` handle and offset from the allocation.
2976If you want to create a buffer, allocate memory for it and bind them together separately,
2977you should use this function for binding instead of standard `vkBindBufferMemory()`,
2978because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2979allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2980(which is illegal in Vulkan).
2981
2982It is recommended to use function vmaCreateBuffer() instead of this one.
2983*/
2984VkResult vmaBindBufferMemory(
2985 VmaAllocator allocator,
2986 VmaAllocation allocation,
2987 VkBuffer buffer);
2988
2989/** \brief Binds image to allocation.
2990
2991Binds specified image to region of memory represented by specified allocation.
2992Gets `VkDeviceMemory` handle and offset from the allocation.
2993If you want to create an image, allocate memory for it and bind them together separately,
2994you should use this function for binding instead of standard `vkBindImageMemory()`,
2995because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2996allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2997(which is illegal in Vulkan).
2998
2999It is recommended to use function vmaCreateImage() instead of this one.
3000*/
3001VkResult vmaBindImageMemory(
3002 VmaAllocator allocator,
3003 VmaAllocation allocation,
3004 VkImage image);
3005
3006/**
3007@param[out] pBuffer Buffer that was created.
3008@param[out] pAllocation Allocation that was created.
3009@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3010
3011This function automatically:
3012
3013-# Creates buffer.
3014-# Allocates appropriate memory for it.
3015-# Binds the buffer with the memory.
3016
3017If any of these operations fail, buffer and allocation are not created,
3018returned value is negative error code, *pBuffer and *pAllocation are null.
3019
3020If the function succeeded, you must destroy both buffer and allocation when you
3021no longer need them using either convenience function vmaDestroyBuffer() or
3022separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3023
3024If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3025VK_KHR_dedicated_allocation extension is used internally to query driver whether
3026it requires or prefers the new buffer to have dedicated allocation. If yes,
3027and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3028and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3029allocation for this buffer, just like when using
3030VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3031*/
3032VkResult vmaCreateBuffer(
3033 VmaAllocator allocator,
3034 const VkBufferCreateInfo* pBufferCreateInfo,
3035 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3036 VkBuffer* pBuffer,
3037 VmaAllocation* pAllocation,
3038 VmaAllocationInfo* pAllocationInfo);
3039
3040/** \brief Destroys Vulkan buffer and frees allocated memory.
3041
3042This is just a convenience function equivalent to:
3043
3044\code
3045vkDestroyBuffer(device, buffer, allocationCallbacks);
3046vmaFreeMemory(allocator, allocation);
3047\endcode
3048
3049It it safe to pass null as buffer and/or allocation.
3050*/
3051void vmaDestroyBuffer(
3052 VmaAllocator allocator,
3053 VkBuffer buffer,
3054 VmaAllocation allocation);
3055
3056/// Function similar to vmaCreateBuffer().
3057VkResult vmaCreateImage(
3058 VmaAllocator allocator,
3059 const VkImageCreateInfo* pImageCreateInfo,
3060 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3061 VkImage* pImage,
3062 VmaAllocation* pAllocation,
3063 VmaAllocationInfo* pAllocationInfo);
3064
3065/** \brief Destroys Vulkan image and frees allocated memory.
3066
3067This is just a convenience function equivalent to:
3068
3069\code
3070vkDestroyImage(device, image, allocationCallbacks);
3071vmaFreeMemory(allocator, allocation);
3072\endcode
3073
3074It it safe to pass null as image and/or allocation.
3075*/
3076void vmaDestroyImage(
3077 VmaAllocator allocator,
3078 VkImage image,
3079 VmaAllocation allocation);
3080
3081#ifdef __cplusplus
3082}
3083#endif
3084
3085#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3086
3087// For Visual Studio IntelliSense.
3088#if defined(__cplusplus) && defined(__INTELLISENSE__)
3089#define VMA_IMPLEMENTATION
3090#endif
3091
3092#ifdef VMA_IMPLEMENTATION
3093#undef VMA_IMPLEMENTATION
3094
3095#include <cstdint>
3096#include <cstdlib>
3097#include <cstring>
3098
3099/*******************************************************************************
3100CONFIGURATION SECTION
3101
3102Define some of these macros before each #include of this header or change them
3103here if you need other then default behavior depending on your environment.
3104*/
3105
3106/*
3107Define this macro to 1 to make the library fetch pointers to Vulkan functions
3108internally, like:
3109
3110 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3111
3112Define to 0 if you are going to provide you own pointers to Vulkan functions via
3113VmaAllocatorCreateInfo::pVulkanFunctions.
3114*/
3115#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3116#define VMA_STATIC_VULKAN_FUNCTIONS 1
3117#endif
3118
3119// Define this macro to 1 to make the library use STL containers instead of its own implementation.
3120//#define VMA_USE_STL_CONTAINERS 1
3121
3122/* Set this macro to 1 to make the library including and using STL containers:
3123std::pair, std::vector, std::list, std::unordered_map.
3124
3125Set it to 0 or undefined to make the library using its own implementation of
3126the containers.
3127*/
3128#if VMA_USE_STL_CONTAINERS
3129 #define VMA_USE_STL_VECTOR 1
3130 #define VMA_USE_STL_UNORDERED_MAP 1
3131 #define VMA_USE_STL_LIST 1
3132#endif
3133
3134#ifndef VMA_USE_STL_SHARED_MUTEX
3135 // Minimum Visual Studio 2015 Update 2
3136 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
3137 #define VMA_USE_STL_SHARED_MUTEX 1
3138 #endif
3139#endif
3140
3141#if VMA_USE_STL_VECTOR
3142 #include <vector>
3143#endif
3144
3145#if VMA_USE_STL_UNORDERED_MAP
3146 #include <unordered_map>
3147#endif
3148
3149#if VMA_USE_STL_LIST
3150 #include <list>
3151#endif
3152
3153/*
3154Following headers are used in this CONFIGURATION section only, so feel free to
3155remove them if not needed.
3156*/
3157#include <cassert> // for assert
3158#include <algorithm> // for min, max
3159#include <mutex>
3160#include <atomic> // for std::atomic
3161
3162#ifndef VMA_NULL
3163 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3164 #define VMA_NULL nullptr
3165#endif
3166
3167#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3168#include <cstdlib>
3169void *aligned_alloc(size_t alignment, size_t size)
3170{
3171 // alignment must be >= sizeof(void*)
3172 if(alignment < sizeof(void*))
3173 {
3174 alignment = sizeof(void*);
3175 }
3176
3177 return memalign(alignment, size);
3178}
3179#elif defined(__APPLE__) || defined(__ANDROID__)
3180#include <cstdlib>
3181void *aligned_alloc(size_t alignment, size_t size)
3182{
3183 // alignment must be >= sizeof(void*)
3184 if(alignment < sizeof(void*))
3185 {
3186 alignment = sizeof(void*);
3187 }
3188
3189 void *pointer;
3190 if(posix_memalign(&pointer, alignment, size) == 0)
3191 return pointer;
3192 return VMA_NULL;
3193}
3194#endif
3195
3196// If your compiler is not compatible with C++11 and definition of
3197// aligned_alloc() function is missing, uncommeting following line may help:
3198
3199//#include <malloc.h>
3200
3201// Normal assert to check for programmer's errors, especially in Debug configuration.
3202#ifndef VMA_ASSERT
3203 #ifdef _DEBUG
3204 #define VMA_ASSERT(expr) assert(expr)
3205 #else
3206 #define VMA_ASSERT(expr)
3207 #endif
3208#endif
3209
3210// Assert that will be called very often, like inside data structures e.g. operator[].
3211// Making it non-empty can make program slow.
3212#ifndef VMA_HEAVY_ASSERT
3213 #ifdef _DEBUG
3214 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3215 #else
3216 #define VMA_HEAVY_ASSERT(expr)
3217 #endif
3218#endif
3219
3220#ifndef VMA_ALIGN_OF
3221 #define VMA_ALIGN_OF(type) (__alignof(type))
3222#endif
3223
3224#ifndef VMA_SYSTEM_ALIGNED_MALLOC
3225 #if defined(_WIN32)
3226 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3227 #else
3228 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3229 #endif
3230#endif
3231
3232#ifndef VMA_SYSTEM_FREE
3233 #if defined(_WIN32)
3234 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
3235 #else
3236 #define VMA_SYSTEM_FREE(ptr) free(ptr)
3237 #endif
3238#endif
3239
3240#ifndef VMA_MIN
3241 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
3242#endif
3243
3244#ifndef VMA_MAX
3245 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
3246#endif
3247
3248#ifndef VMA_SWAP
3249 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
3250#endif
3251
3252#ifndef VMA_SORT
3253 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3254#endif
3255
3256#ifndef VMA_DEBUG_LOG
3257 #define VMA_DEBUG_LOG(format, ...)
3258 /*
3259 #define VMA_DEBUG_LOG(format, ...) do { \
3260 printf(format, __VA_ARGS__); \
3261 printf("\n"); \
3262 } while(false)
3263 */
3264#endif
3265
3266// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3267#if VMA_STATS_STRING_ENABLED
3268 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3269 {
3270 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3271 }
3272 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3273 {
3274 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3275 }
3276 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3277 {
3278 snprintf(outStr, strLen, "%p", ptr);
3279 }
3280#endif
3281
3282#ifndef VMA_MUTEX
3283 class VmaMutex
3284 {
3285 public:
3286 void Lock() { m_Mutex.lock(); }
3287 void Unlock() { m_Mutex.unlock(); }
3288 private:
3289 std::mutex m_Mutex;
3290 };
3291 #define VMA_MUTEX VmaMutex
3292#endif
3293
3294// Read-write mutex, where "read" is shared access, "write" is exclusive access.
3295#ifndef VMA_RW_MUTEX
3296 #if VMA_USE_STL_SHARED_MUTEX
3297 // Use std::shared_mutex from C++17.
3298 #include <shared_mutex>
3299 class VmaRWMutex
3300 {
3301 public:
3302 void LockRead() { m_Mutex.lock_shared(); }
3303 void UnlockRead() { m_Mutex.unlock_shared(); }
3304 void LockWrite() { m_Mutex.lock(); }
3305 void UnlockWrite() { m_Mutex.unlock(); }
3306 private:
3307 std::shared_mutex m_Mutex;
3308 };
3309 #define VMA_RW_MUTEX VmaRWMutex
3310 #elif defined(_WIN32)
3311 // Use SRWLOCK from WinAPI.
3312 class VmaRWMutex
3313 {
3314 public:
3315 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
3316 void LockRead() { AcquireSRWLockShared(&m_Lock); }
3317 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
3318 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
3319 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3320 private:
3321 SRWLOCK m_Lock;
3322 };
3323 #define VMA_RW_MUTEX VmaRWMutex
3324 #else
3325 // Less efficient fallback: Use normal mutex.
3326 class VmaRWMutex
3327 {
3328 public:
3329 void LockRead() { m_Mutex.Lock(); }
3330 void UnlockRead() { m_Mutex.Unlock(); }
3331 void LockWrite() { m_Mutex.Lock(); }
3332 void UnlockWrite() { m_Mutex.Unlock(); }
3333 private:
3334 VMA_MUTEX m_Mutex;
3335 };
3336 #define VMA_RW_MUTEX VmaRWMutex
3337 #endif // #if VMA_USE_STL_SHARED_MUTEX
3338#endif // #ifndef VMA_RW_MUTEX
3339
3340/*
3341If providing your own implementation, you need to implement a subset of std::atomic:
3342
3343- Constructor(uint32_t desired)
3344- uint32_t load() const
3345- void store(uint32_t desired)
3346- bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3347*/
3348#ifndef VMA_ATOMIC_UINT32
3349 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3350#endif
3351
3352#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3353 /**
3354 Every allocation will have its own memory block.
3355 Define to 1 for debugging purposes only.
3356 */
3357 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3358#endif
3359
3360#ifndef VMA_DEBUG_ALIGNMENT
3361 /**
3362 Minimum alignment of all allocations, in bytes.
3363 Set to more than 1 for debugging purposes only. Must be power of two.
3364 */
3365 #define VMA_DEBUG_ALIGNMENT (1)
3366#endif
3367
3368#ifndef VMA_DEBUG_MARGIN
3369 /**
3370 Minimum margin before and after every allocation, in bytes.
3371 Set nonzero for debugging purposes only.
3372 */
3373 #define VMA_DEBUG_MARGIN (0)
3374#endif
3375
3376#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3377 /**
3378 Define this macro to 1 to automatically fill new allocations and destroyed
3379 allocations with some bit pattern.
3380 */
3381 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3382#endif
3383
3384#ifndef VMA_DEBUG_DETECT_CORRUPTION
3385 /**
3386 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3387 enable writing magic value to the margin before and after every allocation and
3388 validating it, so that memory corruptions (out-of-bounds writes) are detected.
3389 */
3390 #define VMA_DEBUG_DETECT_CORRUPTION (0)
3391#endif
3392
3393#ifndef VMA_DEBUG_GLOBAL_MUTEX
3394 /**
3395 Set this to 1 for debugging purposes only, to enable single mutex protecting all
3396 entry calls to the library. Can be useful for debugging multithreading issues.
3397 */
3398 #define VMA_DEBUG_GLOBAL_MUTEX (0)
3399#endif
3400
3401#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3402 /**
3403 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3404 Set to more than 1 for debugging purposes only. Must be power of two.
3405 */
3406 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3407#endif
3408
3409#ifndef VMA_SMALL_HEAP_MAX_SIZE
3410 /// Maximum size of a memory heap in Vulkan to consider it "small".
3411 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3412#endif
3413
3414#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3415 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3416 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3417#endif
3418
3419#ifndef VMA_CLASS_NO_COPY
3420 #define VMA_CLASS_NO_COPY(className) \
3421 private: \
3422 className(const className&) = delete; \
3423 className& operator=(const className&) = delete;
3424#endif
3425
3426static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3427
3428// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3429static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3430
3431static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3432static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3433
3434/*******************************************************************************
3435END OF CONFIGURATION
3436*/
3437
Tony-LunarG390319b2019-03-18 15:54:16 -06003438#if defined(__GNUC__)
3439#pragma GCC diagnostic push
3440#pragma GCC diagnostic ignored "-Wtype-limits"
3441#pragma GCC diagnostic ignored "-Wunused-variable"
3442#if defined(ANDROID)
3443#pragma GCC diagnostic ignored "-Wunused-private-field"
3444#endif
3445#endif
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06003446static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3447
3448static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3449 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3450
3451// Returns number of bits set to 1 in (v).
3452static inline uint32_t VmaCountBitsSet(uint32_t v)
3453{
3454 uint32_t c = v - ((v >> 1) & 0x55555555);
3455 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3456 c = ((c >> 4) + c) & 0x0F0F0F0F;
3457 c = ((c >> 8) + c) & 0x00FF00FF;
3458 c = ((c >> 16) + c) & 0x0000FFFF;
3459 return c;
3460}
3461
3462// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3463// Use types like uint32_t, uint64_t as T.
3464template <typename T>
3465static inline T VmaAlignUp(T val, T align)
3466{
3467 return (val + align - 1) / align * align;
3468}
3469// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3470// Use types like uint32_t, uint64_t as T.
3471template <typename T>
3472static inline T VmaAlignDown(T val, T align)
3473{
3474 return val / align * align;
3475}
3476
3477// Division with mathematical rounding to nearest number.
3478template <typename T>
3479static inline T VmaRoundDiv(T x, T y)
3480{
3481 return (x + (y / (T)2)) / y;
3482}
3483
3484/*
3485Returns true if given number is a power of two.
3486T must be unsigned integer number or signed integer but always nonnegative.
3487For 0 returns true.
3488*/
3489template <typename T>
3490inline bool VmaIsPow2(T x)
3491{
3492 return (x & (x-1)) == 0;
3493}
3494
3495// Returns smallest power of 2 greater or equal to v.
3496static inline uint32_t VmaNextPow2(uint32_t v)
3497{
3498 v--;
3499 v |= v >> 1;
3500 v |= v >> 2;
3501 v |= v >> 4;
3502 v |= v >> 8;
3503 v |= v >> 16;
3504 v++;
3505 return v;
3506}
3507static inline uint64_t VmaNextPow2(uint64_t v)
3508{
3509 v--;
3510 v |= v >> 1;
3511 v |= v >> 2;
3512 v |= v >> 4;
3513 v |= v >> 8;
3514 v |= v >> 16;
3515 v |= v >> 32;
3516 v++;
3517 return v;
3518}
3519
3520// Returns largest power of 2 less or equal to v.
3521static inline uint32_t VmaPrevPow2(uint32_t v)
3522{
3523 v |= v >> 1;
3524 v |= v >> 2;
3525 v |= v >> 4;
3526 v |= v >> 8;
3527 v |= v >> 16;
3528 v = v ^ (v >> 1);
3529 return v;
3530}
3531static inline uint64_t VmaPrevPow2(uint64_t v)
3532{
3533 v |= v >> 1;
3534 v |= v >> 2;
3535 v |= v >> 4;
3536 v |= v >> 8;
3537 v |= v >> 16;
3538 v |= v >> 32;
3539 v = v ^ (v >> 1);
3540 return v;
3541}
3542
3543static inline bool VmaStrIsEmpty(const char* pStr)
3544{
3545 return pStr == VMA_NULL || *pStr == '\0';
3546}
3547
3548static const char* VmaAlgorithmToStr(uint32_t algorithm)
3549{
3550 switch(algorithm)
3551 {
3552 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3553 return "Linear";
3554 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3555 return "Buddy";
3556 case 0:
3557 return "Default";
3558 default:
3559 VMA_ASSERT(0);
3560 return "";
3561 }
3562}
3563
3564#ifndef VMA_SORT
3565
3566template<typename Iterator, typename Compare>
3567Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3568{
3569 Iterator centerValue = end; --centerValue;
3570 Iterator insertIndex = beg;
3571 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3572 {
3573 if(cmp(*memTypeIndex, *centerValue))
3574 {
3575 if(insertIndex != memTypeIndex)
3576 {
3577 VMA_SWAP(*memTypeIndex, *insertIndex);
3578 }
3579 ++insertIndex;
3580 }
3581 }
3582 if(insertIndex != centerValue)
3583 {
3584 VMA_SWAP(*insertIndex, *centerValue);
3585 }
3586 return insertIndex;
3587}
3588
3589template<typename Iterator, typename Compare>
3590void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3591{
3592 if(beg < end)
3593 {
3594 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3595 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3596 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3597 }
3598}
3599
3600#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3601
3602#endif // #ifndef VMA_SORT
3603
3604/*
3605Returns true if two memory blocks occupy overlapping pages.
3606ResourceA must be in less memory offset than ResourceB.
3607
3608Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3609chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3610*/
3611static inline bool VmaBlocksOnSamePage(
3612 VkDeviceSize resourceAOffset,
3613 VkDeviceSize resourceASize,
3614 VkDeviceSize resourceBOffset,
3615 VkDeviceSize pageSize)
3616{
3617 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3618 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3619 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3620 VkDeviceSize resourceBStart = resourceBOffset;
3621 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3622 return resourceAEndPage == resourceBStartPage;
3623}
3624
3625enum VmaSuballocationType
3626{
3627 VMA_SUBALLOCATION_TYPE_FREE = 0,
3628 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3629 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3630 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3631 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3632 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3633 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3634};
3635
3636/*
3637Returns true if given suballocation types could conflict and must respect
3638VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3639or linear image and another one is optimal image. If type is unknown, behave
3640conservatively.
3641*/
3642static inline bool VmaIsBufferImageGranularityConflict(
3643 VmaSuballocationType suballocType1,
3644 VmaSuballocationType suballocType2)
3645{
3646 if(suballocType1 > suballocType2)
3647 {
3648 VMA_SWAP(suballocType1, suballocType2);
3649 }
3650
3651 switch(suballocType1)
3652 {
3653 case VMA_SUBALLOCATION_TYPE_FREE:
3654 return false;
3655 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3656 return true;
3657 case VMA_SUBALLOCATION_TYPE_BUFFER:
3658 return
3659 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3660 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3661 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3662 return
3663 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3664 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3665 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3666 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3667 return
3668 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3669 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3670 return false;
3671 default:
3672 VMA_ASSERT(0);
3673 return true;
3674 }
3675}
3676
3677static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3678{
3679 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3680 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3681 for(size_t i = 0; i < numberCount; ++i, ++pDst)
3682 {
3683 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3684 }
3685}
3686
3687static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3688{
3689 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3690 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3691 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3692 {
3693 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3694 {
3695 return false;
3696 }
3697 }
3698 return true;
3699}
3700
3701// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3702struct VmaMutexLock
3703{
3704 VMA_CLASS_NO_COPY(VmaMutexLock)
3705public:
3706 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3707 m_pMutex(useMutex ? &mutex : VMA_NULL)
3708 { if(m_pMutex) { m_pMutex->Lock(); } }
3709 ~VmaMutexLock()
3710 { if(m_pMutex) { m_pMutex->Unlock(); } }
3711private:
3712 VMA_MUTEX* m_pMutex;
3713};
3714
3715// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3716struct VmaMutexLockRead
3717{
3718 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3719public:
3720 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3721 m_pMutex(useMutex ? &mutex : VMA_NULL)
3722 { if(m_pMutex) { m_pMutex->LockRead(); } }
3723 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3724private:
3725 VMA_RW_MUTEX* m_pMutex;
3726};
3727
3728// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3729struct VmaMutexLockWrite
3730{
3731 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3732public:
3733 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3734 m_pMutex(useMutex ? &mutex : VMA_NULL)
3735 { if(m_pMutex) { m_pMutex->LockWrite(); } }
3736 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3737private:
3738 VMA_RW_MUTEX* m_pMutex;
3739};
3740
3741#if VMA_DEBUG_GLOBAL_MUTEX
3742 static VMA_MUTEX gDebugGlobalMutex;
3743 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3744#else
3745 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3746#endif
3747
3748// Minimum size of a free suballocation to register it in the free suballocation collection.
3749static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3750
3751/*
3752Performs binary search and returns iterator to first element that is greater or
3753equal to (key), according to comparison (cmp).
3754
3755Cmp should return true if first argument is less than second argument.
3756
3757Returned value is the found element, if present in the collection or place where
3758new element with value (key) should be inserted.
3759*/
3760template <typename CmpLess, typename IterT, typename KeyT>
3761static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3762{
3763 size_t down = 0, up = (end - beg);
3764 while(down < up)
3765 {
3766 const size_t mid = (down + up) / 2;
3767 if(cmp(*(beg+mid), key))
3768 {
3769 down = mid + 1;
3770 }
3771 else
3772 {
3773 up = mid;
3774 }
3775 }
3776 return beg + down;
3777}
3778
3779/*
3780Returns true if all pointers in the array are not-null and unique.
3781Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3782T must be pointer type, e.g. VmaAllocation, VmaPool.
3783*/
3784template<typename T>
3785static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3786{
3787 for(uint32_t i = 0; i < count; ++i)
3788 {
3789 const T iPtr = arr[i];
3790 if(iPtr == VMA_NULL)
3791 {
3792 return false;
3793 }
3794 for(uint32_t j = i + 1; j < count; ++j)
3795 {
3796 if(iPtr == arr[j])
3797 {
3798 return false;
3799 }
3800 }
3801 }
3802 return true;
3803}
3804
3805////////////////////////////////////////////////////////////////////////////////
3806// Memory allocation
3807
3808static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3809{
3810 if((pAllocationCallbacks != VMA_NULL) &&
3811 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3812 {
3813 return (*pAllocationCallbacks->pfnAllocation)(
3814 pAllocationCallbacks->pUserData,
3815 size,
3816 alignment,
3817 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3818 }
3819 else
3820 {
3821 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3822 }
3823}
3824
3825static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3826{
3827 if((pAllocationCallbacks != VMA_NULL) &&
3828 (pAllocationCallbacks->pfnFree != VMA_NULL))
3829 {
3830 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3831 }
3832 else
3833 {
3834 VMA_SYSTEM_FREE(ptr);
3835 }
3836}
3837
3838template<typename T>
3839static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3840{
3841 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3842}
3843
3844template<typename T>
3845static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3846{
3847 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3848}
3849
3850#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3851
3852#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3853
3854template<typename T>
3855static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3856{
3857 ptr->~T();
3858 VmaFree(pAllocationCallbacks, ptr);
3859}
3860
3861template<typename T>
3862static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3863{
3864 if(ptr != VMA_NULL)
3865 {
3866 for(size_t i = count; i--; )
3867 {
3868 ptr[i].~T();
3869 }
3870 VmaFree(pAllocationCallbacks, ptr);
3871 }
3872}
3873
3874// STL-compatible allocator.
3875template<typename T>
3876class VmaStlAllocator
3877{
3878public:
3879 const VkAllocationCallbacks* const m_pCallbacks;
3880 typedef T value_type;
3881
3882 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
3883 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3884
3885 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
3886 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3887
3888 template<typename U>
3889 bool operator==(const VmaStlAllocator<U>& rhs) const
3890 {
3891 return m_pCallbacks == rhs.m_pCallbacks;
3892 }
3893 template<typename U>
3894 bool operator!=(const VmaStlAllocator<U>& rhs) const
3895 {
3896 return m_pCallbacks != rhs.m_pCallbacks;
3897 }
3898
3899 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3900};
3901
3902#if VMA_USE_STL_VECTOR
3903
3904#define VmaVector std::vector
3905
3906template<typename T, typename allocatorT>
3907static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3908{
3909 vec.insert(vec.begin() + index, item);
3910}
3911
3912template<typename T, typename allocatorT>
3913static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3914{
3915 vec.erase(vec.begin() + index);
3916}
3917
3918#else // #if VMA_USE_STL_VECTOR
3919
3920/* Class with interface compatible with subset of std::vector.
3921T must be POD because constructors and destructors are not called and memcpy is
3922used for these objects. */
3923template<typename T, typename AllocatorT>
3924class VmaVector
3925{
3926public:
3927 typedef T value_type;
3928
3929 VmaVector(const AllocatorT& allocator) :
3930 m_Allocator(allocator),
3931 m_pArray(VMA_NULL),
3932 m_Count(0),
3933 m_Capacity(0)
3934 {
3935 }
3936
3937 VmaVector(size_t count, const AllocatorT& allocator) :
3938 m_Allocator(allocator),
3939 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3940 m_Count(count),
3941 m_Capacity(count)
3942 {
3943 }
3944
3945 VmaVector(const VmaVector<T, AllocatorT>& src) :
3946 m_Allocator(src.m_Allocator),
3947 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3948 m_Count(src.m_Count),
3949 m_Capacity(src.m_Count)
3950 {
3951 if(m_Count != 0)
3952 {
3953 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3954 }
3955 }
3956
3957 ~VmaVector()
3958 {
3959 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3960 }
3961
3962 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3963 {
3964 if(&rhs != this)
3965 {
3966 resize(rhs.m_Count);
3967 if(m_Count != 0)
3968 {
3969 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3970 }
3971 }
3972 return *this;
3973 }
3974
3975 bool empty() const { return m_Count == 0; }
3976 size_t size() const { return m_Count; }
3977 T* data() { return m_pArray; }
3978 const T* data() const { return m_pArray; }
3979
3980 T& operator[](size_t index)
3981 {
3982 VMA_HEAVY_ASSERT(index < m_Count);
3983 return m_pArray[index];
3984 }
3985 const T& operator[](size_t index) const
3986 {
3987 VMA_HEAVY_ASSERT(index < m_Count);
3988 return m_pArray[index];
3989 }
3990
3991 T& front()
3992 {
3993 VMA_HEAVY_ASSERT(m_Count > 0);
3994 return m_pArray[0];
3995 }
3996 const T& front() const
3997 {
3998 VMA_HEAVY_ASSERT(m_Count > 0);
3999 return m_pArray[0];
4000 }
4001 T& back()
4002 {
4003 VMA_HEAVY_ASSERT(m_Count > 0);
4004 return m_pArray[m_Count - 1];
4005 }
4006 const T& back() const
4007 {
4008 VMA_HEAVY_ASSERT(m_Count > 0);
4009 return m_pArray[m_Count - 1];
4010 }
4011
4012 void reserve(size_t newCapacity, bool freeMemory = false)
4013 {
4014 newCapacity = VMA_MAX(newCapacity, m_Count);
4015
4016 if((newCapacity < m_Capacity) && !freeMemory)
4017 {
4018 newCapacity = m_Capacity;
4019 }
4020
4021 if(newCapacity != m_Capacity)
4022 {
4023 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4024 if(m_Count != 0)
4025 {
4026 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4027 }
4028 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4029 m_Capacity = newCapacity;
4030 m_pArray = newArray;
4031 }
4032 }
4033
4034 void resize(size_t newCount, bool freeMemory = false)
4035 {
4036 size_t newCapacity = m_Capacity;
4037 if(newCount > m_Capacity)
4038 {
4039 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4040 }
4041 else if(freeMemory)
4042 {
4043 newCapacity = newCount;
4044 }
4045
4046 if(newCapacity != m_Capacity)
4047 {
4048 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4049 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4050 if(elementsToCopy != 0)
4051 {
4052 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4053 }
4054 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4055 m_Capacity = newCapacity;
4056 m_pArray = newArray;
4057 }
4058
4059 m_Count = newCount;
4060 }
4061
4062 void clear(bool freeMemory = false)
4063 {
4064 resize(0, freeMemory);
4065 }
4066
4067 void insert(size_t index, const T& src)
4068 {
4069 VMA_HEAVY_ASSERT(index <= m_Count);
4070 const size_t oldCount = size();
4071 resize(oldCount + 1);
4072 if(index < oldCount)
4073 {
4074 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4075 }
4076 m_pArray[index] = src;
4077 }
4078
4079 void remove(size_t index)
4080 {
4081 VMA_HEAVY_ASSERT(index < m_Count);
4082 const size_t oldCount = size();
4083 if(index < oldCount - 1)
4084 {
4085 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4086 }
4087 resize(oldCount - 1);
4088 }
4089
4090 void push_back(const T& src)
4091 {
4092 const size_t newIndex = size();
4093 resize(newIndex + 1);
4094 m_pArray[newIndex] = src;
4095 }
4096
4097 void pop_back()
4098 {
4099 VMA_HEAVY_ASSERT(m_Count > 0);
4100 resize(size() - 1);
4101 }
4102
4103 void push_front(const T& src)
4104 {
4105 insert(0, src);
4106 }
4107
4108 void pop_front()
4109 {
4110 VMA_HEAVY_ASSERT(m_Count > 0);
4111 remove(0);
4112 }
4113
4114 typedef T* iterator;
4115
4116 iterator begin() { return m_pArray; }
4117 iterator end() { return m_pArray + m_Count; }
4118
4119private:
4120 AllocatorT m_Allocator;
4121 T* m_pArray;
4122 size_t m_Count;
4123 size_t m_Capacity;
4124};
4125
4126template<typename T, typename allocatorT>
4127static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4128{
4129 vec.insert(index, item);
4130}
4131
4132template<typename T, typename allocatorT>
4133static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4134{
4135 vec.remove(index);
4136}
4137
4138#endif // #if VMA_USE_STL_VECTOR
4139
4140template<typename CmpLess, typename VectorT>
4141size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4142{
4143 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4144 vector.data(),
4145 vector.data() + vector.size(),
4146 value,
4147 CmpLess()) - vector.data();
4148 VmaVectorInsert(vector, indexToInsert, value);
4149 return indexToInsert;
4150}
4151
4152template<typename CmpLess, typename VectorT>
4153bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4154{
4155 CmpLess comparator;
4156 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4157 vector.begin(),
4158 vector.end(),
4159 value,
4160 comparator);
4161 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4162 {
4163 size_t indexToRemove = it - vector.begin();
4164 VmaVectorRemove(vector, indexToRemove);
4165 return true;
4166 }
4167 return false;
4168}
4169
4170template<typename CmpLess, typename IterT, typename KeyT>
4171IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4172{
4173 CmpLess comparator;
4174 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4175 beg, end, value, comparator);
4176 if(it == end ||
4177 (!comparator(*it, value) && !comparator(value, *it)))
4178 {
4179 return it;
4180 }
4181 return end;
4182}
4183
4184////////////////////////////////////////////////////////////////////////////////
4185// class VmaPoolAllocator
4186
4187/*
4188Allocator for objects of type T using a list of arrays (pools) to speed up
4189allocation. Number of elements that can be allocated is not bounded because
4190allocator can create multiple blocks.
4191*/
4192template<typename T>
4193class VmaPoolAllocator
4194{
4195 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4196public:
4197 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4198 ~VmaPoolAllocator();
4199 void Clear();
4200 T* Alloc();
4201 void Free(T* ptr);
4202
4203private:
4204 union Item
4205 {
4206 uint32_t NextFreeIndex;
4207 T Value;
4208 };
4209
4210 struct ItemBlock
4211 {
4212 Item* pItems;
4213 uint32_t FirstFreeIndex;
4214 };
4215
4216 const VkAllocationCallbacks* m_pAllocationCallbacks;
4217 size_t m_ItemsPerBlock;
4218 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4219
4220 ItemBlock& CreateNewBlock();
4221};
4222
4223template<typename T>
4224VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4225 m_pAllocationCallbacks(pAllocationCallbacks),
4226 m_ItemsPerBlock(itemsPerBlock),
4227 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4228{
4229 VMA_ASSERT(itemsPerBlock > 0);
4230}
4231
4232template<typename T>
4233VmaPoolAllocator<T>::~VmaPoolAllocator()
4234{
4235 Clear();
4236}
4237
4238template<typename T>
4239void VmaPoolAllocator<T>::Clear()
4240{
4241 for(size_t i = m_ItemBlocks.size(); i--; )
4242 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4243 m_ItemBlocks.clear();
4244}
4245
4246template<typename T>
4247T* VmaPoolAllocator<T>::Alloc()
4248{
4249 for(size_t i = m_ItemBlocks.size(); i--; )
4250 {
4251 ItemBlock& block = m_ItemBlocks[i];
4252 // This block has some free items: Use first one.
4253 if(block.FirstFreeIndex != UINT32_MAX)
4254 {
4255 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4256 block.FirstFreeIndex = pItem->NextFreeIndex;
4257 return &pItem->Value;
4258 }
4259 }
4260
4261 // No block has free item: Create new one and use it.
4262 ItemBlock& newBlock = CreateNewBlock();
4263 Item* const pItem = &newBlock.pItems[0];
4264 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4265 return &pItem->Value;
4266}
4267
4268template<typename T>
4269void VmaPoolAllocator<T>::Free(T* ptr)
4270{
4271 // Search all memory blocks to find ptr.
4272 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4273 {
4274 ItemBlock& block = m_ItemBlocks[i];
4275
4276 // Casting to union.
4277 Item* pItemPtr;
4278 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4279
4280 // Check if pItemPtr is in address range of this block.
4281 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4282 {
4283 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4284 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4285 block.FirstFreeIndex = index;
4286 return;
4287 }
4288 }
4289 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4290}
4291
4292template<typename T>
4293typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4294{
4295 ItemBlock newBlock = {
4296 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4297
4298 m_ItemBlocks.push_back(newBlock);
4299
4300 // Setup singly-linked list of all free items in this block.
4301 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4302 newBlock.pItems[i].NextFreeIndex = i + 1;
4303 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4304 return m_ItemBlocks.back();
4305}
4306
4307////////////////////////////////////////////////////////////////////////////////
4308// class VmaRawList, VmaList
4309
4310#if VMA_USE_STL_LIST
4311
4312#define VmaList std::list
4313
4314#else // #if VMA_USE_STL_LIST
4315
4316template<typename T>
4317struct VmaListItem
4318{
4319 VmaListItem* pPrev;
4320 VmaListItem* pNext;
4321 T Value;
4322};
4323
4324// Doubly linked list.
4325template<typename T>
4326class VmaRawList
4327{
4328 VMA_CLASS_NO_COPY(VmaRawList)
4329public:
4330 typedef VmaListItem<T> ItemType;
4331
4332 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4333 ~VmaRawList();
4334 void Clear();
4335
4336 size_t GetCount() const { return m_Count; }
4337 bool IsEmpty() const { return m_Count == 0; }
4338
4339 ItemType* Front() { return m_pFront; }
4340 const ItemType* Front() const { return m_pFront; }
4341 ItemType* Back() { return m_pBack; }
4342 const ItemType* Back() const { return m_pBack; }
4343
4344 ItemType* PushBack();
4345 ItemType* PushFront();
4346 ItemType* PushBack(const T& value);
4347 ItemType* PushFront(const T& value);
4348 void PopBack();
4349 void PopFront();
4350
4351 // Item can be null - it means PushBack.
4352 ItemType* InsertBefore(ItemType* pItem);
4353 // Item can be null - it means PushFront.
4354 ItemType* InsertAfter(ItemType* pItem);
4355
4356 ItemType* InsertBefore(ItemType* pItem, const T& value);
4357 ItemType* InsertAfter(ItemType* pItem, const T& value);
4358
4359 void Remove(ItemType* pItem);
4360
4361private:
4362 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4363 VmaPoolAllocator<ItemType> m_ItemAllocator;
4364 ItemType* m_pFront;
4365 ItemType* m_pBack;
4366 size_t m_Count;
4367};
4368
4369template<typename T>
4370VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4371 m_pAllocationCallbacks(pAllocationCallbacks),
4372 m_ItemAllocator(pAllocationCallbacks, 128),
4373 m_pFront(VMA_NULL),
4374 m_pBack(VMA_NULL),
4375 m_Count(0)
4376{
4377}
4378
4379template<typename T>
4380VmaRawList<T>::~VmaRawList()
4381{
4382 // Intentionally not calling Clear, because that would be unnecessary
4383 // computations to return all items to m_ItemAllocator as free.
4384}
4385
4386template<typename T>
4387void VmaRawList<T>::Clear()
4388{
4389 if(IsEmpty() == false)
4390 {
4391 ItemType* pItem = m_pBack;
4392 while(pItem != VMA_NULL)
4393 {
4394 ItemType* const pPrevItem = pItem->pPrev;
4395 m_ItemAllocator.Free(pItem);
4396 pItem = pPrevItem;
4397 }
4398 m_pFront = VMA_NULL;
4399 m_pBack = VMA_NULL;
4400 m_Count = 0;
4401 }
4402}
4403
4404template<typename T>
4405VmaListItem<T>* VmaRawList<T>::PushBack()
4406{
4407 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4408 pNewItem->pNext = VMA_NULL;
4409 if(IsEmpty())
4410 {
4411 pNewItem->pPrev = VMA_NULL;
4412 m_pFront = pNewItem;
4413 m_pBack = pNewItem;
4414 m_Count = 1;
4415 }
4416 else
4417 {
4418 pNewItem->pPrev = m_pBack;
4419 m_pBack->pNext = pNewItem;
4420 m_pBack = pNewItem;
4421 ++m_Count;
4422 }
4423 return pNewItem;
4424}
4425
4426template<typename T>
4427VmaListItem<T>* VmaRawList<T>::PushFront()
4428{
4429 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4430 pNewItem->pPrev = VMA_NULL;
4431 if(IsEmpty())
4432 {
4433 pNewItem->pNext = VMA_NULL;
4434 m_pFront = pNewItem;
4435 m_pBack = pNewItem;
4436 m_Count = 1;
4437 }
4438 else
4439 {
4440 pNewItem->pNext = m_pFront;
4441 m_pFront->pPrev = pNewItem;
4442 m_pFront = pNewItem;
4443 ++m_Count;
4444 }
4445 return pNewItem;
4446}
4447
4448template<typename T>
4449VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4450{
4451 ItemType* const pNewItem = PushBack();
4452 pNewItem->Value = value;
4453 return pNewItem;
4454}
4455
4456template<typename T>
4457VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4458{
4459 ItemType* const pNewItem = PushFront();
4460 pNewItem->Value = value;
4461 return pNewItem;
4462}
4463
4464template<typename T>
4465void VmaRawList<T>::PopBack()
4466{
4467 VMA_HEAVY_ASSERT(m_Count > 0);
4468 ItemType* const pBackItem = m_pBack;
4469 ItemType* const pPrevItem = pBackItem->pPrev;
4470 if(pPrevItem != VMA_NULL)
4471 {
4472 pPrevItem->pNext = VMA_NULL;
4473 }
4474 m_pBack = pPrevItem;
4475 m_ItemAllocator.Free(pBackItem);
4476 --m_Count;
4477}
4478
4479template<typename T>
4480void VmaRawList<T>::PopFront()
4481{
4482 VMA_HEAVY_ASSERT(m_Count > 0);
4483 ItemType* const pFrontItem = m_pFront;
4484 ItemType* const pNextItem = pFrontItem->pNext;
4485 if(pNextItem != VMA_NULL)
4486 {
4487 pNextItem->pPrev = VMA_NULL;
4488 }
4489 m_pFront = pNextItem;
4490 m_ItemAllocator.Free(pFrontItem);
4491 --m_Count;
4492}
4493
4494template<typename T>
4495void VmaRawList<T>::Remove(ItemType* pItem)
4496{
4497 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4498 VMA_HEAVY_ASSERT(m_Count > 0);
4499
4500 if(pItem->pPrev != VMA_NULL)
4501 {
4502 pItem->pPrev->pNext = pItem->pNext;
4503 }
4504 else
4505 {
4506 VMA_HEAVY_ASSERT(m_pFront == pItem);
4507 m_pFront = pItem->pNext;
4508 }
4509
4510 if(pItem->pNext != VMA_NULL)
4511 {
4512 pItem->pNext->pPrev = pItem->pPrev;
4513 }
4514 else
4515 {
4516 VMA_HEAVY_ASSERT(m_pBack == pItem);
4517 m_pBack = pItem->pPrev;
4518 }
4519
4520 m_ItemAllocator.Free(pItem);
4521 --m_Count;
4522}
4523
4524template<typename T>
4525VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4526{
4527 if(pItem != VMA_NULL)
4528 {
4529 ItemType* const prevItem = pItem->pPrev;
4530 ItemType* const newItem = m_ItemAllocator.Alloc();
4531 newItem->pPrev = prevItem;
4532 newItem->pNext = pItem;
4533 pItem->pPrev = newItem;
4534 if(prevItem != VMA_NULL)
4535 {
4536 prevItem->pNext = newItem;
4537 }
4538 else
4539 {
4540 VMA_HEAVY_ASSERT(m_pFront == pItem);
4541 m_pFront = newItem;
4542 }
4543 ++m_Count;
4544 return newItem;
4545 }
4546 else
4547 return PushBack();
4548}
4549
4550template<typename T>
4551VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4552{
4553 if(pItem != VMA_NULL)
4554 {
4555 ItemType* const nextItem = pItem->pNext;
4556 ItemType* const newItem = m_ItemAllocator.Alloc();
4557 newItem->pNext = nextItem;
4558 newItem->pPrev = pItem;
4559 pItem->pNext = newItem;
4560 if(nextItem != VMA_NULL)
4561 {
4562 nextItem->pPrev = newItem;
4563 }
4564 else
4565 {
4566 VMA_HEAVY_ASSERT(m_pBack == pItem);
4567 m_pBack = newItem;
4568 }
4569 ++m_Count;
4570 return newItem;
4571 }
4572 else
4573 return PushFront();
4574}
4575
4576template<typename T>
4577VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4578{
4579 ItemType* const newItem = InsertBefore(pItem);
4580 newItem->Value = value;
4581 return newItem;
4582}
4583
4584template<typename T>
4585VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4586{
4587 ItemType* const newItem = InsertAfter(pItem);
4588 newItem->Value = value;
4589 return newItem;
4590}
4591
4592template<typename T, typename AllocatorT>
4593class VmaList
4594{
4595 VMA_CLASS_NO_COPY(VmaList)
4596public:
4597 class iterator
4598 {
4599 public:
4600 iterator() :
4601 m_pList(VMA_NULL),
4602 m_pItem(VMA_NULL)
4603 {
4604 }
4605
4606 T& operator*() const
4607 {
4608 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4609 return m_pItem->Value;
4610 }
4611 T* operator->() const
4612 {
4613 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4614 return &m_pItem->Value;
4615 }
4616
4617 iterator& operator++()
4618 {
4619 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4620 m_pItem = m_pItem->pNext;
4621 return *this;
4622 }
4623 iterator& operator--()
4624 {
4625 if(m_pItem != VMA_NULL)
4626 {
4627 m_pItem = m_pItem->pPrev;
4628 }
4629 else
4630 {
4631 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4632 m_pItem = m_pList->Back();
4633 }
4634 return *this;
4635 }
4636
4637 iterator operator++(int)
4638 {
4639 iterator result = *this;
4640 ++*this;
4641 return result;
4642 }
4643 iterator operator--(int)
4644 {
4645 iterator result = *this;
4646 --*this;
4647 return result;
4648 }
4649
4650 bool operator==(const iterator& rhs) const
4651 {
4652 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4653 return m_pItem == rhs.m_pItem;
4654 }
4655 bool operator!=(const iterator& rhs) const
4656 {
4657 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4658 return m_pItem != rhs.m_pItem;
4659 }
4660
4661 private:
4662 VmaRawList<T>* m_pList;
4663 VmaListItem<T>* m_pItem;
4664
4665 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4666 m_pList(pList),
4667 m_pItem(pItem)
4668 {
4669 }
4670
4671 friend class VmaList<T, AllocatorT>;
4672 };
4673
4674 class const_iterator
4675 {
4676 public:
4677 const_iterator() :
4678 m_pList(VMA_NULL),
4679 m_pItem(VMA_NULL)
4680 {
4681 }
4682
4683 const_iterator(const iterator& src) :
4684 m_pList(src.m_pList),
4685 m_pItem(src.m_pItem)
4686 {
4687 }
4688
4689 const T& operator*() const
4690 {
4691 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4692 return m_pItem->Value;
4693 }
4694 const T* operator->() const
4695 {
4696 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4697 return &m_pItem->Value;
4698 }
4699
4700 const_iterator& operator++()
4701 {
4702 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4703 m_pItem = m_pItem->pNext;
4704 return *this;
4705 }
4706 const_iterator& operator--()
4707 {
4708 if(m_pItem != VMA_NULL)
4709 {
4710 m_pItem = m_pItem->pPrev;
4711 }
4712 else
4713 {
4714 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4715 m_pItem = m_pList->Back();
4716 }
4717 return *this;
4718 }
4719
4720 const_iterator operator++(int)
4721 {
4722 const_iterator result = *this;
4723 ++*this;
4724 return result;
4725 }
4726 const_iterator operator--(int)
4727 {
4728 const_iterator result = *this;
4729 --*this;
4730 return result;
4731 }
4732
4733 bool operator==(const const_iterator& rhs) const
4734 {
4735 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4736 return m_pItem == rhs.m_pItem;
4737 }
4738 bool operator!=(const const_iterator& rhs) const
4739 {
4740 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4741 return m_pItem != rhs.m_pItem;
4742 }
4743
4744 private:
4745 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4746 m_pList(pList),
4747 m_pItem(pItem)
4748 {
4749 }
4750
4751 const VmaRawList<T>* m_pList;
4752 const VmaListItem<T>* m_pItem;
4753
4754 friend class VmaList<T, AllocatorT>;
4755 };
4756
4757 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4758
4759 bool empty() const { return m_RawList.IsEmpty(); }
4760 size_t size() const { return m_RawList.GetCount(); }
4761
4762 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
4763 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4764
4765 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
4766 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4767
4768 void clear() { m_RawList.Clear(); }
4769 void push_back(const T& value) { m_RawList.PushBack(value); }
4770 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4771 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4772
4773private:
4774 VmaRawList<T> m_RawList;
4775};
4776
4777#endif // #if VMA_USE_STL_LIST
4778
4779////////////////////////////////////////////////////////////////////////////////
4780// class VmaMap
4781
4782// Unused in this version.
4783#if 0
4784
4785#if VMA_USE_STL_UNORDERED_MAP
4786
4787#define VmaPair std::pair
4788
4789#define VMA_MAP_TYPE(KeyT, ValueT) \
4790 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4791
4792#else // #if VMA_USE_STL_UNORDERED_MAP
4793
4794template<typename T1, typename T2>
4795struct VmaPair
4796{
4797 T1 first;
4798 T2 second;
4799
4800 VmaPair() : first(), second() { }
4801 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4802};
4803
4804/* Class compatible with subset of interface of std::unordered_map.
4805KeyT, ValueT must be POD because they will be stored in VmaVector.
4806*/
4807template<typename KeyT, typename ValueT>
4808class VmaMap
4809{
4810public:
4811 typedef VmaPair<KeyT, ValueT> PairType;
4812 typedef PairType* iterator;
4813
4814 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4815
4816 iterator begin() { return m_Vector.begin(); }
4817 iterator end() { return m_Vector.end(); }
4818
4819 void insert(const PairType& pair);
4820 iterator find(const KeyT& key);
4821 void erase(iterator it);
4822
4823private:
4824 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4825};
4826
4827#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4828
4829template<typename FirstT, typename SecondT>
4830struct VmaPairFirstLess
4831{
4832 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4833 {
4834 return lhs.first < rhs.first;
4835 }
4836 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4837 {
4838 return lhs.first < rhsFirst;
4839 }
4840};
4841
4842template<typename KeyT, typename ValueT>
4843void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4844{
4845 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4846 m_Vector.data(),
4847 m_Vector.data() + m_Vector.size(),
4848 pair,
4849 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4850 VmaVectorInsert(m_Vector, indexToInsert, pair);
4851}
4852
4853template<typename KeyT, typename ValueT>
4854VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4855{
4856 PairType* it = VmaBinaryFindFirstNotLess(
4857 m_Vector.data(),
4858 m_Vector.data() + m_Vector.size(),
4859 key,
4860 VmaPairFirstLess<KeyT, ValueT>());
4861 if((it != m_Vector.end()) && (it->first == key))
4862 {
4863 return it;
4864 }
4865 else
4866 {
4867 return m_Vector.end();
4868 }
4869}
4870
4871template<typename KeyT, typename ValueT>
4872void VmaMap<KeyT, ValueT>::erase(iterator it)
4873{
4874 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4875}
4876
4877#endif // #if VMA_USE_STL_UNORDERED_MAP
4878
4879#endif // #if 0
4880
4881////////////////////////////////////////////////////////////////////////////////
4882
4883class VmaDeviceMemoryBlock;
4884
4885enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4886
4887struct VmaAllocation_T
4888{
4889 VMA_CLASS_NO_COPY(VmaAllocation_T)
4890private:
4891 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4892
4893 enum FLAGS
4894 {
4895 FLAG_USER_DATA_STRING = 0x01,
4896 };
4897
4898public:
4899 enum ALLOCATION_TYPE
4900 {
4901 ALLOCATION_TYPE_NONE,
4902 ALLOCATION_TYPE_BLOCK,
4903 ALLOCATION_TYPE_DEDICATED,
4904 };
4905
4906 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4907 m_Alignment(1),
4908 m_Size(0),
4909 m_pUserData(VMA_NULL),
4910 m_LastUseFrameIndex(currentFrameIndex),
4911 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4912 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4913 m_MapCount(0),
4914 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4915 {
4916#if VMA_STATS_STRING_ENABLED
4917 m_CreationFrameIndex = currentFrameIndex;
4918 m_BufferImageUsage = 0;
4919#endif
4920 }
4921
4922 ~VmaAllocation_T()
4923 {
4924 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4925
4926 // Check if owned string was freed.
4927 VMA_ASSERT(m_pUserData == VMA_NULL);
4928 }
4929
4930 void InitBlockAllocation(
4931 VmaPool hPool,
4932 VmaDeviceMemoryBlock* block,
4933 VkDeviceSize offset,
4934 VkDeviceSize alignment,
4935 VkDeviceSize size,
4936 VmaSuballocationType suballocationType,
4937 bool mapped,
4938 bool canBecomeLost)
4939 {
4940 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4941 VMA_ASSERT(block != VMA_NULL);
4942 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4943 m_Alignment = alignment;
4944 m_Size = size;
4945 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4946 m_SuballocationType = (uint8_t)suballocationType;
4947 m_BlockAllocation.m_hPool = hPool;
4948 m_BlockAllocation.m_Block = block;
4949 m_BlockAllocation.m_Offset = offset;
4950 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4951 }
4952
4953 void InitLost()
4954 {
4955 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4956 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4957 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4958 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4959 m_BlockAllocation.m_Block = VMA_NULL;
4960 m_BlockAllocation.m_Offset = 0;
4961 m_BlockAllocation.m_CanBecomeLost = true;
4962 }
4963
4964 void ChangeBlockAllocation(
4965 VmaAllocator hAllocator,
4966 VmaDeviceMemoryBlock* block,
4967 VkDeviceSize offset);
4968
4969 void ChangeSize(VkDeviceSize newSize);
4970 void ChangeOffset(VkDeviceSize newOffset);
4971
4972 // pMappedData not null means allocation is created with MAPPED flag.
4973 void InitDedicatedAllocation(
4974 uint32_t memoryTypeIndex,
4975 VkDeviceMemory hMemory,
4976 VmaSuballocationType suballocationType,
4977 void* pMappedData,
4978 VkDeviceSize size)
4979 {
4980 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4981 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4982 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4983 m_Alignment = 0;
4984 m_Size = size;
4985 m_SuballocationType = (uint8_t)suballocationType;
4986 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4987 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
4988 m_DedicatedAllocation.m_hMemory = hMemory;
4989 m_DedicatedAllocation.m_pMappedData = pMappedData;
4990 }
4991
4992 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
4993 VkDeviceSize GetAlignment() const { return m_Alignment; }
4994 VkDeviceSize GetSize() const { return m_Size; }
4995 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
4996 void* GetUserData() const { return m_pUserData; }
4997 void SetUserData(VmaAllocator hAllocator, void* pUserData);
4998 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
4999
5000 VmaDeviceMemoryBlock* GetBlock() const
5001 {
5002 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5003 return m_BlockAllocation.m_Block;
5004 }
5005 VkDeviceSize GetOffset() const;
5006 VkDeviceMemory GetMemory() const;
5007 uint32_t GetMemoryTypeIndex() const;
5008 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5009 void* GetMappedData() const;
5010 bool CanBecomeLost() const;
5011 VmaPool GetPool() const;
5012
5013 uint32_t GetLastUseFrameIndex() const
5014 {
5015 return m_LastUseFrameIndex.load();
5016 }
5017 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5018 {
5019 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5020 }
5021 /*
5022 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5023 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5024 - Else, returns false.
5025
5026 If hAllocation is already lost, assert - you should not call it then.
5027 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5028 */
5029 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5030
5031 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5032 {
5033 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5034 outInfo.blockCount = 1;
5035 outInfo.allocationCount = 1;
5036 outInfo.unusedRangeCount = 0;
5037 outInfo.usedBytes = m_Size;
5038 outInfo.unusedBytes = 0;
5039 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5040 outInfo.unusedRangeSizeMin = UINT64_MAX;
5041 outInfo.unusedRangeSizeMax = 0;
5042 }
5043
5044 void BlockAllocMap();
5045 void BlockAllocUnmap();
5046 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5047 void DedicatedAllocUnmap(VmaAllocator hAllocator);
5048
5049#if VMA_STATS_STRING_ENABLED
5050 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
5051 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5052
5053 void InitBufferImageUsage(uint32_t bufferImageUsage)
5054 {
5055 VMA_ASSERT(m_BufferImageUsage == 0);
5056 m_BufferImageUsage = bufferImageUsage;
5057 }
5058
5059 void PrintParameters(class VmaJsonWriter& json) const;
5060#endif
5061
5062private:
5063 VkDeviceSize m_Alignment;
5064 VkDeviceSize m_Size;
5065 void* m_pUserData;
5066 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5067 uint8_t m_Type; // ALLOCATION_TYPE
5068 uint8_t m_SuballocationType; // VmaSuballocationType
5069 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5070 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5071 uint8_t m_MapCount;
5072 uint8_t m_Flags; // enum FLAGS
5073
5074 // Allocation out of VmaDeviceMemoryBlock.
5075 struct BlockAllocation
5076 {
5077 VmaPool m_hPool; // Null if belongs to general memory.
5078 VmaDeviceMemoryBlock* m_Block;
5079 VkDeviceSize m_Offset;
5080 bool m_CanBecomeLost;
5081 };
5082
5083 // Allocation for an object that has its own private VkDeviceMemory.
5084 struct DedicatedAllocation
5085 {
5086 uint32_t m_MemoryTypeIndex;
5087 VkDeviceMemory m_hMemory;
5088 void* m_pMappedData; // Not null means memory is mapped.
5089 };
5090
5091 union
5092 {
5093 // Allocation out of VmaDeviceMemoryBlock.
5094 BlockAllocation m_BlockAllocation;
5095 // Allocation for an object that has its own private VkDeviceMemory.
5096 DedicatedAllocation m_DedicatedAllocation;
5097 };
5098
5099#if VMA_STATS_STRING_ENABLED
5100 uint32_t m_CreationFrameIndex;
5101 uint32_t m_BufferImageUsage; // 0 if unknown.
5102#endif
5103
5104 void FreeUserDataString(VmaAllocator hAllocator);
5105};
5106
5107/*
5108Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5109allocated memory block or free.
5110*/
5111struct VmaSuballocation
5112{
5113 VkDeviceSize offset;
5114 VkDeviceSize size;
5115 VmaAllocation hAllocation;
5116 VmaSuballocationType type;
5117};
5118
5119// Comparator for offsets.
5120struct VmaSuballocationOffsetLess
5121{
5122 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5123 {
5124 return lhs.offset < rhs.offset;
5125 }
5126};
5127struct VmaSuballocationOffsetGreater
5128{
5129 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5130 {
5131 return lhs.offset > rhs.offset;
5132 }
5133};
5134
5135typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5136
5137// Cost of one additional allocation lost, as equivalent in bytes.
5138static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5139
5140/*
5141Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5142
5143If canMakeOtherLost was false:
5144- item points to a FREE suballocation.
5145- itemsToMakeLostCount is 0.
5146
5147If canMakeOtherLost was true:
5148- item points to first of sequence of suballocations, which are either FREE,
5149 or point to VmaAllocations that can become lost.
5150- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5151 the requested allocation to succeed.
5152*/
5153struct VmaAllocationRequest
5154{
5155 VkDeviceSize offset;
5156 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5157 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5158 VmaSuballocationList::iterator item;
5159 size_t itemsToMakeLostCount;
5160 void* customData;
5161
5162 VkDeviceSize CalcCost() const
5163 {
5164 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5165 }
5166};
5167
5168/*
5169Data structure used for bookkeeping of allocations and unused ranges of memory
5170in a single VkDeviceMemory block.
5171*/
5172class VmaBlockMetadata
5173{
5174public:
5175 VmaBlockMetadata(VmaAllocator hAllocator);
5176 virtual ~VmaBlockMetadata() { }
5177 virtual void Init(VkDeviceSize size) { m_Size = size; }
5178
5179 // Validates all data structures inside this object. If not valid, returns false.
5180 virtual bool Validate() const = 0;
5181 VkDeviceSize GetSize() const { return m_Size; }
5182 virtual size_t GetAllocationCount() const = 0;
5183 virtual VkDeviceSize GetSumFreeSize() const = 0;
5184 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5185 // Returns true if this block is empty - contains only single free suballocation.
5186 virtual bool IsEmpty() const = 0;
5187
5188 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5189 // Shouldn't modify blockCount.
5190 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5191
5192#if VMA_STATS_STRING_ENABLED
5193 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5194#endif
5195
5196 // Tries to find a place for suballocation with given parameters inside this block.
5197 // If succeeded, fills pAllocationRequest and returns true.
5198 // If failed, returns false.
5199 virtual bool CreateAllocationRequest(
5200 uint32_t currentFrameIndex,
5201 uint32_t frameInUseCount,
5202 VkDeviceSize bufferImageGranularity,
5203 VkDeviceSize allocSize,
5204 VkDeviceSize allocAlignment,
5205 bool upperAddress,
5206 VmaSuballocationType allocType,
5207 bool canMakeOtherLost,
5208 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5209 uint32_t strategy,
5210 VmaAllocationRequest* pAllocationRequest) = 0;
5211
5212 virtual bool MakeRequestedAllocationsLost(
5213 uint32_t currentFrameIndex,
5214 uint32_t frameInUseCount,
5215 VmaAllocationRequest* pAllocationRequest) = 0;
5216
5217 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5218
5219 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5220
5221 // Makes actual allocation based on request. Request must already be checked and valid.
5222 virtual void Alloc(
5223 const VmaAllocationRequest& request,
5224 VmaSuballocationType type,
5225 VkDeviceSize allocSize,
5226 bool upperAddress,
5227 VmaAllocation hAllocation) = 0;
5228
5229 // Frees suballocation assigned to given memory region.
5230 virtual void Free(const VmaAllocation allocation) = 0;
5231 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5232
5233 // Tries to resize (grow or shrink) space for given allocation, in place.
5234 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5235
5236protected:
5237 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5238
5239#if VMA_STATS_STRING_ENABLED
5240 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5241 VkDeviceSize unusedBytes,
5242 size_t allocationCount,
5243 size_t unusedRangeCount) const;
5244 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5245 VkDeviceSize offset,
5246 VmaAllocation hAllocation) const;
5247 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5248 VkDeviceSize offset,
5249 VkDeviceSize size) const;
5250 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5251#endif
5252
5253private:
5254 VkDeviceSize m_Size;
5255 const VkAllocationCallbacks* m_pAllocationCallbacks;
5256};
5257
5258#define VMA_VALIDATE(cond) do { if(!(cond)) { \
5259 VMA_ASSERT(0 && "Validation failed: " #cond); \
5260 return false; \
5261 } } while(false)
5262
5263class VmaBlockMetadata_Generic : public VmaBlockMetadata
5264{
5265 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5266public:
5267 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5268 virtual ~VmaBlockMetadata_Generic();
5269 virtual void Init(VkDeviceSize size);
5270
5271 virtual bool Validate() const;
5272 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
5273 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5274 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5275 virtual bool IsEmpty() const;
5276
5277 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5278 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5279
5280#if VMA_STATS_STRING_ENABLED
5281 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5282#endif
5283
5284 virtual bool CreateAllocationRequest(
5285 uint32_t currentFrameIndex,
5286 uint32_t frameInUseCount,
5287 VkDeviceSize bufferImageGranularity,
5288 VkDeviceSize allocSize,
5289 VkDeviceSize allocAlignment,
5290 bool upperAddress,
5291 VmaSuballocationType allocType,
5292 bool canMakeOtherLost,
5293 uint32_t strategy,
5294 VmaAllocationRequest* pAllocationRequest);
5295
5296 virtual bool MakeRequestedAllocationsLost(
5297 uint32_t currentFrameIndex,
5298 uint32_t frameInUseCount,
5299 VmaAllocationRequest* pAllocationRequest);
5300
5301 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5302
5303 virtual VkResult CheckCorruption(const void* pBlockData);
5304
5305 virtual void Alloc(
5306 const VmaAllocationRequest& request,
5307 VmaSuballocationType type,
5308 VkDeviceSize allocSize,
5309 bool upperAddress,
5310 VmaAllocation hAllocation);
5311
5312 virtual void Free(const VmaAllocation allocation);
5313 virtual void FreeAtOffset(VkDeviceSize offset);
5314
5315 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5316
5317 ////////////////////////////////////////////////////////////////////////////////
5318 // For defragmentation
5319
5320 bool IsBufferImageGranularityConflictPossible(
5321 VkDeviceSize bufferImageGranularity,
5322 VmaSuballocationType& inOutPrevSuballocType) const;
5323
5324private:
5325 friend class VmaDefragmentationAlgorithm_Generic;
5326 friend class VmaDefragmentationAlgorithm_Fast;
5327
5328 uint32_t m_FreeCount;
5329 VkDeviceSize m_SumFreeSize;
5330 VmaSuballocationList m_Suballocations;
5331 // Suballocations that are free and have size greater than certain threshold.
5332 // Sorted by size, ascending.
5333 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5334
5335 bool ValidateFreeSuballocationList() const;
5336
5337 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5338 // If yes, fills pOffset and returns true. If no, returns false.
5339 bool CheckAllocation(
5340 uint32_t currentFrameIndex,
5341 uint32_t frameInUseCount,
5342 VkDeviceSize bufferImageGranularity,
5343 VkDeviceSize allocSize,
5344 VkDeviceSize allocAlignment,
5345 VmaSuballocationType allocType,
5346 VmaSuballocationList::const_iterator suballocItem,
5347 bool canMakeOtherLost,
5348 VkDeviceSize* pOffset,
5349 size_t* itemsToMakeLostCount,
5350 VkDeviceSize* pSumFreeSize,
5351 VkDeviceSize* pSumItemSize) const;
5352 // Given free suballocation, it merges it with following one, which must also be free.
5353 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5354 // Releases given suballocation, making it free.
5355 // Merges it with adjacent free suballocations if applicable.
5356 // Returns iterator to new free suballocation at this place.
5357 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5358 // Given free suballocation, it inserts it into sorted list of
5359 // m_FreeSuballocationsBySize if it's suitable.
5360 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5361 // Given free suballocation, it removes it from sorted list of
5362 // m_FreeSuballocationsBySize if it's suitable.
5363 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5364};
5365
5366/*
5367Allocations and their references in internal data structure look like this:
5368
5369if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5370
5371 0 +-------+
5372 | |
5373 | |
5374 | |
5375 +-------+
5376 | Alloc | 1st[m_1stNullItemsBeginCount]
5377 +-------+
5378 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5379 +-------+
5380 | ... |
5381 +-------+
5382 | Alloc | 1st[1st.size() - 1]
5383 +-------+
5384 | |
5385 | |
5386 | |
5387GetSize() +-------+
5388
5389if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5390
5391 0 +-------+
5392 | Alloc | 2nd[0]
5393 +-------+
5394 | Alloc | 2nd[1]
5395 +-------+
5396 | ... |
5397 +-------+
5398 | Alloc | 2nd[2nd.size() - 1]
5399 +-------+
5400 | |
5401 | |
5402 | |
5403 +-------+
5404 | Alloc | 1st[m_1stNullItemsBeginCount]
5405 +-------+
5406 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5407 +-------+
5408 | ... |
5409 +-------+
5410 | Alloc | 1st[1st.size() - 1]
5411 +-------+
5412 | |
5413GetSize() +-------+
5414
5415if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5416
5417 0 +-------+
5418 | |
5419 | |
5420 | |
5421 +-------+
5422 | Alloc | 1st[m_1stNullItemsBeginCount]
5423 +-------+
5424 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5425 +-------+
5426 | ... |
5427 +-------+
5428 | Alloc | 1st[1st.size() - 1]
5429 +-------+
5430 | |
5431 | |
5432 | |
5433 +-------+
5434 | Alloc | 2nd[2nd.size() - 1]
5435 +-------+
5436 | ... |
5437 +-------+
5438 | Alloc | 2nd[1]
5439 +-------+
5440 | Alloc | 2nd[0]
5441GetSize() +-------+
5442
5443*/
5444class VmaBlockMetadata_Linear : public VmaBlockMetadata
5445{
5446 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5447public:
5448 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5449 virtual ~VmaBlockMetadata_Linear();
5450 virtual void Init(VkDeviceSize size);
5451
5452 virtual bool Validate() const;
5453 virtual size_t GetAllocationCount() const;
5454 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5455 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5456 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5457
5458 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5459 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5460
5461#if VMA_STATS_STRING_ENABLED
5462 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5463#endif
5464
5465 virtual bool CreateAllocationRequest(
5466 uint32_t currentFrameIndex,
5467 uint32_t frameInUseCount,
5468 VkDeviceSize bufferImageGranularity,
5469 VkDeviceSize allocSize,
5470 VkDeviceSize allocAlignment,
5471 bool upperAddress,
5472 VmaSuballocationType allocType,
5473 bool canMakeOtherLost,
5474 uint32_t strategy,
5475 VmaAllocationRequest* pAllocationRequest);
5476
5477 virtual bool MakeRequestedAllocationsLost(
5478 uint32_t currentFrameIndex,
5479 uint32_t frameInUseCount,
5480 VmaAllocationRequest* pAllocationRequest);
5481
5482 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5483
5484 virtual VkResult CheckCorruption(const void* pBlockData);
5485
5486 virtual void Alloc(
5487 const VmaAllocationRequest& request,
5488 VmaSuballocationType type,
5489 VkDeviceSize allocSize,
5490 bool upperAddress,
5491 VmaAllocation hAllocation);
5492
5493 virtual void Free(const VmaAllocation allocation);
5494 virtual void FreeAtOffset(VkDeviceSize offset);
5495
5496private:
5497 /*
5498 There are two suballocation vectors, used in ping-pong way.
5499 The one with index m_1stVectorIndex is called 1st.
5500 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5501 2nd can be non-empty only when 1st is not empty.
5502 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5503 */
5504 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5505
5506 enum SECOND_VECTOR_MODE
5507 {
5508 SECOND_VECTOR_EMPTY,
5509 /*
5510 Suballocations in 2nd vector are created later than the ones in 1st, but they
5511 all have smaller offset.
5512 */
5513 SECOND_VECTOR_RING_BUFFER,
5514 /*
5515 Suballocations in 2nd vector are upper side of double stack.
5516 They all have offsets higher than those in 1st vector.
5517 Top of this stack means smaller offsets, but higher indices in this vector.
5518 */
5519 SECOND_VECTOR_DOUBLE_STACK,
5520 };
5521
5522 VkDeviceSize m_SumFreeSize;
5523 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5524 uint32_t m_1stVectorIndex;
5525 SECOND_VECTOR_MODE m_2ndVectorMode;
5526
5527 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5528 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5529 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5530 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5531
5532 // Number of items in 1st vector with hAllocation = null at the beginning.
5533 size_t m_1stNullItemsBeginCount;
5534 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5535 size_t m_1stNullItemsMiddleCount;
5536 // Number of items in 2nd vector with hAllocation = null.
5537 size_t m_2ndNullItemsCount;
5538
5539 bool ShouldCompact1st() const;
5540 void CleanupAfterFree();
5541};
5542
5543/*
5544- GetSize() is the original size of allocated memory block.
5545- m_UsableSize is this size aligned down to a power of two.
5546 All allocations and calculations happen relative to m_UsableSize.
5547- GetUnusableSize() is the difference between them.
5548 It is repoted as separate, unused range, not available for allocations.
5549
5550Node at level 0 has size = m_UsableSize.
5551Each next level contains nodes with size 2 times smaller than current level.
5552m_LevelCount is the maximum number of levels to use in the current object.
5553*/
5554class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5555{
5556 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5557public:
5558 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5559 virtual ~VmaBlockMetadata_Buddy();
5560 virtual void Init(VkDeviceSize size);
5561
5562 virtual bool Validate() const;
5563 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
5564 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5565 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5566 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5567
5568 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5569 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5570
5571#if VMA_STATS_STRING_ENABLED
5572 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5573#endif
5574
5575 virtual bool CreateAllocationRequest(
5576 uint32_t currentFrameIndex,
5577 uint32_t frameInUseCount,
5578 VkDeviceSize bufferImageGranularity,
5579 VkDeviceSize allocSize,
5580 VkDeviceSize allocAlignment,
5581 bool upperAddress,
5582 VmaSuballocationType allocType,
5583 bool canMakeOtherLost,
5584 uint32_t strategy,
5585 VmaAllocationRequest* pAllocationRequest);
5586
5587 virtual bool MakeRequestedAllocationsLost(
5588 uint32_t currentFrameIndex,
5589 uint32_t frameInUseCount,
5590 VmaAllocationRequest* pAllocationRequest);
5591
5592 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5593
5594 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5595
5596 virtual void Alloc(
5597 const VmaAllocationRequest& request,
5598 VmaSuballocationType type,
5599 VkDeviceSize allocSize,
5600 bool upperAddress,
5601 VmaAllocation hAllocation);
5602
5603 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
5604 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5605
5606private:
5607 static const VkDeviceSize MIN_NODE_SIZE = 32;
5608 static const size_t MAX_LEVELS = 30;
5609
5610 struct ValidationContext
5611 {
5612 size_t calculatedAllocationCount;
5613 size_t calculatedFreeCount;
5614 VkDeviceSize calculatedSumFreeSize;
5615
5616 ValidationContext() :
5617 calculatedAllocationCount(0),
5618 calculatedFreeCount(0),
5619 calculatedSumFreeSize(0) { }
5620 };
5621
5622 struct Node
5623 {
5624 VkDeviceSize offset;
5625 enum TYPE
5626 {
5627 TYPE_FREE,
5628 TYPE_ALLOCATION,
5629 TYPE_SPLIT,
5630 TYPE_COUNT
5631 } type;
5632 Node* parent;
5633 Node* buddy;
5634
5635 union
5636 {
5637 struct
5638 {
5639 Node* prev;
5640 Node* next;
5641 } free;
5642 struct
5643 {
5644 VmaAllocation alloc;
5645 } allocation;
5646 struct
5647 {
5648 Node* leftChild;
5649 } split;
5650 };
5651 };
5652
5653 // Size of the memory block aligned down to a power of two.
5654 VkDeviceSize m_UsableSize;
5655 uint32_t m_LevelCount;
5656
5657 Node* m_Root;
5658 struct {
5659 Node* front;
5660 Node* back;
5661 } m_FreeList[MAX_LEVELS];
5662 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5663 size_t m_AllocationCount;
5664 // Number of nodes in the tree with type == TYPE_FREE.
5665 size_t m_FreeCount;
5666 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5667 VkDeviceSize m_SumFreeSize;
5668
5669 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5670 void DeleteNode(Node* node);
5671 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5672 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
5673 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5674 // Alloc passed just for validation. Can be null.
5675 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5676 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5677 // Adds node to the front of FreeList at given level.
5678 // node->type must be FREE.
5679 // node->free.prev, next can be undefined.
5680 void AddToFreeListFront(uint32_t level, Node* node);
5681 // Removes node from FreeList at given level.
5682 // node->type must be FREE.
5683 // node->free.prev, next stay untouched.
5684 void RemoveFromFreeList(uint32_t level, Node* node);
5685
5686#if VMA_STATS_STRING_ENABLED
5687 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5688#endif
5689};
5690
5691/*
5692Represents a single block of device memory (`VkDeviceMemory`) with all the
5693data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5694
5695Thread-safety: This class must be externally synchronized.
5696*/
5697class VmaDeviceMemoryBlock
5698{
5699 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5700public:
5701 VmaBlockMetadata* m_pMetadata;
5702
5703 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5704
5705 ~VmaDeviceMemoryBlock()
5706 {
5707 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5708 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5709 }
5710
5711 // Always call after construction.
5712 void Init(
5713 VmaAllocator hAllocator,
5714 uint32_t newMemoryTypeIndex,
5715 VkDeviceMemory newMemory,
5716 VkDeviceSize newSize,
5717 uint32_t id,
5718 uint32_t algorithm);
5719 // Always call before destruction.
5720 void Destroy(VmaAllocator allocator);
5721
5722 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
5723 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5724 uint32_t GetId() const { return m_Id; }
5725 void* GetMappedData() const { return m_pMappedData; }
5726
5727 // Validates all data structures inside this object. If not valid, returns false.
5728 bool Validate() const;
5729
5730 VkResult CheckCorruption(VmaAllocator hAllocator);
5731
5732 // ppData can be null.
5733 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5734 void Unmap(VmaAllocator hAllocator, uint32_t count);
5735
5736 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5737 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5738
5739 VkResult BindBufferMemory(
5740 const VmaAllocator hAllocator,
5741 const VmaAllocation hAllocation,
5742 VkBuffer hBuffer);
5743 VkResult BindImageMemory(
5744 const VmaAllocator hAllocator,
5745 const VmaAllocation hAllocation,
5746 VkImage hImage);
5747
5748private:
5749 uint32_t m_MemoryTypeIndex;
5750 uint32_t m_Id;
5751 VkDeviceMemory m_hMemory;
5752
5753 /*
5754 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5755 Also protects m_MapCount, m_pMappedData.
5756 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5757 */
5758 VMA_MUTEX m_Mutex;
5759 uint32_t m_MapCount;
5760 void* m_pMappedData;
5761};
5762
5763struct VmaPointerLess
5764{
5765 bool operator()(const void* lhs, const void* rhs) const
5766 {
5767 return lhs < rhs;
5768 }
5769};
5770
5771struct VmaDefragmentationMove
5772{
5773 size_t srcBlockIndex;
5774 size_t dstBlockIndex;
5775 VkDeviceSize srcOffset;
5776 VkDeviceSize dstOffset;
5777 VkDeviceSize size;
5778};
5779
5780class VmaDefragmentationAlgorithm;
5781
5782/*
5783Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5784Vulkan memory type.
5785
5786Synchronized internally with a mutex.
5787*/
5788struct VmaBlockVector
5789{
5790 VMA_CLASS_NO_COPY(VmaBlockVector)
5791public:
5792 VmaBlockVector(
5793 VmaAllocator hAllocator,
5794 uint32_t memoryTypeIndex,
5795 VkDeviceSize preferredBlockSize,
5796 size_t minBlockCount,
5797 size_t maxBlockCount,
5798 VkDeviceSize bufferImageGranularity,
5799 uint32_t frameInUseCount,
5800 bool isCustomPool,
5801 bool explicitBlockSize,
5802 uint32_t algorithm);
5803 ~VmaBlockVector();
5804
5805 VkResult CreateMinBlocks();
5806
5807 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5808 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
5809 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
5810 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
5811 uint32_t GetAlgorithm() const { return m_Algorithm; }
5812
5813 void GetPoolStats(VmaPoolStats* pStats);
5814
5815 bool IsEmpty() const { return m_Blocks.empty(); }
5816 bool IsCorruptionDetectionEnabled() const;
5817
5818 VkResult Allocate(
5819 VmaPool hCurrentPool,
5820 uint32_t currentFrameIndex,
5821 VkDeviceSize size,
5822 VkDeviceSize alignment,
5823 const VmaAllocationCreateInfo& createInfo,
5824 VmaSuballocationType suballocType,
5825 size_t allocationCount,
5826 VmaAllocation* pAllocations);
5827
5828 void Free(
5829 VmaAllocation hAllocation);
5830
5831 // Adds statistics of this BlockVector to pStats.
5832 void AddStats(VmaStats* pStats);
5833
5834#if VMA_STATS_STRING_ENABLED
5835 void PrintDetailedMap(class VmaJsonWriter& json);
5836#endif
5837
5838 void MakePoolAllocationsLost(
5839 uint32_t currentFrameIndex,
5840 size_t* pLostAllocationCount);
5841 VkResult CheckCorruption();
5842
5843 // Saves results in pCtx->res.
5844 void Defragment(
5845 class VmaBlockVectorDefragmentationContext* pCtx,
5846 VmaDefragmentationStats* pStats,
5847 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5848 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5849 VkCommandBuffer commandBuffer);
5850 void DefragmentationEnd(
5851 class VmaBlockVectorDefragmentationContext* pCtx,
5852 VmaDefragmentationStats* pStats);
5853
5854 ////////////////////////////////////////////////////////////////////////////////
5855 // To be used only while the m_Mutex is locked. Used during defragmentation.
5856
5857 size_t GetBlockCount() const { return m_Blocks.size(); }
5858 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5859 size_t CalcAllocationCount() const;
5860 bool IsBufferImageGranularityConflictPossible() const;
5861
5862private:
5863 friend class VmaDefragmentationAlgorithm_Generic;
5864
5865 const VmaAllocator m_hAllocator;
5866 const uint32_t m_MemoryTypeIndex;
5867 const VkDeviceSize m_PreferredBlockSize;
5868 const size_t m_MinBlockCount;
5869 const size_t m_MaxBlockCount;
5870 const VkDeviceSize m_BufferImageGranularity;
5871 const uint32_t m_FrameInUseCount;
5872 const bool m_IsCustomPool;
5873 const bool m_ExplicitBlockSize;
5874 const uint32_t m_Algorithm;
5875 /* There can be at most one allocation that is completely empty - a
5876 hysteresis to avoid pessimistic case of alternating creation and destruction
5877 of a VkDeviceMemory. */
5878 bool m_HasEmptyBlock;
5879 VMA_RW_MUTEX m_Mutex;
5880 // Incrementally sorted by sumFreeSize, ascending.
5881 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5882 uint32_t m_NextBlockId;
5883
5884 VkDeviceSize CalcMaxBlockSize() const;
5885
5886 // Finds and removes given block from vector.
5887 void Remove(VmaDeviceMemoryBlock* pBlock);
5888
5889 // Performs single step in sorting m_Blocks. They may not be fully sorted
5890 // after this call.
5891 void IncrementallySortBlocks();
5892
5893 VkResult AllocatePage(
5894 VmaPool hCurrentPool,
5895 uint32_t currentFrameIndex,
5896 VkDeviceSize size,
5897 VkDeviceSize alignment,
5898 const VmaAllocationCreateInfo& createInfo,
5899 VmaSuballocationType suballocType,
5900 VmaAllocation* pAllocation);
5901
5902 // To be used only without CAN_MAKE_OTHER_LOST flag.
5903 VkResult AllocateFromBlock(
5904 VmaDeviceMemoryBlock* pBlock,
5905 VmaPool hCurrentPool,
5906 uint32_t currentFrameIndex,
5907 VkDeviceSize size,
5908 VkDeviceSize alignment,
5909 VmaAllocationCreateFlags allocFlags,
5910 void* pUserData,
5911 VmaSuballocationType suballocType,
5912 uint32_t strategy,
5913 VmaAllocation* pAllocation);
5914
5915 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5916
5917 // Saves result to pCtx->res.
5918 void ApplyDefragmentationMovesCpu(
5919 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5920 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5921 // Saves result to pCtx->res.
5922 void ApplyDefragmentationMovesGpu(
5923 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5924 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5925 VkCommandBuffer commandBuffer);
5926
5927 /*
5928 Used during defragmentation. pDefragmentationStats is optional. It's in/out
5929 - updated with new data.
5930 */
5931 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5932};
5933
5934struct VmaPool_T
5935{
5936 VMA_CLASS_NO_COPY(VmaPool_T)
5937public:
5938 VmaBlockVector m_BlockVector;
5939
5940 VmaPool_T(
5941 VmaAllocator hAllocator,
5942 const VmaPoolCreateInfo& createInfo,
5943 VkDeviceSize preferredBlockSize);
5944 ~VmaPool_T();
5945
5946 uint32_t GetId() const { return m_Id; }
5947 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5948
5949#if VMA_STATS_STRING_ENABLED
5950 //void PrintDetailedMap(class VmaStringBuilder& sb);
5951#endif
5952
5953private:
5954 uint32_t m_Id;
5955};
5956
5957/*
5958Performs defragmentation:
5959
5960- Updates `pBlockVector->m_pMetadata`.
5961- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5962- Does not move actual data, only returns requested moves as `moves`.
5963*/
5964class VmaDefragmentationAlgorithm
5965{
5966 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5967public:
5968 VmaDefragmentationAlgorithm(
5969 VmaAllocator hAllocator,
5970 VmaBlockVector* pBlockVector,
5971 uint32_t currentFrameIndex) :
5972 m_hAllocator(hAllocator),
5973 m_pBlockVector(pBlockVector),
5974 m_CurrentFrameIndex(currentFrameIndex)
5975 {
5976 }
5977 virtual ~VmaDefragmentationAlgorithm()
5978 {
5979 }
5980
5981 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5982 virtual void AddAll() = 0;
5983
5984 virtual VkResult Defragment(
5985 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5986 VkDeviceSize maxBytesToMove,
5987 uint32_t maxAllocationsToMove) = 0;
5988
5989 virtual VkDeviceSize GetBytesMoved() const = 0;
5990 virtual uint32_t GetAllocationsMoved() const = 0;
5991
5992protected:
5993 VmaAllocator const m_hAllocator;
5994 VmaBlockVector* const m_pBlockVector;
5995 const uint32_t m_CurrentFrameIndex;
5996
5997 struct AllocationInfo
5998 {
5999 VmaAllocation m_hAllocation;
6000 VkBool32* m_pChanged;
6001
6002 AllocationInfo() :
6003 m_hAllocation(VK_NULL_HANDLE),
6004 m_pChanged(VMA_NULL)
6005 {
6006 }
6007 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6008 m_hAllocation(hAlloc),
6009 m_pChanged(pChanged)
6010 {
6011 }
6012 };
6013};
6014
6015class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6016{
6017 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6018public:
6019 VmaDefragmentationAlgorithm_Generic(
6020 VmaAllocator hAllocator,
6021 VmaBlockVector* pBlockVector,
6022 uint32_t currentFrameIndex,
6023 bool overlappingMoveSupported);
6024 virtual ~VmaDefragmentationAlgorithm_Generic();
6025
6026 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6027 virtual void AddAll() { m_AllAllocations = true; }
6028
6029 virtual VkResult Defragment(
6030 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6031 VkDeviceSize maxBytesToMove,
6032 uint32_t maxAllocationsToMove);
6033
6034 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6035 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6036
6037private:
6038 uint32_t m_AllocationCount;
6039 bool m_AllAllocations;
6040
6041 VkDeviceSize m_BytesMoved;
6042 uint32_t m_AllocationsMoved;
6043
6044 struct AllocationInfoSizeGreater
6045 {
6046 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6047 {
6048 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6049 }
6050 };
6051
6052 struct AllocationInfoOffsetGreater
6053 {
6054 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6055 {
6056 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6057 }
6058 };
6059
6060 struct BlockInfo
6061 {
6062 size_t m_OriginalBlockIndex;
6063 VmaDeviceMemoryBlock* m_pBlock;
6064 bool m_HasNonMovableAllocations;
6065 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6066
6067 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6068 m_OriginalBlockIndex(SIZE_MAX),
6069 m_pBlock(VMA_NULL),
6070 m_HasNonMovableAllocations(true),
6071 m_Allocations(pAllocationCallbacks)
6072 {
6073 }
6074
6075 void CalcHasNonMovableAllocations()
6076 {
6077 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6078 const size_t defragmentAllocCount = m_Allocations.size();
6079 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6080 }
6081
6082 void SortAllocationsBySizeDescending()
6083 {
6084 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6085 }
6086
6087 void SortAllocationsByOffsetDescending()
6088 {
6089 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6090 }
6091 };
6092
6093 struct BlockPointerLess
6094 {
6095 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6096 {
6097 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6098 }
6099 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6100 {
6101 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6102 }
6103 };
6104
6105 // 1. Blocks with some non-movable allocations go first.
6106 // 2. Blocks with smaller sumFreeSize go first.
6107 struct BlockInfoCompareMoveDestination
6108 {
6109 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6110 {
6111 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6112 {
6113 return true;
6114 }
6115 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6116 {
6117 return false;
6118 }
6119 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6120 {
6121 return true;
6122 }
6123 return false;
6124 }
6125 };
6126
6127 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6128 BlockInfoVector m_Blocks;
6129
6130 VkResult DefragmentRound(
6131 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6132 VkDeviceSize maxBytesToMove,
6133 uint32_t maxAllocationsToMove);
6134
6135 size_t CalcBlocksWithNonMovableCount() const;
6136
6137 static bool MoveMakesSense(
6138 size_t dstBlockIndex, VkDeviceSize dstOffset,
6139 size_t srcBlockIndex, VkDeviceSize srcOffset);
6140};
6141
6142class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6143{
6144 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6145public:
6146 VmaDefragmentationAlgorithm_Fast(
6147 VmaAllocator hAllocator,
6148 VmaBlockVector* pBlockVector,
6149 uint32_t currentFrameIndex,
6150 bool overlappingMoveSupported);
6151 virtual ~VmaDefragmentationAlgorithm_Fast();
6152
6153 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
6154 virtual void AddAll() { m_AllAllocations = true; }
6155
6156 virtual VkResult Defragment(
6157 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6158 VkDeviceSize maxBytesToMove,
6159 uint32_t maxAllocationsToMove);
6160
6161 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6162 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6163
6164private:
6165 struct BlockInfo
6166 {
6167 size_t origBlockIndex;
6168 };
6169
6170 class FreeSpaceDatabase
6171 {
6172 public:
6173 FreeSpaceDatabase()
6174 {
6175 FreeSpace s = {};
6176 s.blockInfoIndex = SIZE_MAX;
6177 for(size_t i = 0; i < MAX_COUNT; ++i)
6178 {
6179 m_FreeSpaces[i] = s;
6180 }
6181 }
6182
6183 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6184 {
6185 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6186 {
6187 return;
6188 }
6189
6190 // Find first invalid or the smallest structure.
6191 size_t bestIndex = SIZE_MAX;
6192 for(size_t i = 0; i < MAX_COUNT; ++i)
6193 {
6194 // Empty structure.
6195 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6196 {
6197 bestIndex = i;
6198 break;
6199 }
6200 if(m_FreeSpaces[i].size < size &&
6201 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6202 {
6203 bestIndex = i;
6204 }
6205 }
6206
6207 if(bestIndex != SIZE_MAX)
6208 {
6209 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6210 m_FreeSpaces[bestIndex].offset = offset;
6211 m_FreeSpaces[bestIndex].size = size;
6212 }
6213 }
6214
6215 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6216 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6217 {
6218 size_t bestIndex = SIZE_MAX;
6219 VkDeviceSize bestFreeSpaceAfter = 0;
6220 for(size_t i = 0; i < MAX_COUNT; ++i)
6221 {
6222 // Structure is valid.
6223 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6224 {
6225 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6226 // Allocation fits into this structure.
6227 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6228 {
6229 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6230 (dstOffset + size);
6231 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6232 {
6233 bestIndex = i;
6234 bestFreeSpaceAfter = freeSpaceAfter;
6235 }
6236 }
6237 }
6238 }
6239
6240 if(bestIndex != SIZE_MAX)
6241 {
6242 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6243 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6244
6245 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6246 {
6247 // Leave this structure for remaining empty space.
6248 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6249 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6250 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6251 }
6252 else
6253 {
6254 // This structure becomes invalid.
6255 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6256 }
6257
6258 return true;
6259 }
6260
6261 return false;
6262 }
6263
6264 private:
6265 static const size_t MAX_COUNT = 4;
6266
6267 struct FreeSpace
6268 {
6269 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6270 VkDeviceSize offset;
6271 VkDeviceSize size;
6272 } m_FreeSpaces[MAX_COUNT];
6273 };
6274
6275 const bool m_OverlappingMoveSupported;
6276
6277 uint32_t m_AllocationCount;
6278 bool m_AllAllocations;
6279
6280 VkDeviceSize m_BytesMoved;
6281 uint32_t m_AllocationsMoved;
6282
6283 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6284
6285 void PreprocessMetadata();
6286 void PostprocessMetadata();
6287 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6288};
6289
6290struct VmaBlockDefragmentationContext
6291{
6292 enum BLOCK_FLAG
6293 {
6294 BLOCK_FLAG_USED = 0x00000001,
6295 };
6296 uint32_t flags;
6297 VkBuffer hBuffer;
6298
6299 VmaBlockDefragmentationContext() :
6300 flags(0),
6301 hBuffer(VK_NULL_HANDLE)
6302 {
6303 }
6304};
6305
6306class VmaBlockVectorDefragmentationContext
6307{
6308 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6309public:
6310 VkResult res;
6311 bool mutexLocked;
6312 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6313
6314 VmaBlockVectorDefragmentationContext(
6315 VmaAllocator hAllocator,
6316 VmaPool hCustomPool, // Optional.
6317 VmaBlockVector* pBlockVector,
6318 uint32_t currFrameIndex,
6319 uint32_t flags);
6320 ~VmaBlockVectorDefragmentationContext();
6321
6322 VmaPool GetCustomPool() const { return m_hCustomPool; }
6323 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
6324 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6325
6326 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6327 void AddAll() { m_AllAllocations = true; }
6328
6329 void Begin(bool overlappingMoveSupported);
6330
6331private:
6332 const VmaAllocator m_hAllocator;
6333 // Null if not from custom pool.
6334 const VmaPool m_hCustomPool;
6335 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6336 VmaBlockVector* const m_pBlockVector;
6337 const uint32_t m_CurrFrameIndex;
6338 const uint32_t m_AlgorithmFlags;
6339 // Owner of this object.
6340 VmaDefragmentationAlgorithm* m_pAlgorithm;
6341
6342 struct AllocInfo
6343 {
6344 VmaAllocation hAlloc;
6345 VkBool32* pChanged;
6346 };
6347 // Used between constructor and Begin.
6348 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6349 bool m_AllAllocations;
6350};
6351
6352struct VmaDefragmentationContext_T
6353{
6354private:
6355 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6356public:
6357 VmaDefragmentationContext_T(
6358 VmaAllocator hAllocator,
6359 uint32_t currFrameIndex,
6360 uint32_t flags,
6361 VmaDefragmentationStats* pStats);
6362 ~VmaDefragmentationContext_T();
6363
6364 void AddPools(uint32_t poolCount, VmaPool* pPools);
6365 void AddAllocations(
6366 uint32_t allocationCount,
6367 VmaAllocation* pAllocations,
6368 VkBool32* pAllocationsChanged);
6369
6370 /*
6371 Returns:
6372 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6373 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6374 - Negative value if error occured and object can be destroyed immediately.
6375 */
6376 VkResult Defragment(
6377 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6378 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6379 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6380
6381private:
6382 const VmaAllocator m_hAllocator;
6383 const uint32_t m_CurrFrameIndex;
6384 const uint32_t m_Flags;
6385 VmaDefragmentationStats* const m_pStats;
6386 // Owner of these objects.
6387 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6388 // Owner of these objects.
6389 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6390};
6391
6392#if VMA_RECORDING_ENABLED
6393
6394class VmaRecorder
6395{
6396public:
6397 VmaRecorder();
6398 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6399 void WriteConfiguration(
6400 const VkPhysicalDeviceProperties& devProps,
6401 const VkPhysicalDeviceMemoryProperties& memProps,
6402 bool dedicatedAllocationExtensionEnabled);
6403 ~VmaRecorder();
6404
6405 void RecordCreateAllocator(uint32_t frameIndex);
6406 void RecordDestroyAllocator(uint32_t frameIndex);
6407 void RecordCreatePool(uint32_t frameIndex,
6408 const VmaPoolCreateInfo& createInfo,
6409 VmaPool pool);
6410 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6411 void RecordAllocateMemory(uint32_t frameIndex,
6412 const VkMemoryRequirements& vkMemReq,
6413 const VmaAllocationCreateInfo& createInfo,
6414 VmaAllocation allocation);
6415 void RecordAllocateMemoryPages(uint32_t frameIndex,
6416 const VkMemoryRequirements& vkMemReq,
6417 const VmaAllocationCreateInfo& createInfo,
6418 uint64_t allocationCount,
6419 const VmaAllocation* pAllocations);
6420 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6421 const VkMemoryRequirements& vkMemReq,
6422 bool requiresDedicatedAllocation,
6423 bool prefersDedicatedAllocation,
6424 const VmaAllocationCreateInfo& createInfo,
6425 VmaAllocation allocation);
6426 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6427 const VkMemoryRequirements& vkMemReq,
6428 bool requiresDedicatedAllocation,
6429 bool prefersDedicatedAllocation,
6430 const VmaAllocationCreateInfo& createInfo,
6431 VmaAllocation allocation);
6432 void RecordFreeMemory(uint32_t frameIndex,
6433 VmaAllocation allocation);
6434 void RecordFreeMemoryPages(uint32_t frameIndex,
6435 uint64_t allocationCount,
6436 const VmaAllocation* pAllocations);
6437 void RecordResizeAllocation(
6438 uint32_t frameIndex,
6439 VmaAllocation allocation,
6440 VkDeviceSize newSize);
6441 void RecordSetAllocationUserData(uint32_t frameIndex,
6442 VmaAllocation allocation,
6443 const void* pUserData);
6444 void RecordCreateLostAllocation(uint32_t frameIndex,
6445 VmaAllocation allocation);
6446 void RecordMapMemory(uint32_t frameIndex,
6447 VmaAllocation allocation);
6448 void RecordUnmapMemory(uint32_t frameIndex,
6449 VmaAllocation allocation);
6450 void RecordFlushAllocation(uint32_t frameIndex,
6451 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6452 void RecordInvalidateAllocation(uint32_t frameIndex,
6453 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6454 void RecordCreateBuffer(uint32_t frameIndex,
6455 const VkBufferCreateInfo& bufCreateInfo,
6456 const VmaAllocationCreateInfo& allocCreateInfo,
6457 VmaAllocation allocation);
6458 void RecordCreateImage(uint32_t frameIndex,
6459 const VkImageCreateInfo& imageCreateInfo,
6460 const VmaAllocationCreateInfo& allocCreateInfo,
6461 VmaAllocation allocation);
6462 void RecordDestroyBuffer(uint32_t frameIndex,
6463 VmaAllocation allocation);
6464 void RecordDestroyImage(uint32_t frameIndex,
6465 VmaAllocation allocation);
6466 void RecordTouchAllocation(uint32_t frameIndex,
6467 VmaAllocation allocation);
6468 void RecordGetAllocationInfo(uint32_t frameIndex,
6469 VmaAllocation allocation);
6470 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6471 VmaPool pool);
6472 void RecordDefragmentationBegin(uint32_t frameIndex,
6473 const VmaDefragmentationInfo2& info,
6474 VmaDefragmentationContext ctx);
6475 void RecordDefragmentationEnd(uint32_t frameIndex,
6476 VmaDefragmentationContext ctx);
6477
6478private:
6479 struct CallParams
6480 {
6481 uint32_t threadId;
6482 double time;
6483 };
6484
6485 class UserDataString
6486 {
6487 public:
6488 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
6489 const char* GetString() const { return m_Str; }
6490
6491 private:
6492 char m_PtrStr[17];
6493 const char* m_Str;
6494 };
6495
6496 bool m_UseMutex;
6497 VmaRecordFlags m_Flags;
6498 FILE* m_File;
6499 VMA_MUTEX m_FileMutex;
6500 int64_t m_Freq;
6501 int64_t m_StartCounter;
6502
6503 void GetBasicParams(CallParams& outParams);
6504
6505 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6506 template<typename T>
6507 void PrintPointerList(uint64_t count, const T* pItems)
6508 {
6509 if(count)
6510 {
6511 fprintf(m_File, "%p", pItems[0]);
6512 for(uint64_t i = 1; i < count; ++i)
6513 {
6514 fprintf(m_File, " %p", pItems[i]);
6515 }
6516 }
6517 }
6518
6519 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6520 void Flush();
6521};
6522
6523#endif // #if VMA_RECORDING_ENABLED
6524
6525// Main allocator object.
6526struct VmaAllocator_T
6527{
6528 VMA_CLASS_NO_COPY(VmaAllocator_T)
6529public:
6530 bool m_UseMutex;
6531 bool m_UseKhrDedicatedAllocation;
6532 VkDevice m_hDevice;
6533 bool m_AllocationCallbacksSpecified;
6534 VkAllocationCallbacks m_AllocationCallbacks;
6535 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6536
6537 // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6538 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6539 VMA_MUTEX m_HeapSizeLimitMutex;
6540
6541 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6542 VkPhysicalDeviceMemoryProperties m_MemProps;
6543
6544 // Default pools.
6545 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6546
6547 // Each vector is sorted by memory (handle value).
6548 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6549 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6550 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6551
6552 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6553 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6554 ~VmaAllocator_T();
6555
6556 const VkAllocationCallbacks* GetAllocationCallbacks() const
6557 {
6558 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6559 }
6560 const VmaVulkanFunctions& GetVulkanFunctions() const
6561 {
6562 return m_VulkanFunctions;
6563 }
6564
6565 VkDeviceSize GetBufferImageGranularity() const
6566 {
6567 return VMA_MAX(
6568 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6569 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6570 }
6571
6572 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
6573 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6574
6575 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6576 {
6577 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6578 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6579 }
6580 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
6581 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6582 {
6583 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6584 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6585 }
6586 // Minimum alignment for all allocations in specific memory type.
6587 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6588 {
6589 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6590 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6591 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6592 }
6593
6594 bool IsIntegratedGpu() const
6595 {
6596 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6597 }
6598
6599#if VMA_RECORDING_ENABLED
6600 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6601#endif
6602
6603 void GetBufferMemoryRequirements(
6604 VkBuffer hBuffer,
6605 VkMemoryRequirements& memReq,
6606 bool& requiresDedicatedAllocation,
6607 bool& prefersDedicatedAllocation) const;
6608 void GetImageMemoryRequirements(
6609 VkImage hImage,
6610 VkMemoryRequirements& memReq,
6611 bool& requiresDedicatedAllocation,
6612 bool& prefersDedicatedAllocation) const;
6613
6614 // Main allocation function.
6615 VkResult AllocateMemory(
6616 const VkMemoryRequirements& vkMemReq,
6617 bool requiresDedicatedAllocation,
6618 bool prefersDedicatedAllocation,
6619 VkBuffer dedicatedBuffer,
6620 VkImage dedicatedImage,
6621 const VmaAllocationCreateInfo& createInfo,
6622 VmaSuballocationType suballocType,
6623 size_t allocationCount,
6624 VmaAllocation* pAllocations);
6625
6626 // Main deallocation function.
6627 void FreeMemory(
6628 size_t allocationCount,
6629 const VmaAllocation* pAllocations);
6630
6631 VkResult ResizeAllocation(
6632 const VmaAllocation alloc,
6633 VkDeviceSize newSize);
6634
6635 void CalculateStats(VmaStats* pStats);
6636
6637#if VMA_STATS_STRING_ENABLED
6638 void PrintDetailedMap(class VmaJsonWriter& json);
6639#endif
6640
6641 VkResult DefragmentationBegin(
6642 const VmaDefragmentationInfo2& info,
6643 VmaDefragmentationStats* pStats,
6644 VmaDefragmentationContext* pContext);
6645 VkResult DefragmentationEnd(
6646 VmaDefragmentationContext context);
6647
6648 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6649 bool TouchAllocation(VmaAllocation hAllocation);
6650
6651 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6652 void DestroyPool(VmaPool pool);
6653 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6654
6655 void SetCurrentFrameIndex(uint32_t frameIndex);
6656 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6657
6658 void MakePoolAllocationsLost(
6659 VmaPool hPool,
6660 size_t* pLostAllocationCount);
6661 VkResult CheckPoolCorruption(VmaPool hPool);
6662 VkResult CheckCorruption(uint32_t memoryTypeBits);
6663
6664 void CreateLostAllocation(VmaAllocation* pAllocation);
6665
6666 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6667 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6668
6669 VkResult Map(VmaAllocation hAllocation, void** ppData);
6670 void Unmap(VmaAllocation hAllocation);
6671
6672 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6673 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6674
6675 void FlushOrInvalidateAllocation(
6676 VmaAllocation hAllocation,
6677 VkDeviceSize offset, VkDeviceSize size,
6678 VMA_CACHE_OPERATION op);
6679
6680 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6681
6682private:
6683 VkDeviceSize m_PreferredLargeHeapBlockSize;
6684
6685 VkPhysicalDevice m_PhysicalDevice;
6686 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6687
6688 VMA_RW_MUTEX m_PoolsMutex;
6689 // Protected by m_PoolsMutex. Sorted by pointer value.
6690 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6691 uint32_t m_NextPoolId;
6692
6693 VmaVulkanFunctions m_VulkanFunctions;
6694
6695#if VMA_RECORDING_ENABLED
6696 VmaRecorder* m_pRecorder;
6697#endif
6698
6699 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6700
6701 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6702
6703 VkResult AllocateMemoryOfType(
6704 VkDeviceSize size,
6705 VkDeviceSize alignment,
6706 bool dedicatedAllocation,
6707 VkBuffer dedicatedBuffer,
6708 VkImage dedicatedImage,
6709 const VmaAllocationCreateInfo& createInfo,
6710 uint32_t memTypeIndex,
6711 VmaSuballocationType suballocType,
6712 size_t allocationCount,
6713 VmaAllocation* pAllocations);
6714
6715 // Helper function only to be used inside AllocateDedicatedMemory.
6716 VkResult AllocateDedicatedMemoryPage(
6717 VkDeviceSize size,
6718 VmaSuballocationType suballocType,
6719 uint32_t memTypeIndex,
6720 const VkMemoryAllocateInfo& allocInfo,
6721 bool map,
6722 bool isUserDataString,
6723 void* pUserData,
6724 VmaAllocation* pAllocation);
6725
6726 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6727 VkResult AllocateDedicatedMemory(
6728 VkDeviceSize size,
6729 VmaSuballocationType suballocType,
6730 uint32_t memTypeIndex,
6731 bool map,
6732 bool isUserDataString,
6733 void* pUserData,
6734 VkBuffer dedicatedBuffer,
6735 VkImage dedicatedImage,
6736 size_t allocationCount,
6737 VmaAllocation* pAllocations);
6738
6739 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6740 void FreeDedicatedMemory(VmaAllocation allocation);
6741};
6742
6743////////////////////////////////////////////////////////////////////////////////
6744// Memory allocation #2 after VmaAllocator_T definition
6745
6746static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6747{
6748 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6749}
6750
6751static void VmaFree(VmaAllocator hAllocator, void* ptr)
6752{
6753 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6754}
6755
6756template<typename T>
6757static T* VmaAllocate(VmaAllocator hAllocator)
6758{
6759 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6760}
6761
6762template<typename T>
6763static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6764{
6765 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6766}
6767
6768template<typename T>
6769static void vma_delete(VmaAllocator hAllocator, T* ptr)
6770{
6771 if(ptr != VMA_NULL)
6772 {
6773 ptr->~T();
6774 VmaFree(hAllocator, ptr);
6775 }
6776}
6777
6778template<typename T>
6779static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6780{
6781 if(ptr != VMA_NULL)
6782 {
6783 for(size_t i = count; i--; )
6784 ptr[i].~T();
6785 VmaFree(hAllocator, ptr);
6786 }
6787}
6788
6789////////////////////////////////////////////////////////////////////////////////
6790// VmaStringBuilder
6791
6792#if VMA_STATS_STRING_ENABLED
6793
6794class VmaStringBuilder
6795{
6796public:
6797 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
6798 size_t GetLength() const { return m_Data.size(); }
6799 const char* GetData() const { return m_Data.data(); }
6800
6801 void Add(char ch) { m_Data.push_back(ch); }
6802 void Add(const char* pStr);
6803 void AddNewLine() { Add('\n'); }
6804 void AddNumber(uint32_t num);
6805 void AddNumber(uint64_t num);
6806 void AddPointer(const void* ptr);
6807
6808private:
6809 VmaVector< char, VmaStlAllocator<char> > m_Data;
6810};
6811
6812void VmaStringBuilder::Add(const char* pStr)
6813{
6814 const size_t strLen = strlen(pStr);
6815 if(strLen > 0)
6816 {
6817 const size_t oldCount = m_Data.size();
6818 m_Data.resize(oldCount + strLen);
6819 memcpy(m_Data.data() + oldCount, pStr, strLen);
6820 }
6821}
6822
6823void VmaStringBuilder::AddNumber(uint32_t num)
6824{
6825 char buf[11];
6826 VmaUint32ToStr(buf, sizeof(buf), num);
6827 Add(buf);
6828}
6829
6830void VmaStringBuilder::AddNumber(uint64_t num)
6831{
6832 char buf[21];
6833 VmaUint64ToStr(buf, sizeof(buf), num);
6834 Add(buf);
6835}
6836
6837void VmaStringBuilder::AddPointer(const void* ptr)
6838{
6839 char buf[21];
6840 VmaPtrToStr(buf, sizeof(buf), ptr);
6841 Add(buf);
6842}
6843
6844#endif // #if VMA_STATS_STRING_ENABLED
6845
6846////////////////////////////////////////////////////////////////////////////////
6847// VmaJsonWriter
6848
6849#if VMA_STATS_STRING_ENABLED
6850
6851class VmaJsonWriter
6852{
6853 VMA_CLASS_NO_COPY(VmaJsonWriter)
6854public:
6855 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6856 ~VmaJsonWriter();
6857
6858 void BeginObject(bool singleLine = false);
6859 void EndObject();
6860
6861 void BeginArray(bool singleLine = false);
6862 void EndArray();
6863
6864 void WriteString(const char* pStr);
6865 void BeginString(const char* pStr = VMA_NULL);
6866 void ContinueString(const char* pStr);
6867 void ContinueString(uint32_t n);
6868 void ContinueString(uint64_t n);
6869 void ContinueString_Pointer(const void* ptr);
6870 void EndString(const char* pStr = VMA_NULL);
6871
6872 void WriteNumber(uint32_t n);
6873 void WriteNumber(uint64_t n);
6874 void WriteBool(bool b);
6875 void WriteNull();
6876
6877private:
6878 static const char* const INDENT;
6879
6880 enum COLLECTION_TYPE
6881 {
6882 COLLECTION_TYPE_OBJECT,
6883 COLLECTION_TYPE_ARRAY,
6884 };
6885 struct StackItem
6886 {
6887 COLLECTION_TYPE type;
6888 uint32_t valueCount;
6889 bool singleLineMode;
6890 };
6891
6892 VmaStringBuilder& m_SB;
6893 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6894 bool m_InsideString;
6895
6896 void BeginValue(bool isString);
6897 void WriteIndent(bool oneLess = false);
6898};
6899
6900const char* const VmaJsonWriter::INDENT = " ";
6901
6902VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6903 m_SB(sb),
6904 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6905 m_InsideString(false)
6906{
6907}
6908
6909VmaJsonWriter::~VmaJsonWriter()
6910{
6911 VMA_ASSERT(!m_InsideString);
6912 VMA_ASSERT(m_Stack.empty());
6913}
6914
6915void VmaJsonWriter::BeginObject(bool singleLine)
6916{
6917 VMA_ASSERT(!m_InsideString);
6918
6919 BeginValue(false);
6920 m_SB.Add('{');
6921
6922 StackItem item;
6923 item.type = COLLECTION_TYPE_OBJECT;
6924 item.valueCount = 0;
6925 item.singleLineMode = singleLine;
6926 m_Stack.push_back(item);
6927}
6928
6929void VmaJsonWriter::EndObject()
6930{
6931 VMA_ASSERT(!m_InsideString);
6932
6933 WriteIndent(true);
6934 m_SB.Add('}');
6935
6936 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6937 m_Stack.pop_back();
6938}
6939
6940void VmaJsonWriter::BeginArray(bool singleLine)
6941{
6942 VMA_ASSERT(!m_InsideString);
6943
6944 BeginValue(false);
6945 m_SB.Add('[');
6946
6947 StackItem item;
6948 item.type = COLLECTION_TYPE_ARRAY;
6949 item.valueCount = 0;
6950 item.singleLineMode = singleLine;
6951 m_Stack.push_back(item);
6952}
6953
6954void VmaJsonWriter::EndArray()
6955{
6956 VMA_ASSERT(!m_InsideString);
6957
6958 WriteIndent(true);
6959 m_SB.Add(']');
6960
6961 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6962 m_Stack.pop_back();
6963}
6964
6965void VmaJsonWriter::WriteString(const char* pStr)
6966{
6967 BeginString(pStr);
6968 EndString();
6969}
6970
6971void VmaJsonWriter::BeginString(const char* pStr)
6972{
6973 VMA_ASSERT(!m_InsideString);
6974
6975 BeginValue(true);
6976 m_SB.Add('"');
6977 m_InsideString = true;
6978 if(pStr != VMA_NULL && pStr[0] != '\0')
6979 {
6980 ContinueString(pStr);
6981 }
6982}
6983
6984void VmaJsonWriter::ContinueString(const char* pStr)
6985{
6986 VMA_ASSERT(m_InsideString);
6987
6988 const size_t strLen = strlen(pStr);
6989 for(size_t i = 0; i < strLen; ++i)
6990 {
6991 char ch = pStr[i];
6992 if(ch == '\\')
6993 {
6994 m_SB.Add("\\\\");
6995 }
6996 else if(ch == '"')
6997 {
6998 m_SB.Add("\\\"");
6999 }
7000 else if(ch >= 32)
7001 {
7002 m_SB.Add(ch);
7003 }
7004 else switch(ch)
7005 {
7006 case '\b':
7007 m_SB.Add("\\b");
7008 break;
7009 case '\f':
7010 m_SB.Add("\\f");
7011 break;
7012 case '\n':
7013 m_SB.Add("\\n");
7014 break;
7015 case '\r':
7016 m_SB.Add("\\r");
7017 break;
7018 case '\t':
7019 m_SB.Add("\\t");
7020 break;
7021 default:
7022 VMA_ASSERT(0 && "Character not currently supported.");
7023 break;
7024 }
7025 }
7026}
7027
7028void VmaJsonWriter::ContinueString(uint32_t n)
7029{
7030 VMA_ASSERT(m_InsideString);
7031 m_SB.AddNumber(n);
7032}
7033
7034void VmaJsonWriter::ContinueString(uint64_t n)
7035{
7036 VMA_ASSERT(m_InsideString);
7037 m_SB.AddNumber(n);
7038}
7039
7040void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7041{
7042 VMA_ASSERT(m_InsideString);
7043 m_SB.AddPointer(ptr);
7044}
7045
7046void VmaJsonWriter::EndString(const char* pStr)
7047{
7048 VMA_ASSERT(m_InsideString);
7049 if(pStr != VMA_NULL && pStr[0] != '\0')
7050 {
7051 ContinueString(pStr);
7052 }
7053 m_SB.Add('"');
7054 m_InsideString = false;
7055}
7056
7057void VmaJsonWriter::WriteNumber(uint32_t n)
7058{
7059 VMA_ASSERT(!m_InsideString);
7060 BeginValue(false);
7061 m_SB.AddNumber(n);
7062}
7063
7064void VmaJsonWriter::WriteNumber(uint64_t n)
7065{
7066 VMA_ASSERT(!m_InsideString);
7067 BeginValue(false);
7068 m_SB.AddNumber(n);
7069}
7070
7071void VmaJsonWriter::WriteBool(bool b)
7072{
7073 VMA_ASSERT(!m_InsideString);
7074 BeginValue(false);
7075 m_SB.Add(b ? "true" : "false");
7076}
7077
7078void VmaJsonWriter::WriteNull()
7079{
7080 VMA_ASSERT(!m_InsideString);
7081 BeginValue(false);
7082 m_SB.Add("null");
7083}
7084
7085void VmaJsonWriter::BeginValue(bool isString)
7086{
7087 if(!m_Stack.empty())
7088 {
7089 StackItem& currItem = m_Stack.back();
7090 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7091 currItem.valueCount % 2 == 0)
7092 {
7093 VMA_ASSERT(isString);
7094 }
7095
7096 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7097 currItem.valueCount % 2 != 0)
7098 {
7099 m_SB.Add(": ");
7100 }
7101 else if(currItem.valueCount > 0)
7102 {
7103 m_SB.Add(", ");
7104 WriteIndent();
7105 }
7106 else
7107 {
7108 WriteIndent();
7109 }
7110 ++currItem.valueCount;
7111 }
7112}
7113
7114void VmaJsonWriter::WriteIndent(bool oneLess)
7115{
7116 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7117 {
7118 m_SB.AddNewLine();
7119
7120 size_t count = m_Stack.size();
7121 if(count > 0 && oneLess)
7122 {
7123 --count;
7124 }
7125 for(size_t i = 0; i < count; ++i)
7126 {
7127 m_SB.Add(INDENT);
7128 }
7129 }
7130}
7131
7132#endif // #if VMA_STATS_STRING_ENABLED
7133
7134////////////////////////////////////////////////////////////////////////////////
7135
7136void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7137{
7138 if(IsUserDataString())
7139 {
7140 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7141
7142 FreeUserDataString(hAllocator);
7143
7144 if(pUserData != VMA_NULL)
7145 {
7146 const char* const newStrSrc = (char*)pUserData;
7147 const size_t newStrLen = strlen(newStrSrc);
7148 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7149 memcpy(newStrDst, newStrSrc, newStrLen + 1);
7150 m_pUserData = newStrDst;
7151 }
7152 }
7153 else
7154 {
7155 m_pUserData = pUserData;
7156 }
7157}
7158
7159void VmaAllocation_T::ChangeBlockAllocation(
7160 VmaAllocator hAllocator,
7161 VmaDeviceMemoryBlock* block,
7162 VkDeviceSize offset)
7163{
7164 VMA_ASSERT(block != VMA_NULL);
7165 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7166
7167 // Move mapping reference counter from old block to new block.
7168 if(block != m_BlockAllocation.m_Block)
7169 {
7170 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7171 if(IsPersistentMap())
7172 ++mapRefCount;
7173 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7174 block->Map(hAllocator, mapRefCount, VMA_NULL);
7175 }
7176
7177 m_BlockAllocation.m_Block = block;
7178 m_BlockAllocation.m_Offset = offset;
7179}
7180
7181void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7182{
7183 VMA_ASSERT(newSize > 0);
7184 m_Size = newSize;
7185}
7186
7187void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7188{
7189 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7190 m_BlockAllocation.m_Offset = newOffset;
7191}
7192
7193VkDeviceSize VmaAllocation_T::GetOffset() const
7194{
7195 switch(m_Type)
7196 {
7197 case ALLOCATION_TYPE_BLOCK:
7198 return m_BlockAllocation.m_Offset;
7199 case ALLOCATION_TYPE_DEDICATED:
7200 return 0;
7201 default:
7202 VMA_ASSERT(0);
7203 return 0;
7204 }
7205}
7206
7207VkDeviceMemory VmaAllocation_T::GetMemory() const
7208{
7209 switch(m_Type)
7210 {
7211 case ALLOCATION_TYPE_BLOCK:
7212 return m_BlockAllocation.m_Block->GetDeviceMemory();
7213 case ALLOCATION_TYPE_DEDICATED:
7214 return m_DedicatedAllocation.m_hMemory;
7215 default:
7216 VMA_ASSERT(0);
7217 return VK_NULL_HANDLE;
7218 }
7219}
7220
7221uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7222{
7223 switch(m_Type)
7224 {
7225 case ALLOCATION_TYPE_BLOCK:
7226 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7227 case ALLOCATION_TYPE_DEDICATED:
7228 return m_DedicatedAllocation.m_MemoryTypeIndex;
7229 default:
7230 VMA_ASSERT(0);
7231 return UINT32_MAX;
7232 }
7233}
7234
7235void* VmaAllocation_T::GetMappedData() const
7236{
7237 switch(m_Type)
7238 {
7239 case ALLOCATION_TYPE_BLOCK:
7240 if(m_MapCount != 0)
7241 {
7242 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7243 VMA_ASSERT(pBlockData != VMA_NULL);
7244 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7245 }
7246 else
7247 {
7248 return VMA_NULL;
7249 }
7250 break;
7251 case ALLOCATION_TYPE_DEDICATED:
7252 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7253 return m_DedicatedAllocation.m_pMappedData;
7254 default:
7255 VMA_ASSERT(0);
7256 return VMA_NULL;
7257 }
7258}
7259
7260bool VmaAllocation_T::CanBecomeLost() const
7261{
7262 switch(m_Type)
7263 {
7264 case ALLOCATION_TYPE_BLOCK:
7265 return m_BlockAllocation.m_CanBecomeLost;
7266 case ALLOCATION_TYPE_DEDICATED:
7267 return false;
7268 default:
7269 VMA_ASSERT(0);
7270 return false;
7271 }
7272}
7273
7274VmaPool VmaAllocation_T::GetPool() const
7275{
7276 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7277 return m_BlockAllocation.m_hPool;
7278}
7279
7280bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7281{
7282 VMA_ASSERT(CanBecomeLost());
7283
7284 /*
7285 Warning: This is a carefully designed algorithm.
7286 Do not modify unless you really know what you're doing :)
7287 */
7288 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7289 for(;;)
7290 {
7291 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7292 {
7293 VMA_ASSERT(0);
7294 return false;
7295 }
7296 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7297 {
7298 return false;
7299 }
7300 else // Last use time earlier than current time.
7301 {
7302 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7303 {
7304 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7305 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7306 return true;
7307 }
7308 }
7309 }
7310}
7311
7312#if VMA_STATS_STRING_ENABLED
7313
7314// Correspond to values of enum VmaSuballocationType.
7315static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7316 "FREE",
7317 "UNKNOWN",
7318 "BUFFER",
7319 "IMAGE_UNKNOWN",
7320 "IMAGE_LINEAR",
7321 "IMAGE_OPTIMAL",
7322};
7323
7324void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7325{
7326 json.WriteString("Type");
7327 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7328
7329 json.WriteString("Size");
7330 json.WriteNumber(m_Size);
7331
7332 if(m_pUserData != VMA_NULL)
7333 {
7334 json.WriteString("UserData");
7335 if(IsUserDataString())
7336 {
7337 json.WriteString((const char*)m_pUserData);
7338 }
7339 else
7340 {
7341 json.BeginString();
7342 json.ContinueString_Pointer(m_pUserData);
7343 json.EndString();
7344 }
7345 }
7346
7347 json.WriteString("CreationFrameIndex");
7348 json.WriteNumber(m_CreationFrameIndex);
7349
7350 json.WriteString("LastUseFrameIndex");
7351 json.WriteNumber(GetLastUseFrameIndex());
7352
7353 if(m_BufferImageUsage != 0)
7354 {
7355 json.WriteString("Usage");
7356 json.WriteNumber(m_BufferImageUsage);
7357 }
7358}
7359
7360#endif
7361
7362void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7363{
7364 VMA_ASSERT(IsUserDataString());
7365 if(m_pUserData != VMA_NULL)
7366 {
7367 char* const oldStr = (char*)m_pUserData;
7368 const size_t oldStrLen = strlen(oldStr);
7369 vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7370 m_pUserData = VMA_NULL;
7371 }
7372}
7373
7374void VmaAllocation_T::BlockAllocMap()
7375{
7376 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7377
7378 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7379 {
7380 ++m_MapCount;
7381 }
7382 else
7383 {
7384 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7385 }
7386}
7387
7388void VmaAllocation_T::BlockAllocUnmap()
7389{
7390 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7391
7392 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7393 {
7394 --m_MapCount;
7395 }
7396 else
7397 {
7398 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7399 }
7400}
7401
7402VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7403{
7404 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7405
7406 if(m_MapCount != 0)
7407 {
7408 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7409 {
7410 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7411 *ppData = m_DedicatedAllocation.m_pMappedData;
7412 ++m_MapCount;
7413 return VK_SUCCESS;
7414 }
7415 else
7416 {
7417 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7418 return VK_ERROR_MEMORY_MAP_FAILED;
7419 }
7420 }
7421 else
7422 {
7423 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7424 hAllocator->m_hDevice,
7425 m_DedicatedAllocation.m_hMemory,
7426 0, // offset
7427 VK_WHOLE_SIZE,
7428 0, // flags
7429 ppData);
7430 if(result == VK_SUCCESS)
7431 {
7432 m_DedicatedAllocation.m_pMappedData = *ppData;
7433 m_MapCount = 1;
7434 }
7435 return result;
7436 }
7437}
7438
7439void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7440{
7441 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7442
7443 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7444 {
7445 --m_MapCount;
7446 if(m_MapCount == 0)
7447 {
7448 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7449 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7450 hAllocator->m_hDevice,
7451 m_DedicatedAllocation.m_hMemory);
7452 }
7453 }
7454 else
7455 {
7456 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7457 }
7458}
7459
7460#if VMA_STATS_STRING_ENABLED
7461
7462static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7463{
7464 json.BeginObject();
7465
7466 json.WriteString("Blocks");
7467 json.WriteNumber(stat.blockCount);
7468
7469 json.WriteString("Allocations");
7470 json.WriteNumber(stat.allocationCount);
7471
7472 json.WriteString("UnusedRanges");
7473 json.WriteNumber(stat.unusedRangeCount);
7474
7475 json.WriteString("UsedBytes");
7476 json.WriteNumber(stat.usedBytes);
7477
7478 json.WriteString("UnusedBytes");
7479 json.WriteNumber(stat.unusedBytes);
7480
7481 if(stat.allocationCount > 1)
7482 {
7483 json.WriteString("AllocationSize");
7484 json.BeginObject(true);
7485 json.WriteString("Min");
7486 json.WriteNumber(stat.allocationSizeMin);
7487 json.WriteString("Avg");
7488 json.WriteNumber(stat.allocationSizeAvg);
7489 json.WriteString("Max");
7490 json.WriteNumber(stat.allocationSizeMax);
7491 json.EndObject();
7492 }
7493
7494 if(stat.unusedRangeCount > 1)
7495 {
7496 json.WriteString("UnusedRangeSize");
7497 json.BeginObject(true);
7498 json.WriteString("Min");
7499 json.WriteNumber(stat.unusedRangeSizeMin);
7500 json.WriteString("Avg");
7501 json.WriteNumber(stat.unusedRangeSizeAvg);
7502 json.WriteString("Max");
7503 json.WriteNumber(stat.unusedRangeSizeMax);
7504 json.EndObject();
7505 }
7506
7507 json.EndObject();
7508}
7509
7510#endif // #if VMA_STATS_STRING_ENABLED
7511
7512struct VmaSuballocationItemSizeLess
7513{
7514 bool operator()(
7515 const VmaSuballocationList::iterator lhs,
7516 const VmaSuballocationList::iterator rhs) const
7517 {
7518 return lhs->size < rhs->size;
7519 }
7520 bool operator()(
7521 const VmaSuballocationList::iterator lhs,
7522 VkDeviceSize rhsSize) const
7523 {
7524 return lhs->size < rhsSize;
7525 }
7526};
7527
7528
7529////////////////////////////////////////////////////////////////////////////////
7530// class VmaBlockMetadata
7531
7532VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7533 m_Size(0),
7534 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7535{
7536}
7537
7538#if VMA_STATS_STRING_ENABLED
7539
7540void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7541 VkDeviceSize unusedBytes,
7542 size_t allocationCount,
7543 size_t unusedRangeCount) const
7544{
7545 json.BeginObject();
7546
7547 json.WriteString("TotalBytes");
7548 json.WriteNumber(GetSize());
7549
7550 json.WriteString("UnusedBytes");
7551 json.WriteNumber(unusedBytes);
7552
7553 json.WriteString("Allocations");
7554 json.WriteNumber((uint64_t)allocationCount);
7555
7556 json.WriteString("UnusedRanges");
7557 json.WriteNumber((uint64_t)unusedRangeCount);
7558
7559 json.WriteString("Suballocations");
7560 json.BeginArray();
7561}
7562
7563void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7564 VkDeviceSize offset,
7565 VmaAllocation hAllocation) const
7566{
7567 json.BeginObject(true);
7568
7569 json.WriteString("Offset");
7570 json.WriteNumber(offset);
7571
7572 hAllocation->PrintParameters(json);
7573
7574 json.EndObject();
7575}
7576
7577void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7578 VkDeviceSize offset,
7579 VkDeviceSize size) const
7580{
7581 json.BeginObject(true);
7582
7583 json.WriteString("Offset");
7584 json.WriteNumber(offset);
7585
7586 json.WriteString("Type");
7587 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7588
7589 json.WriteString("Size");
7590 json.WriteNumber(size);
7591
7592 json.EndObject();
7593}
7594
7595void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7596{
7597 json.EndArray();
7598 json.EndObject();
7599}
7600
7601#endif // #if VMA_STATS_STRING_ENABLED
7602
7603////////////////////////////////////////////////////////////////////////////////
7604// class VmaBlockMetadata_Generic
7605
7606VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7607 VmaBlockMetadata(hAllocator),
7608 m_FreeCount(0),
7609 m_SumFreeSize(0),
7610 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7611 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7612{
7613}
7614
7615VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7616{
7617}
7618
7619void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7620{
7621 VmaBlockMetadata::Init(size);
7622
7623 m_FreeCount = 1;
7624 m_SumFreeSize = size;
7625
7626 VmaSuballocation suballoc = {};
7627 suballoc.offset = 0;
7628 suballoc.size = size;
7629 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7630 suballoc.hAllocation = VK_NULL_HANDLE;
7631
7632 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7633 m_Suballocations.push_back(suballoc);
7634 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7635 --suballocItem;
7636 m_FreeSuballocationsBySize.push_back(suballocItem);
7637}
7638
7639bool VmaBlockMetadata_Generic::Validate() const
7640{
7641 VMA_VALIDATE(!m_Suballocations.empty());
7642
7643 // Expected offset of new suballocation as calculated from previous ones.
7644 VkDeviceSize calculatedOffset = 0;
7645 // Expected number of free suballocations as calculated from traversing their list.
7646 uint32_t calculatedFreeCount = 0;
7647 // Expected sum size of free suballocations as calculated from traversing their list.
7648 VkDeviceSize calculatedSumFreeSize = 0;
7649 // Expected number of free suballocations that should be registered in
7650 // m_FreeSuballocationsBySize calculated from traversing their list.
7651 size_t freeSuballocationsToRegister = 0;
7652 // True if previous visited suballocation was free.
7653 bool prevFree = false;
7654
7655 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7656 suballocItem != m_Suballocations.cend();
7657 ++suballocItem)
7658 {
7659 const VmaSuballocation& subAlloc = *suballocItem;
7660
7661 // Actual offset of this suballocation doesn't match expected one.
7662 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7663
7664 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7665 // Two adjacent free suballocations are invalid. They should be merged.
7666 VMA_VALIDATE(!prevFree || !currFree);
7667
7668 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7669
7670 if(currFree)
7671 {
7672 calculatedSumFreeSize += subAlloc.size;
7673 ++calculatedFreeCount;
7674 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7675 {
7676 ++freeSuballocationsToRegister;
7677 }
7678
7679 // Margin required between allocations - every free space must be at least that large.
7680 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7681 }
7682 else
7683 {
7684 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7685 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7686
7687 // Margin required between allocations - previous allocation must be free.
7688 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7689 }
7690
7691 calculatedOffset += subAlloc.size;
7692 prevFree = currFree;
7693 }
7694
7695 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7696 // match expected one.
7697 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7698
7699 VkDeviceSize lastSize = 0;
7700 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7701 {
7702 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7703
7704 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7705 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7706 // They must be sorted by size ascending.
7707 VMA_VALIDATE(suballocItem->size >= lastSize);
7708
7709 lastSize = suballocItem->size;
7710 }
7711
7712 // Check if totals match calculacted values.
7713 VMA_VALIDATE(ValidateFreeSuballocationList());
7714 VMA_VALIDATE(calculatedOffset == GetSize());
7715 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7716 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7717
7718 return true;
7719}
7720
7721VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7722{
7723 if(!m_FreeSuballocationsBySize.empty())
7724 {
7725 return m_FreeSuballocationsBySize.back()->size;
7726 }
7727 else
7728 {
7729 return 0;
7730 }
7731}
7732
7733bool VmaBlockMetadata_Generic::IsEmpty() const
7734{
7735 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7736}
7737
7738void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7739{
7740 outInfo.blockCount = 1;
7741
7742 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7743 outInfo.allocationCount = rangeCount - m_FreeCount;
7744 outInfo.unusedRangeCount = m_FreeCount;
7745
7746 outInfo.unusedBytes = m_SumFreeSize;
7747 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7748
7749 outInfo.allocationSizeMin = UINT64_MAX;
7750 outInfo.allocationSizeMax = 0;
7751 outInfo.unusedRangeSizeMin = UINT64_MAX;
7752 outInfo.unusedRangeSizeMax = 0;
7753
7754 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7755 suballocItem != m_Suballocations.cend();
7756 ++suballocItem)
7757 {
7758 const VmaSuballocation& suballoc = *suballocItem;
7759 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7760 {
7761 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7762 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7763 }
7764 else
7765 {
7766 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7767 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7768 }
7769 }
7770}
7771
7772void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7773{
7774 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7775
7776 inoutStats.size += GetSize();
7777 inoutStats.unusedSize += m_SumFreeSize;
7778 inoutStats.allocationCount += rangeCount - m_FreeCount;
7779 inoutStats.unusedRangeCount += m_FreeCount;
7780 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7781}
7782
7783#if VMA_STATS_STRING_ENABLED
7784
7785void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7786{
7787 PrintDetailedMap_Begin(json,
7788 m_SumFreeSize, // unusedBytes
7789 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7790 m_FreeCount); // unusedRangeCount
7791
7792 size_t i = 0;
7793 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7794 suballocItem != m_Suballocations.cend();
7795 ++suballocItem, ++i)
7796 {
7797 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7798 {
7799 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7800 }
7801 else
7802 {
7803 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7804 }
7805 }
7806
7807 PrintDetailedMap_End(json);
7808}
7809
7810#endif // #if VMA_STATS_STRING_ENABLED
7811
7812bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7813 uint32_t currentFrameIndex,
7814 uint32_t frameInUseCount,
7815 VkDeviceSize bufferImageGranularity,
7816 VkDeviceSize allocSize,
7817 VkDeviceSize allocAlignment,
7818 bool upperAddress,
7819 VmaSuballocationType allocType,
7820 bool canMakeOtherLost,
7821 uint32_t strategy,
7822 VmaAllocationRequest* pAllocationRequest)
7823{
7824 VMA_ASSERT(allocSize > 0);
7825 VMA_ASSERT(!upperAddress);
7826 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7827 VMA_ASSERT(pAllocationRequest != VMA_NULL);
7828 VMA_HEAVY_ASSERT(Validate());
7829
7830 // There is not enough total free space in this block to fullfill the request: Early return.
7831 if(canMakeOtherLost == false &&
7832 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7833 {
7834 return false;
7835 }
7836
7837 // New algorithm, efficiently searching freeSuballocationsBySize.
7838 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7839 if(freeSuballocCount > 0)
7840 {
7841 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7842 {
7843 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7844 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7845 m_FreeSuballocationsBySize.data(),
7846 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7847 allocSize + 2 * VMA_DEBUG_MARGIN,
7848 VmaSuballocationItemSizeLess());
7849 size_t index = it - m_FreeSuballocationsBySize.data();
7850 for(; index < freeSuballocCount; ++index)
7851 {
7852 if(CheckAllocation(
7853 currentFrameIndex,
7854 frameInUseCount,
7855 bufferImageGranularity,
7856 allocSize,
7857 allocAlignment,
7858 allocType,
7859 m_FreeSuballocationsBySize[index],
7860 false, // canMakeOtherLost
7861 &pAllocationRequest->offset,
7862 &pAllocationRequest->itemsToMakeLostCount,
7863 &pAllocationRequest->sumFreeSize,
7864 &pAllocationRequest->sumItemSize))
7865 {
7866 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7867 return true;
7868 }
7869 }
7870 }
7871 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7872 {
7873 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7874 it != m_Suballocations.end();
7875 ++it)
7876 {
7877 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7878 currentFrameIndex,
7879 frameInUseCount,
7880 bufferImageGranularity,
7881 allocSize,
7882 allocAlignment,
7883 allocType,
7884 it,
7885 false, // canMakeOtherLost
7886 &pAllocationRequest->offset,
7887 &pAllocationRequest->itemsToMakeLostCount,
7888 &pAllocationRequest->sumFreeSize,
7889 &pAllocationRequest->sumItemSize))
7890 {
7891 pAllocationRequest->item = it;
7892 return true;
7893 }
7894 }
7895 }
7896 else // WORST_FIT, FIRST_FIT
7897 {
7898 // Search staring from biggest suballocations.
7899 for(size_t index = freeSuballocCount; index--; )
7900 {
7901 if(CheckAllocation(
7902 currentFrameIndex,
7903 frameInUseCount,
7904 bufferImageGranularity,
7905 allocSize,
7906 allocAlignment,
7907 allocType,
7908 m_FreeSuballocationsBySize[index],
7909 false, // canMakeOtherLost
7910 &pAllocationRequest->offset,
7911 &pAllocationRequest->itemsToMakeLostCount,
7912 &pAllocationRequest->sumFreeSize,
7913 &pAllocationRequest->sumItemSize))
7914 {
7915 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7916 return true;
7917 }
7918 }
7919 }
7920 }
7921
7922 if(canMakeOtherLost)
7923 {
7924 // Brute-force algorithm. TODO: Come up with something better.
7925
7926 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7927 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7928
7929 VmaAllocationRequest tmpAllocRequest = {};
7930 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7931 suballocIt != m_Suballocations.end();
7932 ++suballocIt)
7933 {
7934 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7935 suballocIt->hAllocation->CanBecomeLost())
7936 {
7937 if(CheckAllocation(
7938 currentFrameIndex,
7939 frameInUseCount,
7940 bufferImageGranularity,
7941 allocSize,
7942 allocAlignment,
7943 allocType,
7944 suballocIt,
7945 canMakeOtherLost,
7946 &tmpAllocRequest.offset,
7947 &tmpAllocRequest.itemsToMakeLostCount,
7948 &tmpAllocRequest.sumFreeSize,
7949 &tmpAllocRequest.sumItemSize))
7950 {
7951 tmpAllocRequest.item = suballocIt;
7952
7953 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7954 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7955 {
7956 *pAllocationRequest = tmpAllocRequest;
7957 }
7958 }
7959 }
7960 }
7961
7962 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7963 {
7964 return true;
7965 }
7966 }
7967
7968 return false;
7969}
7970
7971bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7972 uint32_t currentFrameIndex,
7973 uint32_t frameInUseCount,
7974 VmaAllocationRequest* pAllocationRequest)
7975{
7976 while(pAllocationRequest->itemsToMakeLostCount > 0)
7977 {
7978 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7979 {
7980 ++pAllocationRequest->item;
7981 }
7982 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7983 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
7984 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
7985 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7986 {
7987 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
7988 --pAllocationRequest->itemsToMakeLostCount;
7989 }
7990 else
7991 {
7992 return false;
7993 }
7994 }
7995
7996 VMA_HEAVY_ASSERT(Validate());
7997 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7998 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
7999
8000 return true;
8001}
8002
8003uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8004{
8005 uint32_t lostAllocationCount = 0;
8006 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8007 it != m_Suballocations.end();
8008 ++it)
8009 {
8010 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8011 it->hAllocation->CanBecomeLost() &&
8012 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8013 {
8014 it = FreeSuballocation(it);
8015 ++lostAllocationCount;
8016 }
8017 }
8018 return lostAllocationCount;
8019}
8020
8021VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8022{
8023 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8024 it != m_Suballocations.end();
8025 ++it)
8026 {
8027 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8028 {
8029 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8030 {
8031 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8032 return VK_ERROR_VALIDATION_FAILED_EXT;
8033 }
8034 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8035 {
8036 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8037 return VK_ERROR_VALIDATION_FAILED_EXT;
8038 }
8039 }
8040 }
8041
8042 return VK_SUCCESS;
8043}
8044
8045void VmaBlockMetadata_Generic::Alloc(
8046 const VmaAllocationRequest& request,
8047 VmaSuballocationType type,
8048 VkDeviceSize allocSize,
8049 bool upperAddress,
8050 VmaAllocation hAllocation)
8051{
8052 VMA_ASSERT(!upperAddress);
8053 VMA_ASSERT(request.item != m_Suballocations.end());
8054 VmaSuballocation& suballoc = *request.item;
8055 // Given suballocation is a free block.
8056 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8057 // Given offset is inside this suballocation.
8058 VMA_ASSERT(request.offset >= suballoc.offset);
8059 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8060 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8061 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8062
8063 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8064 // it to become used.
8065 UnregisterFreeSuballocation(request.item);
8066
8067 suballoc.offset = request.offset;
8068 suballoc.size = allocSize;
8069 suballoc.type = type;
8070 suballoc.hAllocation = hAllocation;
8071
8072 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8073 if(paddingEnd)
8074 {
8075 VmaSuballocation paddingSuballoc = {};
8076 paddingSuballoc.offset = request.offset + allocSize;
8077 paddingSuballoc.size = paddingEnd;
8078 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8079 VmaSuballocationList::iterator next = request.item;
8080 ++next;
8081 const VmaSuballocationList::iterator paddingEndItem =
8082 m_Suballocations.insert(next, paddingSuballoc);
8083 RegisterFreeSuballocation(paddingEndItem);
8084 }
8085
8086 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8087 if(paddingBegin)
8088 {
8089 VmaSuballocation paddingSuballoc = {};
8090 paddingSuballoc.offset = request.offset - paddingBegin;
8091 paddingSuballoc.size = paddingBegin;
8092 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8093 const VmaSuballocationList::iterator paddingBeginItem =
8094 m_Suballocations.insert(request.item, paddingSuballoc);
8095 RegisterFreeSuballocation(paddingBeginItem);
8096 }
8097
8098 // Update totals.
8099 m_FreeCount = m_FreeCount - 1;
8100 if(paddingBegin > 0)
8101 {
8102 ++m_FreeCount;
8103 }
8104 if(paddingEnd > 0)
8105 {
8106 ++m_FreeCount;
8107 }
8108 m_SumFreeSize -= allocSize;
8109}
8110
8111void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8112{
8113 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8114 suballocItem != m_Suballocations.end();
8115 ++suballocItem)
8116 {
8117 VmaSuballocation& suballoc = *suballocItem;
8118 if(suballoc.hAllocation == allocation)
8119 {
8120 FreeSuballocation(suballocItem);
8121 VMA_HEAVY_ASSERT(Validate());
8122 return;
8123 }
8124 }
8125 VMA_ASSERT(0 && "Not found!");
8126}
8127
8128void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8129{
8130 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8131 suballocItem != m_Suballocations.end();
8132 ++suballocItem)
8133 {
8134 VmaSuballocation& suballoc = *suballocItem;
8135 if(suballoc.offset == offset)
8136 {
8137 FreeSuballocation(suballocItem);
8138 return;
8139 }
8140 }
8141 VMA_ASSERT(0 && "Not found!");
8142}
8143
8144bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8145{
8146 typedef VmaSuballocationList::iterator iter_type;
8147 for(iter_type suballocItem = m_Suballocations.begin();
8148 suballocItem != m_Suballocations.end();
8149 ++suballocItem)
8150 {
8151 VmaSuballocation& suballoc = *suballocItem;
8152 if(suballoc.hAllocation == alloc)
8153 {
8154 iter_type nextItem = suballocItem;
8155 ++nextItem;
8156
8157 // Should have been ensured on higher level.
8158 VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8159
8160 // Shrinking.
8161 if(newSize < alloc->GetSize())
8162 {
8163 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8164
8165 // There is next item.
8166 if(nextItem != m_Suballocations.end())
8167 {
8168 // Next item is free.
8169 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8170 {
8171 // Grow this next item backward.
8172 UnregisterFreeSuballocation(nextItem);
8173 nextItem->offset -= sizeDiff;
8174 nextItem->size += sizeDiff;
8175 RegisterFreeSuballocation(nextItem);
8176 }
8177 // Next item is not free.
8178 else
8179 {
8180 // Create free item after current one.
8181 VmaSuballocation newFreeSuballoc;
8182 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8183 newFreeSuballoc.offset = suballoc.offset + newSize;
8184 newFreeSuballoc.size = sizeDiff;
8185 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8186 iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8187 RegisterFreeSuballocation(newFreeSuballocIt);
8188
8189 ++m_FreeCount;
8190 }
8191 }
8192 // This is the last item.
8193 else
8194 {
8195 // Create free item at the end.
8196 VmaSuballocation newFreeSuballoc;
8197 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8198 newFreeSuballoc.offset = suballoc.offset + newSize;
8199 newFreeSuballoc.size = sizeDiff;
8200 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8201 m_Suballocations.push_back(newFreeSuballoc);
8202
8203 iter_type newFreeSuballocIt = m_Suballocations.end();
8204 RegisterFreeSuballocation(--newFreeSuballocIt);
8205
8206 ++m_FreeCount;
8207 }
8208
8209 suballoc.size = newSize;
8210 m_SumFreeSize += sizeDiff;
8211 }
8212 // Growing.
8213 else
8214 {
8215 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8216
8217 // There is next item.
8218 if(nextItem != m_Suballocations.end())
8219 {
8220 // Next item is free.
8221 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8222 {
8223 // There is not enough free space, including margin.
8224 if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8225 {
8226 return false;
8227 }
8228
8229 // There is more free space than required.
8230 if(nextItem->size > sizeDiff)
8231 {
8232 // Move and shrink this next item.
8233 UnregisterFreeSuballocation(nextItem);
8234 nextItem->offset += sizeDiff;
8235 nextItem->size -= sizeDiff;
8236 RegisterFreeSuballocation(nextItem);
8237 }
8238 // There is exactly the amount of free space required.
8239 else
8240 {
8241 // Remove this next free item.
8242 UnregisterFreeSuballocation(nextItem);
8243 m_Suballocations.erase(nextItem);
8244 --m_FreeCount;
8245 }
8246 }
8247 // Next item is not free - there is no space to grow.
8248 else
8249 {
8250 return false;
8251 }
8252 }
8253 // This is the last item - there is no space to grow.
8254 else
8255 {
8256 return false;
8257 }
8258
8259 suballoc.size = newSize;
8260 m_SumFreeSize -= sizeDiff;
8261 }
8262
8263 // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8264 return true;
8265 }
8266 }
8267 VMA_ASSERT(0 && "Not found!");
8268 return false;
8269}
8270
8271bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8272{
8273 VkDeviceSize lastSize = 0;
8274 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8275 {
8276 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8277
8278 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8279 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8280 VMA_VALIDATE(it->size >= lastSize);
8281 lastSize = it->size;
8282 }
8283 return true;
8284}
8285
8286bool VmaBlockMetadata_Generic::CheckAllocation(
8287 uint32_t currentFrameIndex,
8288 uint32_t frameInUseCount,
8289 VkDeviceSize bufferImageGranularity,
8290 VkDeviceSize allocSize,
8291 VkDeviceSize allocAlignment,
8292 VmaSuballocationType allocType,
8293 VmaSuballocationList::const_iterator suballocItem,
8294 bool canMakeOtherLost,
8295 VkDeviceSize* pOffset,
8296 size_t* itemsToMakeLostCount,
8297 VkDeviceSize* pSumFreeSize,
8298 VkDeviceSize* pSumItemSize) const
8299{
8300 VMA_ASSERT(allocSize > 0);
8301 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8302 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8303 VMA_ASSERT(pOffset != VMA_NULL);
8304
8305 *itemsToMakeLostCount = 0;
8306 *pSumFreeSize = 0;
8307 *pSumItemSize = 0;
8308
8309 if(canMakeOtherLost)
8310 {
8311 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8312 {
8313 *pSumFreeSize = suballocItem->size;
8314 }
8315 else
8316 {
8317 if(suballocItem->hAllocation->CanBecomeLost() &&
8318 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8319 {
8320 ++*itemsToMakeLostCount;
8321 *pSumItemSize = suballocItem->size;
8322 }
8323 else
8324 {
8325 return false;
8326 }
8327 }
8328
8329 // Remaining size is too small for this request: Early return.
8330 if(GetSize() - suballocItem->offset < allocSize)
8331 {
8332 return false;
8333 }
8334
8335 // Start from offset equal to beginning of this suballocation.
8336 *pOffset = suballocItem->offset;
8337
8338 // Apply VMA_DEBUG_MARGIN at the beginning.
8339 if(VMA_DEBUG_MARGIN > 0)
8340 {
8341 *pOffset += VMA_DEBUG_MARGIN;
8342 }
8343
8344 // Apply alignment.
8345 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8346
8347 // Check previous suballocations for BufferImageGranularity conflicts.
8348 // Make bigger alignment if necessary.
8349 if(bufferImageGranularity > 1)
8350 {
8351 bool bufferImageGranularityConflict = false;
8352 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8353 while(prevSuballocItem != m_Suballocations.cbegin())
8354 {
8355 --prevSuballocItem;
8356 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8357 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8358 {
8359 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8360 {
8361 bufferImageGranularityConflict = true;
8362 break;
8363 }
8364 }
8365 else
8366 // Already on previous page.
8367 break;
8368 }
8369 if(bufferImageGranularityConflict)
8370 {
8371 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8372 }
8373 }
8374
8375 // Now that we have final *pOffset, check if we are past suballocItem.
8376 // If yes, return false - this function should be called for another suballocItem as starting point.
8377 if(*pOffset >= suballocItem->offset + suballocItem->size)
8378 {
8379 return false;
8380 }
8381
8382 // Calculate padding at the beginning based on current offset.
8383 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8384
8385 // Calculate required margin at the end.
8386 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8387
8388 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8389 // Another early return check.
8390 if(suballocItem->offset + totalSize > GetSize())
8391 {
8392 return false;
8393 }
8394
8395 // Advance lastSuballocItem until desired size is reached.
8396 // Update itemsToMakeLostCount.
8397 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8398 if(totalSize > suballocItem->size)
8399 {
8400 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8401 while(remainingSize > 0)
8402 {
8403 ++lastSuballocItem;
8404 if(lastSuballocItem == m_Suballocations.cend())
8405 {
8406 return false;
8407 }
8408 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8409 {
8410 *pSumFreeSize += lastSuballocItem->size;
8411 }
8412 else
8413 {
8414 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8415 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8416 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8417 {
8418 ++*itemsToMakeLostCount;
8419 *pSumItemSize += lastSuballocItem->size;
8420 }
8421 else
8422 {
8423 return false;
8424 }
8425 }
8426 remainingSize = (lastSuballocItem->size < remainingSize) ?
8427 remainingSize - lastSuballocItem->size : 0;
8428 }
8429 }
8430
8431 // Check next suballocations for BufferImageGranularity conflicts.
8432 // If conflict exists, we must mark more allocations lost or fail.
8433 if(bufferImageGranularity > 1)
8434 {
8435 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8436 ++nextSuballocItem;
8437 while(nextSuballocItem != m_Suballocations.cend())
8438 {
8439 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8440 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8441 {
8442 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8443 {
8444 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8445 if(nextSuballoc.hAllocation->CanBecomeLost() &&
8446 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8447 {
8448 ++*itemsToMakeLostCount;
8449 }
8450 else
8451 {
8452 return false;
8453 }
8454 }
8455 }
8456 else
8457 {
8458 // Already on next page.
8459 break;
8460 }
8461 ++nextSuballocItem;
8462 }
8463 }
8464 }
8465 else
8466 {
8467 const VmaSuballocation& suballoc = *suballocItem;
8468 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8469
8470 *pSumFreeSize = suballoc.size;
8471
8472 // Size of this suballocation is too small for this request: Early return.
8473 if(suballoc.size < allocSize)
8474 {
8475 return false;
8476 }
8477
8478 // Start from offset equal to beginning of this suballocation.
8479 *pOffset = suballoc.offset;
8480
8481 // Apply VMA_DEBUG_MARGIN at the beginning.
8482 if(VMA_DEBUG_MARGIN > 0)
8483 {
8484 *pOffset += VMA_DEBUG_MARGIN;
8485 }
8486
8487 // Apply alignment.
8488 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8489
8490 // Check previous suballocations for BufferImageGranularity conflicts.
8491 // Make bigger alignment if necessary.
8492 if(bufferImageGranularity > 1)
8493 {
8494 bool bufferImageGranularityConflict = false;
8495 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8496 while(prevSuballocItem != m_Suballocations.cbegin())
8497 {
8498 --prevSuballocItem;
8499 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8500 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8501 {
8502 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8503 {
8504 bufferImageGranularityConflict = true;
8505 break;
8506 }
8507 }
8508 else
8509 // Already on previous page.
8510 break;
8511 }
8512 if(bufferImageGranularityConflict)
8513 {
8514 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8515 }
8516 }
8517
8518 // Calculate padding at the beginning based on current offset.
8519 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8520
8521 // Calculate required margin at the end.
8522 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8523
8524 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8525 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8526 {
8527 return false;
8528 }
8529
8530 // Check next suballocations for BufferImageGranularity conflicts.
8531 // If conflict exists, allocation cannot be made here.
8532 if(bufferImageGranularity > 1)
8533 {
8534 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8535 ++nextSuballocItem;
8536 while(nextSuballocItem != m_Suballocations.cend())
8537 {
8538 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8539 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8540 {
8541 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8542 {
8543 return false;
8544 }
8545 }
8546 else
8547 {
8548 // Already on next page.
8549 break;
8550 }
8551 ++nextSuballocItem;
8552 }
8553 }
8554 }
8555
8556 // All tests passed: Success. pOffset is already filled.
8557 return true;
8558}
8559
8560void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8561{
8562 VMA_ASSERT(item != m_Suballocations.end());
8563 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8564
8565 VmaSuballocationList::iterator nextItem = item;
8566 ++nextItem;
8567 VMA_ASSERT(nextItem != m_Suballocations.end());
8568 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8569
8570 item->size += nextItem->size;
8571 --m_FreeCount;
8572 m_Suballocations.erase(nextItem);
8573}
8574
8575VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8576{
8577 // Change this suballocation to be marked as free.
8578 VmaSuballocation& suballoc = *suballocItem;
8579 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8580 suballoc.hAllocation = VK_NULL_HANDLE;
8581
8582 // Update totals.
8583 ++m_FreeCount;
8584 m_SumFreeSize += suballoc.size;
8585
8586 // Merge with previous and/or next suballocation if it's also free.
8587 bool mergeWithNext = false;
8588 bool mergeWithPrev = false;
8589
8590 VmaSuballocationList::iterator nextItem = suballocItem;
8591 ++nextItem;
8592 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8593 {
8594 mergeWithNext = true;
8595 }
8596
8597 VmaSuballocationList::iterator prevItem = suballocItem;
8598 if(suballocItem != m_Suballocations.begin())
8599 {
8600 --prevItem;
8601 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8602 {
8603 mergeWithPrev = true;
8604 }
8605 }
8606
8607 if(mergeWithNext)
8608 {
8609 UnregisterFreeSuballocation(nextItem);
8610 MergeFreeWithNext(suballocItem);
8611 }
8612
8613 if(mergeWithPrev)
8614 {
8615 UnregisterFreeSuballocation(prevItem);
8616 MergeFreeWithNext(prevItem);
8617 RegisterFreeSuballocation(prevItem);
8618 return prevItem;
8619 }
8620 else
8621 {
8622 RegisterFreeSuballocation(suballocItem);
8623 return suballocItem;
8624 }
8625}
8626
8627void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8628{
8629 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8630 VMA_ASSERT(item->size > 0);
8631
8632 // You may want to enable this validation at the beginning or at the end of
8633 // this function, depending on what do you want to check.
8634 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8635
8636 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8637 {
8638 if(m_FreeSuballocationsBySize.empty())
8639 {
8640 m_FreeSuballocationsBySize.push_back(item);
8641 }
8642 else
8643 {
8644 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8645 }
8646 }
8647
8648 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8649}
8650
8651
8652void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8653{
8654 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8655 VMA_ASSERT(item->size > 0);
8656
8657 // You may want to enable this validation at the beginning or at the end of
8658 // this function, depending on what do you want to check.
8659 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8660
8661 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8662 {
8663 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8664 m_FreeSuballocationsBySize.data(),
8665 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8666 item,
8667 VmaSuballocationItemSizeLess());
8668 for(size_t index = it - m_FreeSuballocationsBySize.data();
8669 index < m_FreeSuballocationsBySize.size();
8670 ++index)
8671 {
8672 if(m_FreeSuballocationsBySize[index] == item)
8673 {
8674 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8675 return;
8676 }
8677 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8678 }
8679 VMA_ASSERT(0 && "Not found.");
8680 }
8681
8682 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8683}
8684
8685bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8686 VkDeviceSize bufferImageGranularity,
8687 VmaSuballocationType& inOutPrevSuballocType) const
8688{
8689 if(bufferImageGranularity == 1 || IsEmpty())
8690 {
8691 return false;
8692 }
8693
8694 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8695 bool typeConflictFound = false;
8696 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8697 it != m_Suballocations.cend();
8698 ++it)
8699 {
8700 const VmaSuballocationType suballocType = it->type;
8701 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8702 {
8703 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8704 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8705 {
8706 typeConflictFound = true;
8707 }
8708 inOutPrevSuballocType = suballocType;
8709 }
8710 }
8711
8712 return typeConflictFound || minAlignment >= bufferImageGranularity;
8713}
8714
8715////////////////////////////////////////////////////////////////////////////////
8716// class VmaBlockMetadata_Linear
8717
8718VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8719 VmaBlockMetadata(hAllocator),
8720 m_SumFreeSize(0),
8721 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8722 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8723 m_1stVectorIndex(0),
8724 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8725 m_1stNullItemsBeginCount(0),
8726 m_1stNullItemsMiddleCount(0),
8727 m_2ndNullItemsCount(0)
8728{
8729}
8730
8731VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8732{
8733}
8734
8735void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8736{
8737 VmaBlockMetadata::Init(size);
8738 m_SumFreeSize = size;
8739}
8740
8741bool VmaBlockMetadata_Linear::Validate() const
8742{
8743 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8744 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8745
8746 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8747 VMA_VALIDATE(!suballocations1st.empty() ||
8748 suballocations2nd.empty() ||
8749 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8750
8751 if(!suballocations1st.empty())
8752 {
8753 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8754 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8755 // Null item at the end should be just pop_back().
8756 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8757 }
8758 if(!suballocations2nd.empty())
8759 {
8760 // Null item at the end should be just pop_back().
8761 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8762 }
8763
8764 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8765 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8766
8767 VkDeviceSize sumUsedSize = 0;
8768 const size_t suballoc1stCount = suballocations1st.size();
8769 VkDeviceSize offset = VMA_DEBUG_MARGIN;
8770
8771 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8772 {
8773 const size_t suballoc2ndCount = suballocations2nd.size();
8774 size_t nullItem2ndCount = 0;
8775 for(size_t i = 0; i < suballoc2ndCount; ++i)
8776 {
8777 const VmaSuballocation& suballoc = suballocations2nd[i];
8778 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8779
8780 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8781 VMA_VALIDATE(suballoc.offset >= offset);
8782
8783 if(!currFree)
8784 {
8785 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8786 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8787 sumUsedSize += suballoc.size;
8788 }
8789 else
8790 {
8791 ++nullItem2ndCount;
8792 }
8793
8794 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8795 }
8796
8797 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8798 }
8799
8800 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8801 {
8802 const VmaSuballocation& suballoc = suballocations1st[i];
8803 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8804 suballoc.hAllocation == VK_NULL_HANDLE);
8805 }
8806
8807 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8808
8809 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8810 {
8811 const VmaSuballocation& suballoc = suballocations1st[i];
8812 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8813
8814 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8815 VMA_VALIDATE(suballoc.offset >= offset);
8816 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8817
8818 if(!currFree)
8819 {
8820 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8821 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8822 sumUsedSize += suballoc.size;
8823 }
8824 else
8825 {
8826 ++nullItem1stCount;
8827 }
8828
8829 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8830 }
8831 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8832
8833 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8834 {
8835 const size_t suballoc2ndCount = suballocations2nd.size();
8836 size_t nullItem2ndCount = 0;
8837 for(size_t i = suballoc2ndCount; i--; )
8838 {
8839 const VmaSuballocation& suballoc = suballocations2nd[i];
8840 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8841
8842 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8843 VMA_VALIDATE(suballoc.offset >= offset);
8844
8845 if(!currFree)
8846 {
8847 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8848 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8849 sumUsedSize += suballoc.size;
8850 }
8851 else
8852 {
8853 ++nullItem2ndCount;
8854 }
8855
8856 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8857 }
8858
8859 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8860 }
8861
8862 VMA_VALIDATE(offset <= GetSize());
8863 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8864
8865 return true;
8866}
8867
8868size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8869{
8870 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8871 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8872}
8873
8874VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8875{
8876 const VkDeviceSize size = GetSize();
8877
8878 /*
8879 We don't consider gaps inside allocation vectors with freed allocations because
8880 they are not suitable for reuse in linear allocator. We consider only space that
8881 is available for new allocations.
8882 */
8883 if(IsEmpty())
8884 {
8885 return size;
8886 }
8887
8888 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8889
8890 switch(m_2ndVectorMode)
8891 {
8892 case SECOND_VECTOR_EMPTY:
8893 /*
8894 Available space is after end of 1st, as well as before beginning of 1st (which
8895 whould make it a ring buffer).
8896 */
8897 {
8898 const size_t suballocations1stCount = suballocations1st.size();
8899 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8900 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8901 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8902 return VMA_MAX(
8903 firstSuballoc.offset,
8904 size - (lastSuballoc.offset + lastSuballoc.size));
8905 }
8906 break;
8907
8908 case SECOND_VECTOR_RING_BUFFER:
8909 /*
8910 Available space is only between end of 2nd and beginning of 1st.
8911 */
8912 {
8913 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8914 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8915 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8916 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8917 }
8918 break;
8919
8920 case SECOND_VECTOR_DOUBLE_STACK:
8921 /*
8922 Available space is only between end of 1st and top of 2nd.
8923 */
8924 {
8925 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8926 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8927 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8928 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8929 }
8930 break;
8931
8932 default:
8933 VMA_ASSERT(0);
8934 return 0;
8935 }
8936}
8937
8938void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8939{
8940 const VkDeviceSize size = GetSize();
8941 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8942 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8943 const size_t suballoc1stCount = suballocations1st.size();
8944 const size_t suballoc2ndCount = suballocations2nd.size();
8945
8946 outInfo.blockCount = 1;
8947 outInfo.allocationCount = (uint32_t)GetAllocationCount();
8948 outInfo.unusedRangeCount = 0;
8949 outInfo.usedBytes = 0;
8950 outInfo.allocationSizeMin = UINT64_MAX;
8951 outInfo.allocationSizeMax = 0;
8952 outInfo.unusedRangeSizeMin = UINT64_MAX;
8953 outInfo.unusedRangeSizeMax = 0;
8954
8955 VkDeviceSize lastOffset = 0;
8956
8957 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8958 {
8959 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8960 size_t nextAlloc2ndIndex = 0;
8961 while(lastOffset < freeSpace2ndTo1stEnd)
8962 {
8963 // Find next non-null allocation or move nextAllocIndex to the end.
8964 while(nextAlloc2ndIndex < suballoc2ndCount &&
8965 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8966 {
8967 ++nextAlloc2ndIndex;
8968 }
8969
8970 // Found non-null allocation.
8971 if(nextAlloc2ndIndex < suballoc2ndCount)
8972 {
8973 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8974
8975 // 1. Process free space before this allocation.
8976 if(lastOffset < suballoc.offset)
8977 {
8978 // There is free space from lastOffset to suballoc.offset.
8979 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8980 ++outInfo.unusedRangeCount;
8981 outInfo.unusedBytes += unusedRangeSize;
8982 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8983 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8984 }
8985
8986 // 2. Process this allocation.
8987 // There is allocation with suballoc.offset, suballoc.size.
8988 outInfo.usedBytes += suballoc.size;
8989 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8990 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
8991
8992 // 3. Prepare for next iteration.
8993 lastOffset = suballoc.offset + suballoc.size;
8994 ++nextAlloc2ndIndex;
8995 }
8996 // We are at the end.
8997 else
8998 {
8999 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9000 if(lastOffset < freeSpace2ndTo1stEnd)
9001 {
9002 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9003 ++outInfo.unusedRangeCount;
9004 outInfo.unusedBytes += unusedRangeSize;
9005 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9006 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9007 }
9008
9009 // End of loop.
9010 lastOffset = freeSpace2ndTo1stEnd;
9011 }
9012 }
9013 }
9014
9015 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9016 const VkDeviceSize freeSpace1stTo2ndEnd =
9017 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9018 while(lastOffset < freeSpace1stTo2ndEnd)
9019 {
9020 // Find next non-null allocation or move nextAllocIndex to the end.
9021 while(nextAlloc1stIndex < suballoc1stCount &&
9022 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9023 {
9024 ++nextAlloc1stIndex;
9025 }
9026
9027 // Found non-null allocation.
9028 if(nextAlloc1stIndex < suballoc1stCount)
9029 {
9030 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9031
9032 // 1. Process free space before this allocation.
9033 if(lastOffset < suballoc.offset)
9034 {
9035 // There is free space from lastOffset to suballoc.offset.
9036 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9037 ++outInfo.unusedRangeCount;
9038 outInfo.unusedBytes += unusedRangeSize;
9039 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9040 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9041 }
9042
9043 // 2. Process this allocation.
9044 // There is allocation with suballoc.offset, suballoc.size.
9045 outInfo.usedBytes += suballoc.size;
9046 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9047 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9048
9049 // 3. Prepare for next iteration.
9050 lastOffset = suballoc.offset + suballoc.size;
9051 ++nextAlloc1stIndex;
9052 }
9053 // We are at the end.
9054 else
9055 {
9056 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9057 if(lastOffset < freeSpace1stTo2ndEnd)
9058 {
9059 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9060 ++outInfo.unusedRangeCount;
9061 outInfo.unusedBytes += unusedRangeSize;
9062 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9063 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9064 }
9065
9066 // End of loop.
9067 lastOffset = freeSpace1stTo2ndEnd;
9068 }
9069 }
9070
9071 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9072 {
9073 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9074 while(lastOffset < size)
9075 {
9076 // Find next non-null allocation or move nextAllocIndex to the end.
9077 while(nextAlloc2ndIndex != SIZE_MAX &&
9078 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9079 {
9080 --nextAlloc2ndIndex;
9081 }
9082
9083 // Found non-null allocation.
9084 if(nextAlloc2ndIndex != SIZE_MAX)
9085 {
9086 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9087
9088 // 1. Process free space before this allocation.
9089 if(lastOffset < suballoc.offset)
9090 {
9091 // There is free space from lastOffset to suballoc.offset.
9092 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9093 ++outInfo.unusedRangeCount;
9094 outInfo.unusedBytes += unusedRangeSize;
9095 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9096 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9097 }
9098
9099 // 2. Process this allocation.
9100 // There is allocation with suballoc.offset, suballoc.size.
9101 outInfo.usedBytes += suballoc.size;
9102 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9103 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9104
9105 // 3. Prepare for next iteration.
9106 lastOffset = suballoc.offset + suballoc.size;
9107 --nextAlloc2ndIndex;
9108 }
9109 // We are at the end.
9110 else
9111 {
9112 // There is free space from lastOffset to size.
9113 if(lastOffset < size)
9114 {
9115 const VkDeviceSize unusedRangeSize = size - lastOffset;
9116 ++outInfo.unusedRangeCount;
9117 outInfo.unusedBytes += unusedRangeSize;
9118 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9119 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9120 }
9121
9122 // End of loop.
9123 lastOffset = size;
9124 }
9125 }
9126 }
9127
9128 outInfo.unusedBytes = size - outInfo.usedBytes;
9129}
9130
9131void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9132{
9133 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9134 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9135 const VkDeviceSize size = GetSize();
9136 const size_t suballoc1stCount = suballocations1st.size();
9137 const size_t suballoc2ndCount = suballocations2nd.size();
9138
9139 inoutStats.size += size;
9140
9141 VkDeviceSize lastOffset = 0;
9142
9143 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9144 {
9145 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9146 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9147 while(lastOffset < freeSpace2ndTo1stEnd)
9148 {
9149 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9150 while(nextAlloc2ndIndex < suballoc2ndCount &&
9151 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9152 {
9153 ++nextAlloc2ndIndex;
9154 }
9155
9156 // Found non-null allocation.
9157 if(nextAlloc2ndIndex < suballoc2ndCount)
9158 {
9159 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9160
9161 // 1. Process free space before this allocation.
9162 if(lastOffset < suballoc.offset)
9163 {
9164 // There is free space from lastOffset to suballoc.offset.
9165 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9166 inoutStats.unusedSize += unusedRangeSize;
9167 ++inoutStats.unusedRangeCount;
9168 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9169 }
9170
9171 // 2. Process this allocation.
9172 // There is allocation with suballoc.offset, suballoc.size.
9173 ++inoutStats.allocationCount;
9174
9175 // 3. Prepare for next iteration.
9176 lastOffset = suballoc.offset + suballoc.size;
9177 ++nextAlloc2ndIndex;
9178 }
9179 // We are at the end.
9180 else
9181 {
9182 if(lastOffset < freeSpace2ndTo1stEnd)
9183 {
9184 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9185 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9186 inoutStats.unusedSize += unusedRangeSize;
9187 ++inoutStats.unusedRangeCount;
9188 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9189 }
9190
9191 // End of loop.
9192 lastOffset = freeSpace2ndTo1stEnd;
9193 }
9194 }
9195 }
9196
9197 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9198 const VkDeviceSize freeSpace1stTo2ndEnd =
9199 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9200 while(lastOffset < freeSpace1stTo2ndEnd)
9201 {
9202 // Find next non-null allocation or move nextAllocIndex to the end.
9203 while(nextAlloc1stIndex < suballoc1stCount &&
9204 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9205 {
9206 ++nextAlloc1stIndex;
9207 }
9208
9209 // Found non-null allocation.
9210 if(nextAlloc1stIndex < suballoc1stCount)
9211 {
9212 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9213
9214 // 1. Process free space before this allocation.
9215 if(lastOffset < suballoc.offset)
9216 {
9217 // There is free space from lastOffset to suballoc.offset.
9218 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9219 inoutStats.unusedSize += unusedRangeSize;
9220 ++inoutStats.unusedRangeCount;
9221 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9222 }
9223
9224 // 2. Process this allocation.
9225 // There is allocation with suballoc.offset, suballoc.size.
9226 ++inoutStats.allocationCount;
9227
9228 // 3. Prepare for next iteration.
9229 lastOffset = suballoc.offset + suballoc.size;
9230 ++nextAlloc1stIndex;
9231 }
9232 // We are at the end.
9233 else
9234 {
9235 if(lastOffset < freeSpace1stTo2ndEnd)
9236 {
9237 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9238 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9239 inoutStats.unusedSize += unusedRangeSize;
9240 ++inoutStats.unusedRangeCount;
9241 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9242 }
9243
9244 // End of loop.
9245 lastOffset = freeSpace1stTo2ndEnd;
9246 }
9247 }
9248
9249 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9250 {
9251 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9252 while(lastOffset < size)
9253 {
9254 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9255 while(nextAlloc2ndIndex != SIZE_MAX &&
9256 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9257 {
9258 --nextAlloc2ndIndex;
9259 }
9260
9261 // Found non-null allocation.
9262 if(nextAlloc2ndIndex != SIZE_MAX)
9263 {
9264 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9265
9266 // 1. Process free space before this allocation.
9267 if(lastOffset < suballoc.offset)
9268 {
9269 // There is free space from lastOffset to suballoc.offset.
9270 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9271 inoutStats.unusedSize += unusedRangeSize;
9272 ++inoutStats.unusedRangeCount;
9273 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9274 }
9275
9276 // 2. Process this allocation.
9277 // There is allocation with suballoc.offset, suballoc.size.
9278 ++inoutStats.allocationCount;
9279
9280 // 3. Prepare for next iteration.
9281 lastOffset = suballoc.offset + suballoc.size;
9282 --nextAlloc2ndIndex;
9283 }
9284 // We are at the end.
9285 else
9286 {
9287 if(lastOffset < size)
9288 {
9289 // There is free space from lastOffset to size.
9290 const VkDeviceSize unusedRangeSize = size - lastOffset;
9291 inoutStats.unusedSize += unusedRangeSize;
9292 ++inoutStats.unusedRangeCount;
9293 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9294 }
9295
9296 // End of loop.
9297 lastOffset = size;
9298 }
9299 }
9300 }
9301}
9302
9303#if VMA_STATS_STRING_ENABLED
9304void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9305{
9306 const VkDeviceSize size = GetSize();
9307 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9308 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9309 const size_t suballoc1stCount = suballocations1st.size();
9310 const size_t suballoc2ndCount = suballocations2nd.size();
9311
9312 // FIRST PASS
9313
9314 size_t unusedRangeCount = 0;
9315 VkDeviceSize usedBytes = 0;
9316
9317 VkDeviceSize lastOffset = 0;
9318
9319 size_t alloc2ndCount = 0;
9320 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9321 {
9322 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9323 size_t nextAlloc2ndIndex = 0;
9324 while(lastOffset < freeSpace2ndTo1stEnd)
9325 {
9326 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9327 while(nextAlloc2ndIndex < suballoc2ndCount &&
9328 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9329 {
9330 ++nextAlloc2ndIndex;
9331 }
9332
9333 // Found non-null allocation.
9334 if(nextAlloc2ndIndex < suballoc2ndCount)
9335 {
9336 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9337
9338 // 1. Process free space before this allocation.
9339 if(lastOffset < suballoc.offset)
9340 {
9341 // There is free space from lastOffset to suballoc.offset.
9342 ++unusedRangeCount;
9343 }
9344
9345 // 2. Process this allocation.
9346 // There is allocation with suballoc.offset, suballoc.size.
9347 ++alloc2ndCount;
9348 usedBytes += suballoc.size;
9349
9350 // 3. Prepare for next iteration.
9351 lastOffset = suballoc.offset + suballoc.size;
9352 ++nextAlloc2ndIndex;
9353 }
9354 // We are at the end.
9355 else
9356 {
9357 if(lastOffset < freeSpace2ndTo1stEnd)
9358 {
9359 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9360 ++unusedRangeCount;
9361 }
9362
9363 // End of loop.
9364 lastOffset = freeSpace2ndTo1stEnd;
9365 }
9366 }
9367 }
9368
9369 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9370 size_t alloc1stCount = 0;
9371 const VkDeviceSize freeSpace1stTo2ndEnd =
9372 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9373 while(lastOffset < freeSpace1stTo2ndEnd)
9374 {
9375 // Find next non-null allocation or move nextAllocIndex to the end.
9376 while(nextAlloc1stIndex < suballoc1stCount &&
9377 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9378 {
9379 ++nextAlloc1stIndex;
9380 }
9381
9382 // Found non-null allocation.
9383 if(nextAlloc1stIndex < suballoc1stCount)
9384 {
9385 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9386
9387 // 1. Process free space before this allocation.
9388 if(lastOffset < suballoc.offset)
9389 {
9390 // There is free space from lastOffset to suballoc.offset.
9391 ++unusedRangeCount;
9392 }
9393
9394 // 2. Process this allocation.
9395 // There is allocation with suballoc.offset, suballoc.size.
9396 ++alloc1stCount;
9397 usedBytes += suballoc.size;
9398
9399 // 3. Prepare for next iteration.
9400 lastOffset = suballoc.offset + suballoc.size;
9401 ++nextAlloc1stIndex;
9402 }
9403 // We are at the end.
9404 else
9405 {
9406 if(lastOffset < size)
9407 {
9408 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9409 ++unusedRangeCount;
9410 }
9411
9412 // End of loop.
9413 lastOffset = freeSpace1stTo2ndEnd;
9414 }
9415 }
9416
9417 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9418 {
9419 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9420 while(lastOffset < size)
9421 {
9422 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9423 while(nextAlloc2ndIndex != SIZE_MAX &&
9424 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9425 {
9426 --nextAlloc2ndIndex;
9427 }
9428
9429 // Found non-null allocation.
9430 if(nextAlloc2ndIndex != SIZE_MAX)
9431 {
9432 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9433
9434 // 1. Process free space before this allocation.
9435 if(lastOffset < suballoc.offset)
9436 {
9437 // There is free space from lastOffset to suballoc.offset.
9438 ++unusedRangeCount;
9439 }
9440
9441 // 2. Process this allocation.
9442 // There is allocation with suballoc.offset, suballoc.size.
9443 ++alloc2ndCount;
9444 usedBytes += suballoc.size;
9445
9446 // 3. Prepare for next iteration.
9447 lastOffset = suballoc.offset + suballoc.size;
9448 --nextAlloc2ndIndex;
9449 }
9450 // We are at the end.
9451 else
9452 {
9453 if(lastOffset < size)
9454 {
9455 // There is free space from lastOffset to size.
9456 ++unusedRangeCount;
9457 }
9458
9459 // End of loop.
9460 lastOffset = size;
9461 }
9462 }
9463 }
9464
9465 const VkDeviceSize unusedBytes = size - usedBytes;
9466 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9467
9468 // SECOND PASS
9469 lastOffset = 0;
9470
9471 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9472 {
9473 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9474 size_t nextAlloc2ndIndex = 0;
9475 while(lastOffset < freeSpace2ndTo1stEnd)
9476 {
9477 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9478 while(nextAlloc2ndIndex < suballoc2ndCount &&
9479 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9480 {
9481 ++nextAlloc2ndIndex;
9482 }
9483
9484 // Found non-null allocation.
9485 if(nextAlloc2ndIndex < suballoc2ndCount)
9486 {
9487 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9488
9489 // 1. Process free space before this allocation.
9490 if(lastOffset < suballoc.offset)
9491 {
9492 // There is free space from lastOffset to suballoc.offset.
9493 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9494 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9495 }
9496
9497 // 2. Process this allocation.
9498 // There is allocation with suballoc.offset, suballoc.size.
9499 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9500
9501 // 3. Prepare for next iteration.
9502 lastOffset = suballoc.offset + suballoc.size;
9503 ++nextAlloc2ndIndex;
9504 }
9505 // We are at the end.
9506 else
9507 {
9508 if(lastOffset < freeSpace2ndTo1stEnd)
9509 {
9510 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9511 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9512 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9513 }
9514
9515 // End of loop.
9516 lastOffset = freeSpace2ndTo1stEnd;
9517 }
9518 }
9519 }
9520
9521 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9522 while(lastOffset < freeSpace1stTo2ndEnd)
9523 {
9524 // Find next non-null allocation or move nextAllocIndex to the end.
9525 while(nextAlloc1stIndex < suballoc1stCount &&
9526 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9527 {
9528 ++nextAlloc1stIndex;
9529 }
9530
9531 // Found non-null allocation.
9532 if(nextAlloc1stIndex < suballoc1stCount)
9533 {
9534 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9535
9536 // 1. Process free space before this allocation.
9537 if(lastOffset < suballoc.offset)
9538 {
9539 // There is free space from lastOffset to suballoc.offset.
9540 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9541 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9542 }
9543
9544 // 2. Process this allocation.
9545 // There is allocation with suballoc.offset, suballoc.size.
9546 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9547
9548 // 3. Prepare for next iteration.
9549 lastOffset = suballoc.offset + suballoc.size;
9550 ++nextAlloc1stIndex;
9551 }
9552 // We are at the end.
9553 else
9554 {
9555 if(lastOffset < freeSpace1stTo2ndEnd)
9556 {
9557 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9558 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9559 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9560 }
9561
9562 // End of loop.
9563 lastOffset = freeSpace1stTo2ndEnd;
9564 }
9565 }
9566
9567 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9568 {
9569 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9570 while(lastOffset < size)
9571 {
9572 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9573 while(nextAlloc2ndIndex != SIZE_MAX &&
9574 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9575 {
9576 --nextAlloc2ndIndex;
9577 }
9578
9579 // Found non-null allocation.
9580 if(nextAlloc2ndIndex != SIZE_MAX)
9581 {
9582 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9583
9584 // 1. Process free space before this allocation.
9585 if(lastOffset < suballoc.offset)
9586 {
9587 // There is free space from lastOffset to suballoc.offset.
9588 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9589 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9590 }
9591
9592 // 2. Process this allocation.
9593 // There is allocation with suballoc.offset, suballoc.size.
9594 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9595
9596 // 3. Prepare for next iteration.
9597 lastOffset = suballoc.offset + suballoc.size;
9598 --nextAlloc2ndIndex;
9599 }
9600 // We are at the end.
9601 else
9602 {
9603 if(lastOffset < size)
9604 {
9605 // There is free space from lastOffset to size.
9606 const VkDeviceSize unusedRangeSize = size - lastOffset;
9607 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9608 }
9609
9610 // End of loop.
9611 lastOffset = size;
9612 }
9613 }
9614 }
9615
9616 PrintDetailedMap_End(json);
9617}
9618#endif // #if VMA_STATS_STRING_ENABLED
9619
9620bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9621 uint32_t currentFrameIndex,
9622 uint32_t frameInUseCount,
9623 VkDeviceSize bufferImageGranularity,
9624 VkDeviceSize allocSize,
9625 VkDeviceSize allocAlignment,
9626 bool upperAddress,
9627 VmaSuballocationType allocType,
9628 bool canMakeOtherLost,
9629 uint32_t strategy,
9630 VmaAllocationRequest* pAllocationRequest)
9631{
9632 VMA_ASSERT(allocSize > 0);
9633 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9634 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9635 VMA_HEAVY_ASSERT(Validate());
9636
9637 const VkDeviceSize size = GetSize();
9638 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9639 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9640
9641 if(upperAddress)
9642 {
9643 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9644 {
9645 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9646 return false;
9647 }
9648
9649 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9650 if(allocSize > size)
9651 {
9652 return false;
9653 }
9654 VkDeviceSize resultBaseOffset = size - allocSize;
9655 if(!suballocations2nd.empty())
9656 {
9657 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9658 resultBaseOffset = lastSuballoc.offset - allocSize;
9659 if(allocSize > lastSuballoc.offset)
9660 {
9661 return false;
9662 }
9663 }
9664
9665 // Start from offset equal to end of free space.
9666 VkDeviceSize resultOffset = resultBaseOffset;
9667
9668 // Apply VMA_DEBUG_MARGIN at the end.
9669 if(VMA_DEBUG_MARGIN > 0)
9670 {
9671 if(resultOffset < VMA_DEBUG_MARGIN)
9672 {
9673 return false;
9674 }
9675 resultOffset -= VMA_DEBUG_MARGIN;
9676 }
9677
9678 // Apply alignment.
9679 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9680
9681 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9682 // Make bigger alignment if necessary.
9683 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9684 {
9685 bool bufferImageGranularityConflict = false;
9686 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9687 {
9688 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9689 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9690 {
9691 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9692 {
9693 bufferImageGranularityConflict = true;
9694 break;
9695 }
9696 }
9697 else
9698 // Already on previous page.
9699 break;
9700 }
9701 if(bufferImageGranularityConflict)
9702 {
9703 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9704 }
9705 }
9706
9707 // There is enough free space.
9708 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9709 suballocations1st.back().offset + suballocations1st.back().size :
9710 0;
9711 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9712 {
9713 // Check previous suballocations for BufferImageGranularity conflicts.
9714 // If conflict exists, allocation cannot be made here.
9715 if(bufferImageGranularity > 1)
9716 {
9717 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9718 {
9719 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9720 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9721 {
9722 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9723 {
9724 return false;
9725 }
9726 }
9727 else
9728 {
9729 // Already on next page.
9730 break;
9731 }
9732 }
9733 }
9734
9735 // All tests passed: Success.
9736 pAllocationRequest->offset = resultOffset;
9737 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9738 pAllocationRequest->sumItemSize = 0;
9739 // pAllocationRequest->item unused.
9740 pAllocationRequest->itemsToMakeLostCount = 0;
9741 return true;
9742 }
9743 }
9744 else // !upperAddress
9745 {
9746 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9747 {
9748 // Try to allocate at the end of 1st vector.
9749
9750 VkDeviceSize resultBaseOffset = 0;
9751 if(!suballocations1st.empty())
9752 {
9753 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9754 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9755 }
9756
9757 // Start from offset equal to beginning of free space.
9758 VkDeviceSize resultOffset = resultBaseOffset;
9759
9760 // Apply VMA_DEBUG_MARGIN at the beginning.
9761 if(VMA_DEBUG_MARGIN > 0)
9762 {
9763 resultOffset += VMA_DEBUG_MARGIN;
9764 }
9765
9766 // Apply alignment.
9767 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9768
9769 // Check previous suballocations for BufferImageGranularity conflicts.
9770 // Make bigger alignment if necessary.
9771 if(bufferImageGranularity > 1 && !suballocations1st.empty())
9772 {
9773 bool bufferImageGranularityConflict = false;
9774 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9775 {
9776 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9777 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9778 {
9779 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9780 {
9781 bufferImageGranularityConflict = true;
9782 break;
9783 }
9784 }
9785 else
9786 // Already on previous page.
9787 break;
9788 }
9789 if(bufferImageGranularityConflict)
9790 {
9791 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9792 }
9793 }
9794
9795 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9796 suballocations2nd.back().offset : size;
9797
9798 // There is enough free space at the end after alignment.
9799 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9800 {
9801 // Check next suballocations for BufferImageGranularity conflicts.
9802 // If conflict exists, allocation cannot be made here.
9803 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9804 {
9805 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9806 {
9807 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9808 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9809 {
9810 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9811 {
9812 return false;
9813 }
9814 }
9815 else
9816 {
9817 // Already on previous page.
9818 break;
9819 }
9820 }
9821 }
9822
9823 // All tests passed: Success.
9824 pAllocationRequest->offset = resultOffset;
9825 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9826 pAllocationRequest->sumItemSize = 0;
9827 // pAllocationRequest->item unused.
9828 pAllocationRequest->itemsToMakeLostCount = 0;
9829 return true;
9830 }
9831 }
9832
9833 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9834 // beginning of 1st vector as the end of free space.
9835 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9836 {
9837 VMA_ASSERT(!suballocations1st.empty());
9838
9839 VkDeviceSize resultBaseOffset = 0;
9840 if(!suballocations2nd.empty())
9841 {
9842 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9843 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9844 }
9845
9846 // Start from offset equal to beginning of free space.
9847 VkDeviceSize resultOffset = resultBaseOffset;
9848
9849 // Apply VMA_DEBUG_MARGIN at the beginning.
9850 if(VMA_DEBUG_MARGIN > 0)
9851 {
9852 resultOffset += VMA_DEBUG_MARGIN;
9853 }
9854
9855 // Apply alignment.
9856 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9857
9858 // Check previous suballocations for BufferImageGranularity conflicts.
9859 // Make bigger alignment if necessary.
9860 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9861 {
9862 bool bufferImageGranularityConflict = false;
9863 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9864 {
9865 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9866 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9867 {
9868 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9869 {
9870 bufferImageGranularityConflict = true;
9871 break;
9872 }
9873 }
9874 else
9875 // Already on previous page.
9876 break;
9877 }
9878 if(bufferImageGranularityConflict)
9879 {
9880 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9881 }
9882 }
9883
9884 pAllocationRequest->itemsToMakeLostCount = 0;
9885 pAllocationRequest->sumItemSize = 0;
9886 size_t index1st = m_1stNullItemsBeginCount;
9887
9888 if(canMakeOtherLost)
9889 {
9890 while(index1st < suballocations1st.size() &&
9891 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9892 {
9893 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9894 const VmaSuballocation& suballoc = suballocations1st[index1st];
9895 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9896 {
9897 // No problem.
9898 }
9899 else
9900 {
9901 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9902 if(suballoc.hAllocation->CanBecomeLost() &&
9903 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9904 {
9905 ++pAllocationRequest->itemsToMakeLostCount;
9906 pAllocationRequest->sumItemSize += suballoc.size;
9907 }
9908 else
9909 {
9910 return false;
9911 }
9912 }
9913 ++index1st;
9914 }
9915
9916 // Check next suballocations for BufferImageGranularity conflicts.
9917 // If conflict exists, we must mark more allocations lost or fail.
9918 if(bufferImageGranularity > 1)
9919 {
9920 while(index1st < suballocations1st.size())
9921 {
9922 const VmaSuballocation& suballoc = suballocations1st[index1st];
9923 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9924 {
9925 if(suballoc.hAllocation != VK_NULL_HANDLE)
9926 {
9927 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9928 if(suballoc.hAllocation->CanBecomeLost() &&
9929 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9930 {
9931 ++pAllocationRequest->itemsToMakeLostCount;
9932 pAllocationRequest->sumItemSize += suballoc.size;
9933 }
9934 else
9935 {
9936 return false;
9937 }
9938 }
9939 }
9940 else
9941 {
9942 // Already on next page.
9943 break;
9944 }
9945 ++index1st;
9946 }
9947 }
9948 }
9949
9950 // There is enough free space at the end after alignment.
9951 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9952 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9953 {
9954 // Check next suballocations for BufferImageGranularity conflicts.
9955 // If conflict exists, allocation cannot be made here.
9956 if(bufferImageGranularity > 1)
9957 {
9958 for(size_t nextSuballocIndex = index1st;
9959 nextSuballocIndex < suballocations1st.size();
9960 nextSuballocIndex++)
9961 {
9962 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9963 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9964 {
9965 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9966 {
9967 return false;
9968 }
9969 }
9970 else
9971 {
9972 // Already on next page.
9973 break;
9974 }
9975 }
9976 }
9977
9978 // All tests passed: Success.
9979 pAllocationRequest->offset = resultOffset;
9980 pAllocationRequest->sumFreeSize =
9981 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
9982 - resultBaseOffset
9983 - pAllocationRequest->sumItemSize;
9984 // pAllocationRequest->item unused.
9985 return true;
9986 }
9987 }
9988 }
9989
9990 return false;
9991}
9992
9993bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
9994 uint32_t currentFrameIndex,
9995 uint32_t frameInUseCount,
9996 VmaAllocationRequest* pAllocationRequest)
9997{
9998 if(pAllocationRequest->itemsToMakeLostCount == 0)
9999 {
10000 return true;
10001 }
10002
10003 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10004
10005 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10006 size_t index1st = m_1stNullItemsBeginCount;
10007 size_t madeLostCount = 0;
10008 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10009 {
10010 VMA_ASSERT(index1st < suballocations1st.size());
10011 VmaSuballocation& suballoc = suballocations1st[index1st];
10012 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10013 {
10014 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10015 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10016 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10017 {
10018 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10019 suballoc.hAllocation = VK_NULL_HANDLE;
10020 m_SumFreeSize += suballoc.size;
10021 ++m_1stNullItemsMiddleCount;
10022 ++madeLostCount;
10023 }
10024 else
10025 {
10026 return false;
10027 }
10028 }
10029 ++index1st;
10030 }
10031
10032 CleanupAfterFree();
10033 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10034
10035 return true;
10036}
10037
10038uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10039{
10040 uint32_t lostAllocationCount = 0;
10041
10042 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10043 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10044 {
10045 VmaSuballocation& suballoc = suballocations1st[i];
10046 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10047 suballoc.hAllocation->CanBecomeLost() &&
10048 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10049 {
10050 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10051 suballoc.hAllocation = VK_NULL_HANDLE;
10052 ++m_1stNullItemsMiddleCount;
10053 m_SumFreeSize += suballoc.size;
10054 ++lostAllocationCount;
10055 }
10056 }
10057
10058 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10059 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10060 {
10061 VmaSuballocation& suballoc = suballocations2nd[i];
10062 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10063 suballoc.hAllocation->CanBecomeLost() &&
10064 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10065 {
10066 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10067 suballoc.hAllocation = VK_NULL_HANDLE;
10068 ++m_2ndNullItemsCount;
10069 ++lostAllocationCount;
10070 }
10071 }
10072
10073 if(lostAllocationCount)
10074 {
10075 CleanupAfterFree();
10076 }
10077
10078 return lostAllocationCount;
10079}
10080
10081VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10082{
10083 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10084 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10085 {
10086 const VmaSuballocation& suballoc = suballocations1st[i];
10087 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10088 {
10089 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10090 {
10091 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10092 return VK_ERROR_VALIDATION_FAILED_EXT;
10093 }
10094 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10095 {
10096 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10097 return VK_ERROR_VALIDATION_FAILED_EXT;
10098 }
10099 }
10100 }
10101
10102 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10103 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10104 {
10105 const VmaSuballocation& suballoc = suballocations2nd[i];
10106 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10107 {
10108 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10109 {
10110 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10111 return VK_ERROR_VALIDATION_FAILED_EXT;
10112 }
10113 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10114 {
10115 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10116 return VK_ERROR_VALIDATION_FAILED_EXT;
10117 }
10118 }
10119 }
10120
10121 return VK_SUCCESS;
10122}
10123
10124void VmaBlockMetadata_Linear::Alloc(
10125 const VmaAllocationRequest& request,
10126 VmaSuballocationType type,
10127 VkDeviceSize allocSize,
10128 bool upperAddress,
10129 VmaAllocation hAllocation)
10130{
10131 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10132
10133 if(upperAddress)
10134 {
10135 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10136 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10137 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10138 suballocations2nd.push_back(newSuballoc);
10139 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10140 }
10141 else
10142 {
10143 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10144
10145 // First allocation.
10146 if(suballocations1st.empty())
10147 {
10148 suballocations1st.push_back(newSuballoc);
10149 }
10150 else
10151 {
10152 // New allocation at the end of 1st vector.
10153 if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10154 {
10155 // Check if it fits before the end of the block.
10156 VMA_ASSERT(request.offset + allocSize <= GetSize());
10157 suballocations1st.push_back(newSuballoc);
10158 }
10159 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10160 else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10161 {
10162 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10163
10164 switch(m_2ndVectorMode)
10165 {
10166 case SECOND_VECTOR_EMPTY:
10167 // First allocation from second part ring buffer.
10168 VMA_ASSERT(suballocations2nd.empty());
10169 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10170 break;
10171 case SECOND_VECTOR_RING_BUFFER:
10172 // 2-part ring buffer is already started.
10173 VMA_ASSERT(!suballocations2nd.empty());
10174 break;
10175 case SECOND_VECTOR_DOUBLE_STACK:
10176 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10177 break;
10178 default:
10179 VMA_ASSERT(0);
10180 }
10181
10182 suballocations2nd.push_back(newSuballoc);
10183 }
10184 else
10185 {
10186 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10187 }
10188 }
10189 }
10190
10191 m_SumFreeSize -= newSuballoc.size;
10192}
10193
10194void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10195{
10196 FreeAtOffset(allocation->GetOffset());
10197}
10198
10199void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10200{
10201 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10202 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10203
10204 if(!suballocations1st.empty())
10205 {
10206 // First allocation: Mark it as next empty at the beginning.
10207 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10208 if(firstSuballoc.offset == offset)
10209 {
10210 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10211 firstSuballoc.hAllocation = VK_NULL_HANDLE;
10212 m_SumFreeSize += firstSuballoc.size;
10213 ++m_1stNullItemsBeginCount;
10214 CleanupAfterFree();
10215 return;
10216 }
10217 }
10218
10219 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10220 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10221 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10222 {
10223 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10224 if(lastSuballoc.offset == offset)
10225 {
10226 m_SumFreeSize += lastSuballoc.size;
10227 suballocations2nd.pop_back();
10228 CleanupAfterFree();
10229 return;
10230 }
10231 }
10232 // Last allocation in 1st vector.
10233 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10234 {
10235 VmaSuballocation& lastSuballoc = suballocations1st.back();
10236 if(lastSuballoc.offset == offset)
10237 {
10238 m_SumFreeSize += lastSuballoc.size;
10239 suballocations1st.pop_back();
10240 CleanupAfterFree();
10241 return;
10242 }
10243 }
10244
10245 // Item from the middle of 1st vector.
10246 {
10247 VmaSuballocation refSuballoc;
10248 refSuballoc.offset = offset;
10249 // Rest of members stays uninitialized intentionally for better performance.
10250 SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10251 suballocations1st.begin() + m_1stNullItemsBeginCount,
10252 suballocations1st.end(),
10253 refSuballoc);
10254 if(it != suballocations1st.end())
10255 {
10256 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10257 it->hAllocation = VK_NULL_HANDLE;
10258 ++m_1stNullItemsMiddleCount;
10259 m_SumFreeSize += it->size;
10260 CleanupAfterFree();
10261 return;
10262 }
10263 }
10264
10265 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10266 {
10267 // Item from the middle of 2nd vector.
10268 VmaSuballocation refSuballoc;
10269 refSuballoc.offset = offset;
10270 // Rest of members stays uninitialized intentionally for better performance.
10271 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10272 VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10273 VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10274 if(it != suballocations2nd.end())
10275 {
10276 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10277 it->hAllocation = VK_NULL_HANDLE;
10278 ++m_2ndNullItemsCount;
10279 m_SumFreeSize += it->size;
10280 CleanupAfterFree();
10281 return;
10282 }
10283 }
10284
10285 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10286}
10287
10288bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10289{
10290 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10291 const size_t suballocCount = AccessSuballocations1st().size();
10292 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10293}
10294
10295void VmaBlockMetadata_Linear::CleanupAfterFree()
10296{
10297 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10298 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10299
10300 if(IsEmpty())
10301 {
10302 suballocations1st.clear();
10303 suballocations2nd.clear();
10304 m_1stNullItemsBeginCount = 0;
10305 m_1stNullItemsMiddleCount = 0;
10306 m_2ndNullItemsCount = 0;
10307 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10308 }
10309 else
10310 {
10311 const size_t suballoc1stCount = suballocations1st.size();
10312 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10313 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10314
10315 // Find more null items at the beginning of 1st vector.
10316 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10317 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10318 {
10319 ++m_1stNullItemsBeginCount;
10320 --m_1stNullItemsMiddleCount;
10321 }
10322
10323 // Find more null items at the end of 1st vector.
10324 while(m_1stNullItemsMiddleCount > 0 &&
10325 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10326 {
10327 --m_1stNullItemsMiddleCount;
10328 suballocations1st.pop_back();
10329 }
10330
10331 // Find more null items at the end of 2nd vector.
10332 while(m_2ndNullItemsCount > 0 &&
10333 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10334 {
10335 --m_2ndNullItemsCount;
10336 suballocations2nd.pop_back();
10337 }
10338
10339 if(ShouldCompact1st())
10340 {
10341 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10342 size_t srcIndex = m_1stNullItemsBeginCount;
10343 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10344 {
10345 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10346 {
10347 ++srcIndex;
10348 }
10349 if(dstIndex != srcIndex)
10350 {
10351 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10352 }
10353 ++srcIndex;
10354 }
10355 suballocations1st.resize(nonNullItemCount);
10356 m_1stNullItemsBeginCount = 0;
10357 m_1stNullItemsMiddleCount = 0;
10358 }
10359
10360 // 2nd vector became empty.
10361 if(suballocations2nd.empty())
10362 {
10363 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10364 }
10365
10366 // 1st vector became empty.
10367 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10368 {
10369 suballocations1st.clear();
10370 m_1stNullItemsBeginCount = 0;
10371
10372 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10373 {
10374 // Swap 1st with 2nd. Now 2nd is empty.
10375 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10376 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10377 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10378 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10379 {
10380 ++m_1stNullItemsBeginCount;
10381 --m_1stNullItemsMiddleCount;
10382 }
10383 m_2ndNullItemsCount = 0;
10384 m_1stVectorIndex ^= 1;
10385 }
10386 }
10387 }
10388
10389 VMA_HEAVY_ASSERT(Validate());
10390}
10391
10392
10393////////////////////////////////////////////////////////////////////////////////
10394// class VmaBlockMetadata_Buddy
10395
10396VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10397 VmaBlockMetadata(hAllocator),
10398 m_Root(VMA_NULL),
10399 m_AllocationCount(0),
10400 m_FreeCount(1),
10401 m_SumFreeSize(0)
10402{
10403 memset(m_FreeList, 0, sizeof(m_FreeList));
10404}
10405
10406VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10407{
10408 DeleteNode(m_Root);
10409}
10410
10411void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10412{
10413 VmaBlockMetadata::Init(size);
10414
10415 m_UsableSize = VmaPrevPow2(size);
10416 m_SumFreeSize = m_UsableSize;
10417
10418 // Calculate m_LevelCount.
10419 m_LevelCount = 1;
10420 while(m_LevelCount < MAX_LEVELS &&
10421 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10422 {
10423 ++m_LevelCount;
10424 }
10425
10426 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10427 rootNode->offset = 0;
10428 rootNode->type = Node::TYPE_FREE;
10429 rootNode->parent = VMA_NULL;
10430 rootNode->buddy = VMA_NULL;
10431
10432 m_Root = rootNode;
10433 AddToFreeListFront(0, rootNode);
10434}
10435
10436bool VmaBlockMetadata_Buddy::Validate() const
10437{
10438 // Validate tree.
10439 ValidationContext ctx;
10440 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10441 {
10442 VMA_VALIDATE(false && "ValidateNode failed.");
10443 }
10444 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10445 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10446
10447 // Validate free node lists.
10448 for(uint32_t level = 0; level < m_LevelCount; ++level)
10449 {
10450 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10451 m_FreeList[level].front->free.prev == VMA_NULL);
10452
10453 for(Node* node = m_FreeList[level].front;
10454 node != VMA_NULL;
10455 node = node->free.next)
10456 {
10457 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10458
10459 if(node->free.next == VMA_NULL)
10460 {
10461 VMA_VALIDATE(m_FreeList[level].back == node);
10462 }
10463 else
10464 {
10465 VMA_VALIDATE(node->free.next->free.prev == node);
10466 }
10467 }
10468 }
10469
10470 // Validate that free lists ar higher levels are empty.
10471 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10472 {
10473 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10474 }
10475
10476 return true;
10477}
10478
10479VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10480{
10481 for(uint32_t level = 0; level < m_LevelCount; ++level)
10482 {
10483 if(m_FreeList[level].front != VMA_NULL)
10484 {
10485 return LevelToNodeSize(level);
10486 }
10487 }
10488 return 0;
10489}
10490
10491void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10492{
10493 const VkDeviceSize unusableSize = GetUnusableSize();
10494
10495 outInfo.blockCount = 1;
10496
10497 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10498 outInfo.usedBytes = outInfo.unusedBytes = 0;
10499
10500 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10501 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10502 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10503
10504 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10505
10506 if(unusableSize > 0)
10507 {
10508 ++outInfo.unusedRangeCount;
10509 outInfo.unusedBytes += unusableSize;
10510 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10511 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10512 }
10513}
10514
10515void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10516{
10517 const VkDeviceSize unusableSize = GetUnusableSize();
10518
10519 inoutStats.size += GetSize();
10520 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10521 inoutStats.allocationCount += m_AllocationCount;
10522 inoutStats.unusedRangeCount += m_FreeCount;
10523 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10524
10525 if(unusableSize > 0)
10526 {
10527 ++inoutStats.unusedRangeCount;
10528 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10529 }
10530}
10531
10532#if VMA_STATS_STRING_ENABLED
10533
10534void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10535{
10536 // TODO optimize
10537 VmaStatInfo stat;
10538 CalcAllocationStatInfo(stat);
10539
10540 PrintDetailedMap_Begin(
10541 json,
10542 stat.unusedBytes,
10543 stat.allocationCount,
10544 stat.unusedRangeCount);
10545
10546 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10547
10548 const VkDeviceSize unusableSize = GetUnusableSize();
10549 if(unusableSize > 0)
10550 {
10551 PrintDetailedMap_UnusedRange(json,
10552 m_UsableSize, // offset
10553 unusableSize); // size
10554 }
10555
10556 PrintDetailedMap_End(json);
10557}
10558
10559#endif // #if VMA_STATS_STRING_ENABLED
10560
10561bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10562 uint32_t currentFrameIndex,
10563 uint32_t frameInUseCount,
10564 VkDeviceSize bufferImageGranularity,
10565 VkDeviceSize allocSize,
10566 VkDeviceSize allocAlignment,
10567 bool upperAddress,
10568 VmaSuballocationType allocType,
10569 bool canMakeOtherLost,
10570 uint32_t strategy,
10571 VmaAllocationRequest* pAllocationRequest)
10572{
10573 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10574
10575 // Simple way to respect bufferImageGranularity. May be optimized some day.
10576 // Whenever it might be an OPTIMAL image...
10577 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10578 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10579 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10580 {
10581 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10582 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10583 }
10584
10585 if(allocSize > m_UsableSize)
10586 {
10587 return false;
10588 }
10589
10590 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10591 for(uint32_t level = targetLevel + 1; level--; )
10592 {
10593 for(Node* freeNode = m_FreeList[level].front;
10594 freeNode != VMA_NULL;
10595 freeNode = freeNode->free.next)
10596 {
10597 if(freeNode->offset % allocAlignment == 0)
10598 {
10599 pAllocationRequest->offset = freeNode->offset;
10600 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10601 pAllocationRequest->sumItemSize = 0;
10602 pAllocationRequest->itemsToMakeLostCount = 0;
10603 pAllocationRequest->customData = (void*)(uintptr_t)level;
10604 return true;
10605 }
10606 }
10607 }
10608
10609 return false;
10610}
10611
10612bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10613 uint32_t currentFrameIndex,
10614 uint32_t frameInUseCount,
10615 VmaAllocationRequest* pAllocationRequest)
10616{
10617 /*
10618 Lost allocations are not supported in buddy allocator at the moment.
10619 Support might be added in the future.
10620 */
10621 return pAllocationRequest->itemsToMakeLostCount == 0;
10622}
10623
10624uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10625{
10626 /*
10627 Lost allocations are not supported in buddy allocator at the moment.
10628 Support might be added in the future.
10629 */
10630 return 0;
10631}
10632
10633void VmaBlockMetadata_Buddy::Alloc(
10634 const VmaAllocationRequest& request,
10635 VmaSuballocationType type,
10636 VkDeviceSize allocSize,
10637 bool upperAddress,
10638 VmaAllocation hAllocation)
10639{
10640 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10641 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10642
10643 Node* currNode = m_FreeList[currLevel].front;
10644 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10645 while(currNode->offset != request.offset)
10646 {
10647 currNode = currNode->free.next;
10648 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10649 }
10650
10651 // Go down, splitting free nodes.
10652 while(currLevel < targetLevel)
10653 {
10654 // currNode is already first free node at currLevel.
10655 // Remove it from list of free nodes at this currLevel.
10656 RemoveFromFreeList(currLevel, currNode);
10657
10658 const uint32_t childrenLevel = currLevel + 1;
10659
10660 // Create two free sub-nodes.
10661 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10662 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10663
10664 leftChild->offset = currNode->offset;
10665 leftChild->type = Node::TYPE_FREE;
10666 leftChild->parent = currNode;
10667 leftChild->buddy = rightChild;
10668
10669 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10670 rightChild->type = Node::TYPE_FREE;
10671 rightChild->parent = currNode;
10672 rightChild->buddy = leftChild;
10673
10674 // Convert current currNode to split type.
10675 currNode->type = Node::TYPE_SPLIT;
10676 currNode->split.leftChild = leftChild;
10677
10678 // Add child nodes to free list. Order is important!
10679 AddToFreeListFront(childrenLevel, rightChild);
10680 AddToFreeListFront(childrenLevel, leftChild);
10681
10682 ++m_FreeCount;
10683 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10684 ++currLevel;
10685 currNode = m_FreeList[currLevel].front;
10686
10687 /*
10688 We can be sure that currNode, as left child of node previously split,
10689 also fullfills the alignment requirement.
10690 */
10691 }
10692
10693 // Remove from free list.
10694 VMA_ASSERT(currLevel == targetLevel &&
10695 currNode != VMA_NULL &&
10696 currNode->type == Node::TYPE_FREE);
10697 RemoveFromFreeList(currLevel, currNode);
10698
10699 // Convert to allocation node.
10700 currNode->type = Node::TYPE_ALLOCATION;
10701 currNode->allocation.alloc = hAllocation;
10702
10703 ++m_AllocationCount;
10704 --m_FreeCount;
10705 m_SumFreeSize -= allocSize;
10706}
10707
10708void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10709{
10710 if(node->type == Node::TYPE_SPLIT)
10711 {
10712 DeleteNode(node->split.leftChild->buddy);
10713 DeleteNode(node->split.leftChild);
10714 }
10715
10716 vma_delete(GetAllocationCallbacks(), node);
10717}
10718
10719bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10720{
10721 VMA_VALIDATE(level < m_LevelCount);
10722 VMA_VALIDATE(curr->parent == parent);
10723 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10724 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10725 switch(curr->type)
10726 {
10727 case Node::TYPE_FREE:
10728 // curr->free.prev, next are validated separately.
10729 ctx.calculatedSumFreeSize += levelNodeSize;
10730 ++ctx.calculatedFreeCount;
10731 break;
10732 case Node::TYPE_ALLOCATION:
10733 ++ctx.calculatedAllocationCount;
10734 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10735 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10736 break;
10737 case Node::TYPE_SPLIT:
10738 {
10739 const uint32_t childrenLevel = level + 1;
10740 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10741 const Node* const leftChild = curr->split.leftChild;
10742 VMA_VALIDATE(leftChild != VMA_NULL);
10743 VMA_VALIDATE(leftChild->offset == curr->offset);
10744 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10745 {
10746 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10747 }
10748 const Node* const rightChild = leftChild->buddy;
10749 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10750 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10751 {
10752 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10753 }
10754 }
10755 break;
10756 default:
10757 return false;
10758 }
10759
10760 return true;
10761}
10762
10763uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10764{
10765 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10766 uint32_t level = 0;
10767 VkDeviceSize currLevelNodeSize = m_UsableSize;
10768 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10769 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10770 {
10771 ++level;
10772 currLevelNodeSize = nextLevelNodeSize;
10773 nextLevelNodeSize = currLevelNodeSize >> 1;
10774 }
10775 return level;
10776}
10777
10778void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10779{
10780 // Find node and level.
10781 Node* node = m_Root;
10782 VkDeviceSize nodeOffset = 0;
10783 uint32_t level = 0;
10784 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10785 while(node->type == Node::TYPE_SPLIT)
10786 {
10787 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10788 if(offset < nodeOffset + nextLevelSize)
10789 {
10790 node = node->split.leftChild;
10791 }
10792 else
10793 {
10794 node = node->split.leftChild->buddy;
10795 nodeOffset += nextLevelSize;
10796 }
10797 ++level;
10798 levelNodeSize = nextLevelSize;
10799 }
10800
10801 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10802 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10803
10804 ++m_FreeCount;
10805 --m_AllocationCount;
10806 m_SumFreeSize += alloc->GetSize();
10807
10808 node->type = Node::TYPE_FREE;
10809
10810 // Join free nodes if possible.
10811 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10812 {
10813 RemoveFromFreeList(level, node->buddy);
10814 Node* const parent = node->parent;
10815
10816 vma_delete(GetAllocationCallbacks(), node->buddy);
10817 vma_delete(GetAllocationCallbacks(), node);
10818 parent->type = Node::TYPE_FREE;
10819
10820 node = parent;
10821 --level;
10822 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10823 --m_FreeCount;
10824 }
10825
10826 AddToFreeListFront(level, node);
10827}
10828
10829void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10830{
10831 switch(node->type)
10832 {
10833 case Node::TYPE_FREE:
10834 ++outInfo.unusedRangeCount;
10835 outInfo.unusedBytes += levelNodeSize;
10836 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10837 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10838 break;
10839 case Node::TYPE_ALLOCATION:
10840 {
10841 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10842 ++outInfo.allocationCount;
10843 outInfo.usedBytes += allocSize;
10844 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10845 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10846
10847 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10848 if(unusedRangeSize > 0)
10849 {
10850 ++outInfo.unusedRangeCount;
10851 outInfo.unusedBytes += unusedRangeSize;
10852 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10853 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10854 }
10855 }
10856 break;
10857 case Node::TYPE_SPLIT:
10858 {
10859 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10860 const Node* const leftChild = node->split.leftChild;
10861 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10862 const Node* const rightChild = leftChild->buddy;
10863 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10864 }
10865 break;
10866 default:
10867 VMA_ASSERT(0);
10868 }
10869}
10870
10871void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10872{
10873 VMA_ASSERT(node->type == Node::TYPE_FREE);
10874
10875 // List is empty.
10876 Node* const frontNode = m_FreeList[level].front;
10877 if(frontNode == VMA_NULL)
10878 {
10879 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10880 node->free.prev = node->free.next = VMA_NULL;
10881 m_FreeList[level].front = m_FreeList[level].back = node;
10882 }
10883 else
10884 {
10885 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10886 node->free.prev = VMA_NULL;
10887 node->free.next = frontNode;
10888 frontNode->free.prev = node;
10889 m_FreeList[level].front = node;
10890 }
10891}
10892
10893void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10894{
10895 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10896
10897 // It is at the front.
10898 if(node->free.prev == VMA_NULL)
10899 {
10900 VMA_ASSERT(m_FreeList[level].front == node);
10901 m_FreeList[level].front = node->free.next;
10902 }
10903 else
10904 {
10905 Node* const prevFreeNode = node->free.prev;
10906 VMA_ASSERT(prevFreeNode->free.next == node);
10907 prevFreeNode->free.next = node->free.next;
10908 }
10909
10910 // It is at the back.
10911 if(node->free.next == VMA_NULL)
10912 {
10913 VMA_ASSERT(m_FreeList[level].back == node);
10914 m_FreeList[level].back = node->free.prev;
10915 }
10916 else
10917 {
10918 Node* const nextFreeNode = node->free.next;
10919 VMA_ASSERT(nextFreeNode->free.prev == node);
10920 nextFreeNode->free.prev = node->free.prev;
10921 }
10922}
10923
10924#if VMA_STATS_STRING_ENABLED
10925void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10926{
10927 switch(node->type)
10928 {
10929 case Node::TYPE_FREE:
10930 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10931 break;
10932 case Node::TYPE_ALLOCATION:
10933 {
10934 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10935 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10936 if(allocSize < levelNodeSize)
10937 {
10938 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10939 }
10940 }
10941 break;
10942 case Node::TYPE_SPLIT:
10943 {
10944 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10945 const Node* const leftChild = node->split.leftChild;
10946 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10947 const Node* const rightChild = leftChild->buddy;
10948 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10949 }
10950 break;
10951 default:
10952 VMA_ASSERT(0);
10953 }
10954}
10955#endif // #if VMA_STATS_STRING_ENABLED
10956
10957
10958////////////////////////////////////////////////////////////////////////////////
10959// class VmaDeviceMemoryBlock
10960
10961VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
10962 m_pMetadata(VMA_NULL),
10963 m_MemoryTypeIndex(UINT32_MAX),
10964 m_Id(0),
10965 m_hMemory(VK_NULL_HANDLE),
10966 m_MapCount(0),
10967 m_pMappedData(VMA_NULL)
10968{
10969}
10970
10971void VmaDeviceMemoryBlock::Init(
10972 VmaAllocator hAllocator,
10973 uint32_t newMemoryTypeIndex,
10974 VkDeviceMemory newMemory,
10975 VkDeviceSize newSize,
10976 uint32_t id,
10977 uint32_t algorithm)
10978{
10979 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
10980
10981 m_MemoryTypeIndex = newMemoryTypeIndex;
10982 m_Id = id;
10983 m_hMemory = newMemory;
10984
10985 switch(algorithm)
10986 {
10987 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
10988 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
10989 break;
10990 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
10991 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
10992 break;
10993 default:
10994 VMA_ASSERT(0);
10995 // Fall-through.
10996 case 0:
10997 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
10998 }
10999 m_pMetadata->Init(newSize);
11000}
11001
11002void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11003{
11004 // This is the most important assert in the entire library.
11005 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11006 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11007
11008 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11009 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11010 m_hMemory = VK_NULL_HANDLE;
11011
11012 vma_delete(allocator, m_pMetadata);
11013 m_pMetadata = VMA_NULL;
11014}
11015
11016bool VmaDeviceMemoryBlock::Validate() const
11017{
11018 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11019 (m_pMetadata->GetSize() != 0));
11020
11021 return m_pMetadata->Validate();
11022}
11023
11024VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11025{
11026 void* pData = nullptr;
11027 VkResult res = Map(hAllocator, 1, &pData);
11028 if(res != VK_SUCCESS)
11029 {
11030 return res;
11031 }
11032
11033 res = m_pMetadata->CheckCorruption(pData);
11034
11035 Unmap(hAllocator, 1);
11036
11037 return res;
11038}
11039
11040VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11041{
11042 if(count == 0)
11043 {
11044 return VK_SUCCESS;
11045 }
11046
11047 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11048 if(m_MapCount != 0)
11049 {
11050 m_MapCount += count;
11051 VMA_ASSERT(m_pMappedData != VMA_NULL);
11052 if(ppData != VMA_NULL)
11053 {
11054 *ppData = m_pMappedData;
11055 }
11056 return VK_SUCCESS;
11057 }
11058 else
11059 {
11060 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11061 hAllocator->m_hDevice,
11062 m_hMemory,
11063 0, // offset
11064 VK_WHOLE_SIZE,
11065 0, // flags
11066 &m_pMappedData);
11067 if(result == VK_SUCCESS)
11068 {
11069 if(ppData != VMA_NULL)
11070 {
11071 *ppData = m_pMappedData;
11072 }
11073 m_MapCount = count;
11074 }
11075 return result;
11076 }
11077}
11078
11079void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11080{
11081 if(count == 0)
11082 {
11083 return;
11084 }
11085
11086 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11087 if(m_MapCount >= count)
11088 {
11089 m_MapCount -= count;
11090 if(m_MapCount == 0)
11091 {
11092 m_pMappedData = VMA_NULL;
11093 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11094 }
11095 }
11096 else
11097 {
11098 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11099 }
11100}
11101
11102VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11103{
11104 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11105 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11106
11107 void* pData;
11108 VkResult res = Map(hAllocator, 1, &pData);
11109 if(res != VK_SUCCESS)
11110 {
11111 return res;
11112 }
11113
11114 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11115 VmaWriteMagicValue(pData, allocOffset + allocSize);
11116
11117 Unmap(hAllocator, 1);
11118
11119 return VK_SUCCESS;
11120}
11121
11122VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11123{
11124 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11125 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11126
11127 void* pData;
11128 VkResult res = Map(hAllocator, 1, &pData);
11129 if(res != VK_SUCCESS)
11130 {
11131 return res;
11132 }
11133
11134 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11135 {
11136 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11137 }
11138 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11139 {
11140 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11141 }
11142
11143 Unmap(hAllocator, 1);
11144
11145 return VK_SUCCESS;
11146}
11147
11148VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11149 const VmaAllocator hAllocator,
11150 const VmaAllocation hAllocation,
11151 VkBuffer hBuffer)
11152{
11153 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11154 hAllocation->GetBlock() == this);
11155 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11156 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11157 return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11158 hAllocator->m_hDevice,
11159 hBuffer,
11160 m_hMemory,
11161 hAllocation->GetOffset());
11162}
11163
11164VkResult VmaDeviceMemoryBlock::BindImageMemory(
11165 const VmaAllocator hAllocator,
11166 const VmaAllocation hAllocation,
11167 VkImage hImage)
11168{
11169 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11170 hAllocation->GetBlock() == this);
11171 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11172 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11173 return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11174 hAllocator->m_hDevice,
11175 hImage,
11176 m_hMemory,
11177 hAllocation->GetOffset());
11178}
11179
11180static void InitStatInfo(VmaStatInfo& outInfo)
11181{
11182 memset(&outInfo, 0, sizeof(outInfo));
11183 outInfo.allocationSizeMin = UINT64_MAX;
11184 outInfo.unusedRangeSizeMin = UINT64_MAX;
11185}
11186
11187// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
11188static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11189{
11190 inoutInfo.blockCount += srcInfo.blockCount;
11191 inoutInfo.allocationCount += srcInfo.allocationCount;
11192 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11193 inoutInfo.usedBytes += srcInfo.usedBytes;
11194 inoutInfo.unusedBytes += srcInfo.unusedBytes;
11195 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11196 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11197 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11198 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11199}
11200
11201static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11202{
11203 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11204 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11205 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11206 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11207}
11208
11209VmaPool_T::VmaPool_T(
11210 VmaAllocator hAllocator,
11211 const VmaPoolCreateInfo& createInfo,
11212 VkDeviceSize preferredBlockSize) :
11213 m_BlockVector(
11214 hAllocator,
11215 createInfo.memoryTypeIndex,
11216 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11217 createInfo.minBlockCount,
11218 createInfo.maxBlockCount,
11219 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11220 createInfo.frameInUseCount,
11221 true, // isCustomPool
11222 createInfo.blockSize != 0, // explicitBlockSize
11223 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11224 m_Id(0)
11225{
11226}
11227
11228VmaPool_T::~VmaPool_T()
11229{
11230}
11231
11232#if VMA_STATS_STRING_ENABLED
11233
11234#endif // #if VMA_STATS_STRING_ENABLED
11235
11236VmaBlockVector::VmaBlockVector(
11237 VmaAllocator hAllocator,
11238 uint32_t memoryTypeIndex,
11239 VkDeviceSize preferredBlockSize,
11240 size_t minBlockCount,
11241 size_t maxBlockCount,
11242 VkDeviceSize bufferImageGranularity,
11243 uint32_t frameInUseCount,
11244 bool isCustomPool,
11245 bool explicitBlockSize,
11246 uint32_t algorithm) :
11247 m_hAllocator(hAllocator),
11248 m_MemoryTypeIndex(memoryTypeIndex),
11249 m_PreferredBlockSize(preferredBlockSize),
11250 m_MinBlockCount(minBlockCount),
11251 m_MaxBlockCount(maxBlockCount),
11252 m_BufferImageGranularity(bufferImageGranularity),
11253 m_FrameInUseCount(frameInUseCount),
11254 m_IsCustomPool(isCustomPool),
11255 m_ExplicitBlockSize(explicitBlockSize),
11256 m_Algorithm(algorithm),
11257 m_HasEmptyBlock(false),
11258 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11259 m_NextBlockId(0)
11260{
11261}
11262
11263VmaBlockVector::~VmaBlockVector()
11264{
11265 for(size_t i = m_Blocks.size(); i--; )
11266 {
11267 m_Blocks[i]->Destroy(m_hAllocator);
11268 vma_delete(m_hAllocator, m_Blocks[i]);
11269 }
11270}
11271
11272VkResult VmaBlockVector::CreateMinBlocks()
11273{
11274 for(size_t i = 0; i < m_MinBlockCount; ++i)
11275 {
11276 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11277 if(res != VK_SUCCESS)
11278 {
11279 return res;
11280 }
11281 }
11282 return VK_SUCCESS;
11283}
11284
11285void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11286{
11287 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11288
11289 const size_t blockCount = m_Blocks.size();
11290
11291 pStats->size = 0;
11292 pStats->unusedSize = 0;
11293 pStats->allocationCount = 0;
11294 pStats->unusedRangeCount = 0;
11295 pStats->unusedRangeSizeMax = 0;
11296 pStats->blockCount = blockCount;
11297
11298 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11299 {
11300 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11301 VMA_ASSERT(pBlock);
11302 VMA_HEAVY_ASSERT(pBlock->Validate());
11303 pBlock->m_pMetadata->AddPoolStats(*pStats);
11304 }
11305}
11306
11307bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11308{
11309 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11310 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11311 (VMA_DEBUG_MARGIN > 0) &&
11312 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11313}
11314
11315static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11316
11317VkResult VmaBlockVector::Allocate(
11318 VmaPool hCurrentPool,
11319 uint32_t currentFrameIndex,
11320 VkDeviceSize size,
11321 VkDeviceSize alignment,
11322 const VmaAllocationCreateInfo& createInfo,
11323 VmaSuballocationType suballocType,
11324 size_t allocationCount,
11325 VmaAllocation* pAllocations)
11326{
11327 size_t allocIndex;
11328 VkResult res = VK_SUCCESS;
11329
11330 {
11331 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11332 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11333 {
11334 res = AllocatePage(
11335 hCurrentPool,
11336 currentFrameIndex,
11337 size,
11338 alignment,
11339 createInfo,
11340 suballocType,
11341 pAllocations + allocIndex);
11342 if(res != VK_SUCCESS)
11343 {
11344 break;
11345 }
11346 }
11347 }
11348
11349 if(res != VK_SUCCESS)
11350 {
11351 // Free all already created allocations.
11352 while(allocIndex--)
11353 {
11354 Free(pAllocations[allocIndex]);
11355 }
11356 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11357 }
11358
11359 return res;
11360}
11361
11362VkResult VmaBlockVector::AllocatePage(
11363 VmaPool hCurrentPool,
11364 uint32_t currentFrameIndex,
11365 VkDeviceSize size,
11366 VkDeviceSize alignment,
11367 const VmaAllocationCreateInfo& createInfo,
11368 VmaSuballocationType suballocType,
11369 VmaAllocation* pAllocation)
11370{
11371 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11372 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11373 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11374 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11375 const bool canCreateNewBlock =
11376 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11377 (m_Blocks.size() < m_MaxBlockCount);
11378 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11379
11380 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11381 // Which in turn is available only when maxBlockCount = 1.
11382 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11383 {
11384 canMakeOtherLost = false;
11385 }
11386
11387 // Upper address can only be used with linear allocator and within single memory block.
11388 if(isUpperAddress &&
11389 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11390 {
11391 return VK_ERROR_FEATURE_NOT_PRESENT;
11392 }
11393
11394 // Validate strategy.
11395 switch(strategy)
11396 {
11397 case 0:
11398 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11399 break;
11400 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11401 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11402 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11403 break;
11404 default:
11405 return VK_ERROR_FEATURE_NOT_PRESENT;
11406 }
11407
11408 // Early reject: requested allocation size is larger that maximum block size for this block vector.
11409 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11410 {
11411 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11412 }
11413
11414 /*
11415 Under certain condition, this whole section can be skipped for optimization, so
11416 we move on directly to trying to allocate with canMakeOtherLost. That's the case
11417 e.g. for custom pools with linear algorithm.
11418 */
11419 if(!canMakeOtherLost || canCreateNewBlock)
11420 {
11421 // 1. Search existing allocations. Try to allocate without making other allocations lost.
11422 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11423 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11424
11425 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11426 {
11427 // Use only last block.
11428 if(!m_Blocks.empty())
11429 {
11430 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11431 VMA_ASSERT(pCurrBlock);
11432 VkResult res = AllocateFromBlock(
11433 pCurrBlock,
11434 hCurrentPool,
11435 currentFrameIndex,
11436 size,
11437 alignment,
11438 allocFlagsCopy,
11439 createInfo.pUserData,
11440 suballocType,
11441 strategy,
11442 pAllocation);
11443 if(res == VK_SUCCESS)
11444 {
11445 VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11446 return VK_SUCCESS;
11447 }
11448 }
11449 }
11450 else
11451 {
11452 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11453 {
11454 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11455 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11456 {
11457 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11458 VMA_ASSERT(pCurrBlock);
11459 VkResult res = AllocateFromBlock(
11460 pCurrBlock,
11461 hCurrentPool,
11462 currentFrameIndex,
11463 size,
11464 alignment,
11465 allocFlagsCopy,
11466 createInfo.pUserData,
11467 suballocType,
11468 strategy,
11469 pAllocation);
11470 if(res == VK_SUCCESS)
11471 {
11472 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11473 return VK_SUCCESS;
11474 }
11475 }
11476 }
11477 else // WORST_FIT, FIRST_FIT
11478 {
11479 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11480 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11481 {
11482 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11483 VMA_ASSERT(pCurrBlock);
11484 VkResult res = AllocateFromBlock(
11485 pCurrBlock,
11486 hCurrentPool,
11487 currentFrameIndex,
11488 size,
11489 alignment,
11490 allocFlagsCopy,
11491 createInfo.pUserData,
11492 suballocType,
11493 strategy,
11494 pAllocation);
11495 if(res == VK_SUCCESS)
11496 {
11497 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11498 return VK_SUCCESS;
11499 }
11500 }
11501 }
11502 }
11503
11504 // 2. Try to create new block.
11505 if(canCreateNewBlock)
11506 {
11507 // Calculate optimal size for new block.
11508 VkDeviceSize newBlockSize = m_PreferredBlockSize;
11509 uint32_t newBlockSizeShift = 0;
11510 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11511
11512 if(!m_ExplicitBlockSize)
11513 {
11514 // Allocate 1/8, 1/4, 1/2 as first blocks.
11515 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11516 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11517 {
11518 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11519 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11520 {
11521 newBlockSize = smallerNewBlockSize;
11522 ++newBlockSizeShift;
11523 }
11524 else
11525 {
11526 break;
11527 }
11528 }
11529 }
11530
11531 size_t newBlockIndex = 0;
11532 VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11533 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11534 if(!m_ExplicitBlockSize)
11535 {
11536 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11537 {
11538 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11539 if(smallerNewBlockSize >= size)
11540 {
11541 newBlockSize = smallerNewBlockSize;
11542 ++newBlockSizeShift;
11543 res = CreateBlock(newBlockSize, &newBlockIndex);
11544 }
11545 else
11546 {
11547 break;
11548 }
11549 }
11550 }
11551
11552 if(res == VK_SUCCESS)
11553 {
11554 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11555 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11556
11557 res = AllocateFromBlock(
11558 pBlock,
11559 hCurrentPool,
11560 currentFrameIndex,
11561 size,
11562 alignment,
11563 allocFlagsCopy,
11564 createInfo.pUserData,
11565 suballocType,
11566 strategy,
11567 pAllocation);
11568 if(res == VK_SUCCESS)
11569 {
11570 VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
11571 return VK_SUCCESS;
11572 }
11573 else
11574 {
11575 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11576 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11577 }
11578 }
11579 }
11580 }
11581
11582 // 3. Try to allocate from existing blocks with making other allocations lost.
11583 if(canMakeOtherLost)
11584 {
11585 uint32_t tryIndex = 0;
11586 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11587 {
11588 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11589 VmaAllocationRequest bestRequest = {};
11590 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11591
11592 // 1. Search existing allocations.
11593 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11594 {
11595 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11596 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11597 {
11598 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11599 VMA_ASSERT(pCurrBlock);
11600 VmaAllocationRequest currRequest = {};
11601 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11602 currentFrameIndex,
11603 m_FrameInUseCount,
11604 m_BufferImageGranularity,
11605 size,
11606 alignment,
11607 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11608 suballocType,
11609 canMakeOtherLost,
11610 strategy,
11611 &currRequest))
11612 {
11613 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11614 if(pBestRequestBlock == VMA_NULL ||
11615 currRequestCost < bestRequestCost)
11616 {
11617 pBestRequestBlock = pCurrBlock;
11618 bestRequest = currRequest;
11619 bestRequestCost = currRequestCost;
11620
11621 if(bestRequestCost == 0)
11622 {
11623 break;
11624 }
11625 }
11626 }
11627 }
11628 }
11629 else // WORST_FIT, FIRST_FIT
11630 {
11631 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11632 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11633 {
11634 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11635 VMA_ASSERT(pCurrBlock);
11636 VmaAllocationRequest currRequest = {};
11637 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11638 currentFrameIndex,
11639 m_FrameInUseCount,
11640 m_BufferImageGranularity,
11641 size,
11642 alignment,
11643 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11644 suballocType,
11645 canMakeOtherLost,
11646 strategy,
11647 &currRequest))
11648 {
11649 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11650 if(pBestRequestBlock == VMA_NULL ||
11651 currRequestCost < bestRequestCost ||
11652 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11653 {
11654 pBestRequestBlock = pCurrBlock;
11655 bestRequest = currRequest;
11656 bestRequestCost = currRequestCost;
11657
11658 if(bestRequestCost == 0 ||
11659 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11660 {
11661 break;
11662 }
11663 }
11664 }
11665 }
11666 }
11667
11668 if(pBestRequestBlock != VMA_NULL)
11669 {
11670 if(mapped)
11671 {
11672 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11673 if(res != VK_SUCCESS)
11674 {
11675 return res;
11676 }
11677 }
11678
11679 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11680 currentFrameIndex,
11681 m_FrameInUseCount,
11682 &bestRequest))
11683 {
11684 // We no longer have an empty Allocation.
11685 if(pBestRequestBlock->m_pMetadata->IsEmpty())
11686 {
11687 m_HasEmptyBlock = false;
11688 }
11689 // Allocate from this pBlock.
11690 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11691 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11692 (*pAllocation)->InitBlockAllocation(
11693 hCurrentPool,
11694 pBestRequestBlock,
11695 bestRequest.offset,
11696 alignment,
11697 size,
11698 suballocType,
11699 mapped,
11700 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11701 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11702 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
11703 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11704 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11705 {
11706 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11707 }
11708 if(IsCorruptionDetectionEnabled())
11709 {
11710 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11711 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11712 }
11713 return VK_SUCCESS;
11714 }
11715 // else: Some allocations must have been touched while we are here. Next try.
11716 }
11717 else
11718 {
11719 // Could not find place in any of the blocks - break outer loop.
11720 break;
11721 }
11722 }
11723 /* Maximum number of tries exceeded - a very unlike event when many other
11724 threads are simultaneously touching allocations making it impossible to make
11725 lost at the same time as we try to allocate. */
11726 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11727 {
11728 return VK_ERROR_TOO_MANY_OBJECTS;
11729 }
11730 }
11731
11732 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11733}
11734
11735void VmaBlockVector::Free(
11736 VmaAllocation hAllocation)
11737{
11738 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11739
11740 // Scope for lock.
11741 {
11742 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11743
11744 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11745
11746 if(IsCorruptionDetectionEnabled())
11747 {
11748 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11749 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11750 }
11751
11752 if(hAllocation->IsPersistentMap())
11753 {
11754 pBlock->Unmap(m_hAllocator, 1);
11755 }
11756
11757 pBlock->m_pMetadata->Free(hAllocation);
11758 VMA_HEAVY_ASSERT(pBlock->Validate());
11759
11760 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11761
11762 // pBlock became empty after this deallocation.
11763 if(pBlock->m_pMetadata->IsEmpty())
11764 {
11765 // Already has empty Allocation. We don't want to have two, so delete this one.
11766 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11767 {
11768 pBlockToDelete = pBlock;
11769 Remove(pBlock);
11770 }
11771 // We now have first empty block.
11772 else
11773 {
11774 m_HasEmptyBlock = true;
11775 }
11776 }
11777 // pBlock didn't become empty, but we have another empty block - find and free that one.
11778 // (This is optional, heuristics.)
11779 else if(m_HasEmptyBlock)
11780 {
11781 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11782 if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11783 {
11784 pBlockToDelete = pLastBlock;
11785 m_Blocks.pop_back();
11786 m_HasEmptyBlock = false;
11787 }
11788 }
11789
11790 IncrementallySortBlocks();
11791 }
11792
11793 // Destruction of a free Allocation. Deferred until this point, outside of mutex
11794 // lock, for performance reason.
11795 if(pBlockToDelete != VMA_NULL)
11796 {
11797 VMA_DEBUG_LOG(" Deleted empty allocation");
11798 pBlockToDelete->Destroy(m_hAllocator);
11799 vma_delete(m_hAllocator, pBlockToDelete);
11800 }
11801}
11802
11803VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11804{
11805 VkDeviceSize result = 0;
11806 for(size_t i = m_Blocks.size(); i--; )
11807 {
11808 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11809 if(result >= m_PreferredBlockSize)
11810 {
11811 break;
11812 }
11813 }
11814 return result;
11815}
11816
11817void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11818{
11819 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11820 {
11821 if(m_Blocks[blockIndex] == pBlock)
11822 {
11823 VmaVectorRemove(m_Blocks, blockIndex);
11824 return;
11825 }
11826 }
11827 VMA_ASSERT(0);
11828}
11829
11830void VmaBlockVector::IncrementallySortBlocks()
11831{
11832 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11833 {
11834 // Bubble sort only until first swap.
11835 for(size_t i = 1; i < m_Blocks.size(); ++i)
11836 {
11837 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11838 {
11839 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11840 return;
11841 }
11842 }
11843 }
11844}
11845
11846VkResult VmaBlockVector::AllocateFromBlock(
11847 VmaDeviceMemoryBlock* pBlock,
11848 VmaPool hCurrentPool,
11849 uint32_t currentFrameIndex,
11850 VkDeviceSize size,
11851 VkDeviceSize alignment,
11852 VmaAllocationCreateFlags allocFlags,
11853 void* pUserData,
11854 VmaSuballocationType suballocType,
11855 uint32_t strategy,
11856 VmaAllocation* pAllocation)
11857{
11858 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11859 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11860 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11861 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11862
11863 VmaAllocationRequest currRequest = {};
11864 if(pBlock->m_pMetadata->CreateAllocationRequest(
11865 currentFrameIndex,
11866 m_FrameInUseCount,
11867 m_BufferImageGranularity,
11868 size,
11869 alignment,
11870 isUpperAddress,
11871 suballocType,
11872 false, // canMakeOtherLost
11873 strategy,
11874 &currRequest))
11875 {
11876 // Allocate from pCurrBlock.
11877 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11878
11879 if(mapped)
11880 {
11881 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11882 if(res != VK_SUCCESS)
11883 {
11884 return res;
11885 }
11886 }
11887
11888 // We no longer have an empty Allocation.
11889 if(pBlock->m_pMetadata->IsEmpty())
11890 {
11891 m_HasEmptyBlock = false;
11892 }
11893
11894 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11895 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11896 (*pAllocation)->InitBlockAllocation(
11897 hCurrentPool,
11898 pBlock,
11899 currRequest.offset,
11900 alignment,
11901 size,
11902 suballocType,
11903 mapped,
11904 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11905 VMA_HEAVY_ASSERT(pBlock->Validate());
11906 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11907 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11908 {
11909 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11910 }
11911 if(IsCorruptionDetectionEnabled())
11912 {
11913 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11914 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11915 }
11916 return VK_SUCCESS;
11917 }
11918 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11919}
11920
11921VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11922{
11923 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11924 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11925 allocInfo.allocationSize = blockSize;
11926 VkDeviceMemory mem = VK_NULL_HANDLE;
11927 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11928 if(res < 0)
11929 {
11930 return res;
11931 }
11932
11933 // New VkDeviceMemory successfully created.
11934
11935 // Create new Allocation for it.
11936 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11937 pBlock->Init(
11938 m_hAllocator,
11939 m_MemoryTypeIndex,
11940 mem,
11941 allocInfo.allocationSize,
11942 m_NextBlockId++,
11943 m_Algorithm);
11944
11945 m_Blocks.push_back(pBlock);
11946 if(pNewBlockIndex != VMA_NULL)
11947 {
11948 *pNewBlockIndex = m_Blocks.size() - 1;
11949 }
11950
11951 return VK_SUCCESS;
11952}
11953
11954void VmaBlockVector::ApplyDefragmentationMovesCpu(
11955 class VmaBlockVectorDefragmentationContext* pDefragCtx,
11956 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11957{
11958 const size_t blockCount = m_Blocks.size();
11959 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11960
11961 enum BLOCK_FLAG
11962 {
11963 BLOCK_FLAG_USED = 0x00000001,
11964 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11965 };
11966
11967 struct BlockInfo
11968 {
11969 uint32_t flags;
11970 void* pMappedData;
11971 };
11972 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11973 blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11974 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
11975
11976 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11977 const size_t moveCount = moves.size();
11978 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11979 {
11980 const VmaDefragmentationMove& move = moves[moveIndex];
11981 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
11982 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
11983 }
11984
11985 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
11986
11987 // Go over all blocks. Get mapped pointer or map if necessary.
11988 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
11989 {
11990 BlockInfo& currBlockInfo = blockInfo[blockIndex];
11991 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
11992 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
11993 {
11994 currBlockInfo.pMappedData = pBlock->GetMappedData();
11995 // It is not originally mapped - map it.
11996 if(currBlockInfo.pMappedData == VMA_NULL)
11997 {
11998 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
11999 if(pDefragCtx->res == VK_SUCCESS)
12000 {
12001 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12002 }
12003 }
12004 }
12005 }
12006
12007 // Go over all moves. Do actual data transfer.
12008 if(pDefragCtx->res == VK_SUCCESS)
12009 {
12010 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12011 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12012
12013 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12014 {
12015 const VmaDefragmentationMove& move = moves[moveIndex];
12016
12017 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12018 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12019
12020 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12021
12022 // Invalidate source.
12023 if(isNonCoherent)
12024 {
12025 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12026 memRange.memory = pSrcBlock->GetDeviceMemory();
12027 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12028 memRange.size = VMA_MIN(
12029 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12030 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12031 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12032 }
12033
12034 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12035 memmove(
12036 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12037 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12038 static_cast<size_t>(move.size));
12039
12040 if(IsCorruptionDetectionEnabled())
12041 {
12042 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12043 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12044 }
12045
12046 // Flush destination.
12047 if(isNonCoherent)
12048 {
12049 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12050 memRange.memory = pDstBlock->GetDeviceMemory();
12051 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12052 memRange.size = VMA_MIN(
12053 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12054 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12055 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12056 }
12057 }
12058 }
12059
12060 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12061 // Regardless of pCtx->res == VK_SUCCESS.
12062 for(size_t blockIndex = blockCount; blockIndex--; )
12063 {
12064 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12065 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12066 {
12067 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12068 pBlock->Unmap(m_hAllocator, 1);
12069 }
12070 }
12071}
12072
12073void VmaBlockVector::ApplyDefragmentationMovesGpu(
12074 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12075 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12076 VkCommandBuffer commandBuffer)
12077{
12078 const size_t blockCount = m_Blocks.size();
12079
12080 pDefragCtx->blockContexts.resize(blockCount);
12081 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12082
12083 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12084 const size_t moveCount = moves.size();
12085 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12086 {
12087 const VmaDefragmentationMove& move = moves[moveIndex];
12088 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12089 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12090 }
12091
12092 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12093
12094 // Go over all blocks. Create and bind buffer for whole block if necessary.
12095 {
12096 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12097 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12098 VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12099
12100 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12101 {
12102 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12103 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12104 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12105 {
12106 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12107 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12108 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12109 if(pDefragCtx->res == VK_SUCCESS)
12110 {
12111 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12112 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12113 }
12114 }
12115 }
12116 }
12117
12118 // Go over all moves. Post data transfer commands to command buffer.
12119 if(pDefragCtx->res == VK_SUCCESS)
12120 {
12121 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12122 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12123
12124 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12125 {
12126 const VmaDefragmentationMove& move = moves[moveIndex];
12127
12128 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12129 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12130
12131 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12132
12133 VkBufferCopy region = {
12134 move.srcOffset,
12135 move.dstOffset,
12136 move.size };
12137 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12138 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12139 }
12140 }
12141
12142 // Save buffers to defrag context for later destruction.
12143 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12144 {
12145 pDefragCtx->res = VK_NOT_READY;
12146 }
12147}
12148
12149void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12150{
12151 m_HasEmptyBlock = false;
12152 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12153 {
12154 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12155 if(pBlock->m_pMetadata->IsEmpty())
12156 {
12157 if(m_Blocks.size() > m_MinBlockCount)
12158 {
12159 if(pDefragmentationStats != VMA_NULL)
12160 {
12161 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12162 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12163 }
12164
12165 VmaVectorRemove(m_Blocks, blockIndex);
12166 pBlock->Destroy(m_hAllocator);
12167 vma_delete(m_hAllocator, pBlock);
12168 }
12169 else
12170 {
12171 m_HasEmptyBlock = true;
12172 }
12173 }
12174 }
12175}
12176
12177#if VMA_STATS_STRING_ENABLED
12178
12179void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12180{
12181 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12182
12183 json.BeginObject();
12184
12185 if(m_IsCustomPool)
12186 {
12187 json.WriteString("MemoryTypeIndex");
12188 json.WriteNumber(m_MemoryTypeIndex);
12189
12190 json.WriteString("BlockSize");
12191 json.WriteNumber(m_PreferredBlockSize);
12192
12193 json.WriteString("BlockCount");
12194 json.BeginObject(true);
12195 if(m_MinBlockCount > 0)
12196 {
12197 json.WriteString("Min");
12198 json.WriteNumber((uint64_t)m_MinBlockCount);
12199 }
12200 if(m_MaxBlockCount < SIZE_MAX)
12201 {
12202 json.WriteString("Max");
12203 json.WriteNumber((uint64_t)m_MaxBlockCount);
12204 }
12205 json.WriteString("Cur");
12206 json.WriteNumber((uint64_t)m_Blocks.size());
12207 json.EndObject();
12208
12209 if(m_FrameInUseCount > 0)
12210 {
12211 json.WriteString("FrameInUseCount");
12212 json.WriteNumber(m_FrameInUseCount);
12213 }
12214
12215 if(m_Algorithm != 0)
12216 {
12217 json.WriteString("Algorithm");
12218 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12219 }
12220 }
12221 else
12222 {
12223 json.WriteString("PreferredBlockSize");
12224 json.WriteNumber(m_PreferredBlockSize);
12225 }
12226
12227 json.WriteString("Blocks");
12228 json.BeginObject();
12229 for(size_t i = 0; i < m_Blocks.size(); ++i)
12230 {
12231 json.BeginString();
12232 json.ContinueString(m_Blocks[i]->GetId());
12233 json.EndString();
12234
12235 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12236 }
12237 json.EndObject();
12238
12239 json.EndObject();
12240}
12241
12242#endif // #if VMA_STATS_STRING_ENABLED
12243
12244void VmaBlockVector::Defragment(
12245 class VmaBlockVectorDefragmentationContext* pCtx,
12246 VmaDefragmentationStats* pStats,
12247 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12248 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12249 VkCommandBuffer commandBuffer)
12250{
12251 pCtx->res = VK_SUCCESS;
12252
12253 const VkMemoryPropertyFlags memPropFlags =
12254 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12255 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12256 const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12257
12258 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12259 isHostVisible;
12260 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12261 (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12262
12263 // There are options to defragment this memory type.
12264 if(canDefragmentOnCpu || canDefragmentOnGpu)
12265 {
12266 bool defragmentOnGpu;
12267 // There is only one option to defragment this memory type.
12268 if(canDefragmentOnGpu != canDefragmentOnCpu)
12269 {
12270 defragmentOnGpu = canDefragmentOnGpu;
12271 }
12272 // Both options are available: Heuristics to choose the best one.
12273 else
12274 {
12275 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12276 m_hAllocator->IsIntegratedGpu();
12277 }
12278
12279 bool overlappingMoveSupported = !defragmentOnGpu;
12280
12281 if(m_hAllocator->m_UseMutex)
12282 {
12283 m_Mutex.LockWrite();
12284 pCtx->mutexLocked = true;
12285 }
12286
12287 pCtx->Begin(overlappingMoveSupported);
12288
12289 // Defragment.
12290
12291 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12292 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12293 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12294 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12295 pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12296
12297 // Accumulate statistics.
12298 if(pStats != VMA_NULL)
12299 {
12300 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12301 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12302 pStats->bytesMoved += bytesMoved;
12303 pStats->allocationsMoved += allocationsMoved;
12304 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12305 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12306 if(defragmentOnGpu)
12307 {
12308 maxGpuBytesToMove -= bytesMoved;
12309 maxGpuAllocationsToMove -= allocationsMoved;
12310 }
12311 else
12312 {
12313 maxCpuBytesToMove -= bytesMoved;
12314 maxCpuAllocationsToMove -= allocationsMoved;
12315 }
12316 }
12317
12318 if(pCtx->res >= VK_SUCCESS)
12319 {
12320 if(defragmentOnGpu)
12321 {
12322 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12323 }
12324 else
12325 {
12326 ApplyDefragmentationMovesCpu(pCtx, moves);
12327 }
12328 }
12329 }
12330}
12331
12332void VmaBlockVector::DefragmentationEnd(
12333 class VmaBlockVectorDefragmentationContext* pCtx,
12334 VmaDefragmentationStats* pStats)
12335{
12336 // Destroy buffers.
12337 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12338 {
12339 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12340 if(blockCtx.hBuffer)
12341 {
12342 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12343 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12344 }
12345 }
12346
12347 if(pCtx->res >= VK_SUCCESS)
12348 {
12349 FreeEmptyBlocks(pStats);
12350 }
12351
12352 if(pCtx->mutexLocked)
12353 {
12354 VMA_ASSERT(m_hAllocator->m_UseMutex);
12355 m_Mutex.UnlockWrite();
12356 }
12357}
12358
12359size_t VmaBlockVector::CalcAllocationCount() const
12360{
12361 size_t result = 0;
12362 for(size_t i = 0; i < m_Blocks.size(); ++i)
12363 {
12364 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12365 }
12366 return result;
12367}
12368
12369bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12370{
12371 if(m_BufferImageGranularity == 1)
12372 {
12373 return false;
12374 }
12375 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12376 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12377 {
12378 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12379 VMA_ASSERT(m_Algorithm == 0);
12380 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12381 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12382 {
12383 return true;
12384 }
12385 }
12386 return false;
12387}
12388
12389void VmaBlockVector::MakePoolAllocationsLost(
12390 uint32_t currentFrameIndex,
12391 size_t* pLostAllocationCount)
12392{
12393 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12394 size_t lostAllocationCount = 0;
12395 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12396 {
12397 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12398 VMA_ASSERT(pBlock);
12399 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12400 }
12401 if(pLostAllocationCount != VMA_NULL)
12402 {
12403 *pLostAllocationCount = lostAllocationCount;
12404 }
12405}
12406
12407VkResult VmaBlockVector::CheckCorruption()
12408{
12409 if(!IsCorruptionDetectionEnabled())
12410 {
12411 return VK_ERROR_FEATURE_NOT_PRESENT;
12412 }
12413
12414 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12415 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12416 {
12417 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12418 VMA_ASSERT(pBlock);
12419 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12420 if(res != VK_SUCCESS)
12421 {
12422 return res;
12423 }
12424 }
12425 return VK_SUCCESS;
12426}
12427
12428void VmaBlockVector::AddStats(VmaStats* pStats)
12429{
12430 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12431 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12432
12433 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12434
12435 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12436 {
12437 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12438 VMA_ASSERT(pBlock);
12439 VMA_HEAVY_ASSERT(pBlock->Validate());
12440 VmaStatInfo allocationStatInfo;
12441 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12442 VmaAddStatInfo(pStats->total, allocationStatInfo);
12443 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12444 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12445 }
12446}
12447
12448////////////////////////////////////////////////////////////////////////////////
12449// VmaDefragmentationAlgorithm_Generic members definition
12450
12451VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12452 VmaAllocator hAllocator,
12453 VmaBlockVector* pBlockVector,
12454 uint32_t currentFrameIndex,
12455 bool overlappingMoveSupported) :
12456 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060012457 m_AllocationCount(0),
Tony-LunarG390319b2019-03-18 15:54:16 -060012458 m_AllAllocations(false),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060012459 m_BytesMoved(0),
12460 m_AllocationsMoved(0),
12461 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12462{
12463 // Create block info for each block.
12464 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12465 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12466 {
12467 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12468 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12469 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12470 m_Blocks.push_back(pBlockInfo);
12471 }
12472
12473 // Sort them by m_pBlock pointer value.
12474 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12475}
12476
12477VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12478{
12479 for(size_t i = m_Blocks.size(); i--; )
12480 {
12481 vma_delete(m_hAllocator, m_Blocks[i]);
12482 }
12483}
12484
12485void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12486{
12487 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12488 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12489 {
12490 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12491 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12492 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12493 {
12494 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12495 (*it)->m_Allocations.push_back(allocInfo);
12496 }
12497 else
12498 {
12499 VMA_ASSERT(0);
12500 }
12501
12502 ++m_AllocationCount;
12503 }
12504}
12505
12506VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12507 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12508 VkDeviceSize maxBytesToMove,
12509 uint32_t maxAllocationsToMove)
12510{
12511 if(m_Blocks.empty())
12512 {
12513 return VK_SUCCESS;
12514 }
12515
12516 // This is a choice based on research.
12517 // Option 1:
12518 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12519 // Option 2:
12520 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12521 // Option 3:
12522 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12523
12524 size_t srcBlockMinIndex = 0;
12525 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12526 /*
12527 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12528 {
12529 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12530 if(blocksWithNonMovableCount > 0)
12531 {
12532 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12533 }
12534 }
12535 */
12536
12537 size_t srcBlockIndex = m_Blocks.size() - 1;
12538 size_t srcAllocIndex = SIZE_MAX;
12539 for(;;)
12540 {
12541 // 1. Find next allocation to move.
12542 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12543 // 1.2. Then start from last to first m_Allocations.
12544 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12545 {
12546 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12547 {
12548 // Finished: no more allocations to process.
12549 if(srcBlockIndex == srcBlockMinIndex)
12550 {
12551 return VK_SUCCESS;
12552 }
12553 else
12554 {
12555 --srcBlockIndex;
12556 srcAllocIndex = SIZE_MAX;
12557 }
12558 }
12559 else
12560 {
12561 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12562 }
12563 }
12564
12565 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12566 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12567
12568 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12569 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12570 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12571 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12572
12573 // 2. Try to find new place for this allocation in preceding or current block.
12574 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12575 {
12576 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12577 VmaAllocationRequest dstAllocRequest;
12578 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12579 m_CurrentFrameIndex,
12580 m_pBlockVector->GetFrameInUseCount(),
12581 m_pBlockVector->GetBufferImageGranularity(),
12582 size,
12583 alignment,
12584 false, // upperAddress
12585 suballocType,
12586 false, // canMakeOtherLost
12587 strategy,
12588 &dstAllocRequest) &&
12589 MoveMakesSense(
12590 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12591 {
12592 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12593
12594 // Reached limit on number of allocations or bytes to move.
12595 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12596 (m_BytesMoved + size > maxBytesToMove))
12597 {
12598 return VK_SUCCESS;
12599 }
12600
12601 VmaDefragmentationMove move;
12602 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12603 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12604 move.srcOffset = srcOffset;
12605 move.dstOffset = dstAllocRequest.offset;
12606 move.size = size;
12607 moves.push_back(move);
12608
12609 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12610 dstAllocRequest,
12611 suballocType,
12612 size,
12613 false, // upperAddress
12614 allocInfo.m_hAllocation);
12615 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12616
12617 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12618
12619 if(allocInfo.m_pChanged != VMA_NULL)
12620 {
12621 *allocInfo.m_pChanged = VK_TRUE;
12622 }
12623
12624 ++m_AllocationsMoved;
12625 m_BytesMoved += size;
12626
12627 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12628
12629 break;
12630 }
12631 }
12632
12633 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12634
12635 if(srcAllocIndex > 0)
12636 {
12637 --srcAllocIndex;
12638 }
12639 else
12640 {
12641 if(srcBlockIndex > 0)
12642 {
12643 --srcBlockIndex;
12644 srcAllocIndex = SIZE_MAX;
12645 }
12646 else
12647 {
12648 return VK_SUCCESS;
12649 }
12650 }
12651 }
12652}
12653
12654size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12655{
12656 size_t result = 0;
12657 for(size_t i = 0; i < m_Blocks.size(); ++i)
12658 {
12659 if(m_Blocks[i]->m_HasNonMovableAllocations)
12660 {
12661 ++result;
12662 }
12663 }
12664 return result;
12665}
12666
12667VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12668 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12669 VkDeviceSize maxBytesToMove,
12670 uint32_t maxAllocationsToMove)
12671{
12672 if(!m_AllAllocations && m_AllocationCount == 0)
12673 {
12674 return VK_SUCCESS;
12675 }
12676
12677 const size_t blockCount = m_Blocks.size();
12678 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12679 {
12680 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12681
12682 if(m_AllAllocations)
12683 {
12684 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12685 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12686 it != pMetadata->m_Suballocations.end();
12687 ++it)
12688 {
12689 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12690 {
12691 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12692 pBlockInfo->m_Allocations.push_back(allocInfo);
12693 }
12694 }
12695 }
12696
12697 pBlockInfo->CalcHasNonMovableAllocations();
12698
12699 // This is a choice based on research.
12700 // Option 1:
12701 pBlockInfo->SortAllocationsByOffsetDescending();
12702 // Option 2:
12703 //pBlockInfo->SortAllocationsBySizeDescending();
12704 }
12705
12706 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12707 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12708
12709 // This is a choice based on research.
12710 const uint32_t roundCount = 2;
12711
12712 // Execute defragmentation rounds (the main part).
12713 VkResult result = VK_SUCCESS;
12714 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12715 {
12716 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12717 }
12718
12719 return result;
12720}
12721
12722bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12723 size_t dstBlockIndex, VkDeviceSize dstOffset,
12724 size_t srcBlockIndex, VkDeviceSize srcOffset)
12725{
12726 if(dstBlockIndex < srcBlockIndex)
12727 {
12728 return true;
12729 }
12730 if(dstBlockIndex > srcBlockIndex)
12731 {
12732 return false;
12733 }
12734 if(dstOffset < srcOffset)
12735 {
12736 return true;
12737 }
12738 return false;
12739}
12740
12741////////////////////////////////////////////////////////////////////////////////
12742// VmaDefragmentationAlgorithm_Fast
12743
12744VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12745 VmaAllocator hAllocator,
12746 VmaBlockVector* pBlockVector,
12747 uint32_t currentFrameIndex,
12748 bool overlappingMoveSupported) :
12749 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12750 m_OverlappingMoveSupported(overlappingMoveSupported),
12751 m_AllocationCount(0),
12752 m_AllAllocations(false),
12753 m_BytesMoved(0),
12754 m_AllocationsMoved(0),
12755 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12756{
12757 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12758
12759}
12760
12761VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12762{
12763}
12764
12765VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12766 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12767 VkDeviceSize maxBytesToMove,
12768 uint32_t maxAllocationsToMove)
12769{
12770 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12771
12772 const size_t blockCount = m_pBlockVector->GetBlockCount();
12773 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12774 {
12775 return VK_SUCCESS;
12776 }
12777
12778 PreprocessMetadata();
12779
12780 // Sort blocks in order from most destination.
12781
12782 m_BlockInfos.resize(blockCount);
12783 for(size_t i = 0; i < blockCount; ++i)
12784 {
12785 m_BlockInfos[i].origBlockIndex = i;
12786 }
12787
12788 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12789 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12790 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12791 });
12792
12793 // THE MAIN ALGORITHM
12794
12795 FreeSpaceDatabase freeSpaceDb;
12796
12797 size_t dstBlockInfoIndex = 0;
12798 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12799 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12800 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12801 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12802 VkDeviceSize dstOffset = 0;
12803
12804 bool end = false;
12805 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12806 {
12807 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12808 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12809 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12810 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12811 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12812 {
12813 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12814 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12815 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12816 if(m_AllocationsMoved == maxAllocationsToMove ||
12817 m_BytesMoved + srcAllocSize > maxBytesToMove)
12818 {
12819 end = true;
12820 break;
12821 }
12822 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12823
12824 // Try to place it in one of free spaces from the database.
12825 size_t freeSpaceInfoIndex;
12826 VkDeviceSize dstAllocOffset;
12827 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12828 freeSpaceInfoIndex, dstAllocOffset))
12829 {
12830 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12831 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12832 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12833 VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12834
12835 // Same block
12836 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12837 {
12838 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12839
12840 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12841
12842 VmaSuballocation suballoc = *srcSuballocIt;
12843 suballoc.offset = dstAllocOffset;
12844 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12845 m_BytesMoved += srcAllocSize;
12846 ++m_AllocationsMoved;
12847
12848 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12849 ++nextSuballocIt;
12850 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12851 srcSuballocIt = nextSuballocIt;
12852
12853 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12854
12855 VmaDefragmentationMove move = {
12856 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12857 srcAllocOffset, dstAllocOffset,
12858 srcAllocSize };
12859 moves.push_back(move);
12860 }
12861 // Different block
12862 else
12863 {
12864 // MOVE OPTION 2: Move the allocation to a different block.
12865
12866 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12867
12868 VmaSuballocation suballoc = *srcSuballocIt;
12869 suballoc.offset = dstAllocOffset;
12870 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12871 m_BytesMoved += srcAllocSize;
12872 ++m_AllocationsMoved;
12873
12874 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12875 ++nextSuballocIt;
12876 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12877 srcSuballocIt = nextSuballocIt;
12878
12879 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12880
12881 VmaDefragmentationMove move = {
12882 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12883 srcAllocOffset, dstAllocOffset,
12884 srcAllocSize };
12885 moves.push_back(move);
12886 }
12887 }
12888 else
12889 {
12890 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12891
12892 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12893 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12894 dstAllocOffset + srcAllocSize > dstBlockSize)
12895 {
12896 // But before that, register remaining free space at the end of dst block.
12897 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12898
12899 ++dstBlockInfoIndex;
12900 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12901 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12902 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12903 dstBlockSize = pDstMetadata->GetSize();
12904 dstOffset = 0;
12905 dstAllocOffset = 0;
12906 }
12907
12908 // Same block
12909 if(dstBlockInfoIndex == srcBlockInfoIndex)
12910 {
12911 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12912
12913 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12914
12915 bool skipOver = overlap;
12916 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12917 {
12918 // If destination and source place overlap, skip if it would move it
12919 // by only < 1/64 of its size.
12920 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12921 }
12922
12923 if(skipOver)
12924 {
12925 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12926
12927 dstOffset = srcAllocOffset + srcAllocSize;
12928 ++srcSuballocIt;
12929 }
12930 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12931 else
12932 {
12933 srcSuballocIt->offset = dstAllocOffset;
12934 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12935 dstOffset = dstAllocOffset + srcAllocSize;
12936 m_BytesMoved += srcAllocSize;
12937 ++m_AllocationsMoved;
12938 ++srcSuballocIt;
12939 VmaDefragmentationMove move = {
12940 srcOrigBlockIndex, dstOrigBlockIndex,
12941 srcAllocOffset, dstAllocOffset,
12942 srcAllocSize };
12943 moves.push_back(move);
12944 }
12945 }
12946 // Different block
12947 else
12948 {
12949 // MOVE OPTION 2: Move the allocation to a different block.
12950
12951 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12952 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12953
12954 VmaSuballocation suballoc = *srcSuballocIt;
12955 suballoc.offset = dstAllocOffset;
12956 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12957 dstOffset = dstAllocOffset + srcAllocSize;
12958 m_BytesMoved += srcAllocSize;
12959 ++m_AllocationsMoved;
12960
12961 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12962 ++nextSuballocIt;
12963 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12964 srcSuballocIt = nextSuballocIt;
12965
12966 pDstMetadata->m_Suballocations.push_back(suballoc);
12967
12968 VmaDefragmentationMove move = {
12969 srcOrigBlockIndex, dstOrigBlockIndex,
12970 srcAllocOffset, dstAllocOffset,
12971 srcAllocSize };
12972 moves.push_back(move);
12973 }
12974 }
12975 }
12976 }
12977
12978 m_BlockInfos.clear();
12979
12980 PostprocessMetadata();
12981
12982 return VK_SUCCESS;
12983}
12984
12985void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
12986{
12987 const size_t blockCount = m_pBlockVector->GetBlockCount();
12988 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12989 {
12990 VmaBlockMetadata_Generic* const pMetadata =
12991 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
12992 pMetadata->m_FreeCount = 0;
12993 pMetadata->m_SumFreeSize = pMetadata->GetSize();
12994 pMetadata->m_FreeSuballocationsBySize.clear();
12995 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
12996 it != pMetadata->m_Suballocations.end(); )
12997 {
12998 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
12999 {
13000 VmaSuballocationList::iterator nextIt = it;
13001 ++nextIt;
13002 pMetadata->m_Suballocations.erase(it);
13003 it = nextIt;
13004 }
13005 else
13006 {
13007 ++it;
13008 }
13009 }
13010 }
13011}
13012
13013void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13014{
13015 const size_t blockCount = m_pBlockVector->GetBlockCount();
13016 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13017 {
13018 VmaBlockMetadata_Generic* const pMetadata =
13019 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13020 const VkDeviceSize blockSize = pMetadata->GetSize();
13021
13022 // No allocations in this block - entire area is free.
13023 if(pMetadata->m_Suballocations.empty())
13024 {
13025 pMetadata->m_FreeCount = 1;
13026 //pMetadata->m_SumFreeSize is already set to blockSize.
13027 VmaSuballocation suballoc = {
13028 0, // offset
13029 blockSize, // size
13030 VMA_NULL, // hAllocation
13031 VMA_SUBALLOCATION_TYPE_FREE };
13032 pMetadata->m_Suballocations.push_back(suballoc);
13033 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13034 }
13035 // There are some allocations in this block.
13036 else
13037 {
13038 VkDeviceSize offset = 0;
13039 VmaSuballocationList::iterator it;
13040 for(it = pMetadata->m_Suballocations.begin();
13041 it != pMetadata->m_Suballocations.end();
13042 ++it)
13043 {
13044 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13045 VMA_ASSERT(it->offset >= offset);
13046
13047 // Need to insert preceding free space.
13048 if(it->offset > offset)
13049 {
13050 ++pMetadata->m_FreeCount;
13051 const VkDeviceSize freeSize = it->offset - offset;
13052 VmaSuballocation suballoc = {
13053 offset, // offset
13054 freeSize, // size
13055 VMA_NULL, // hAllocation
13056 VMA_SUBALLOCATION_TYPE_FREE };
13057 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13058 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13059 {
13060 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13061 }
13062 }
13063
13064 pMetadata->m_SumFreeSize -= it->size;
13065 offset = it->offset + it->size;
13066 }
13067
13068 // Need to insert trailing free space.
13069 if(offset < blockSize)
13070 {
13071 ++pMetadata->m_FreeCount;
13072 const VkDeviceSize freeSize = blockSize - offset;
13073 VmaSuballocation suballoc = {
13074 offset, // offset
13075 freeSize, // size
13076 VMA_NULL, // hAllocation
13077 VMA_SUBALLOCATION_TYPE_FREE };
13078 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13079 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13080 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13081 {
13082 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13083 }
13084 }
13085
13086 VMA_SORT(
13087 pMetadata->m_FreeSuballocationsBySize.begin(),
13088 pMetadata->m_FreeSuballocationsBySize.end(),
13089 VmaSuballocationItemSizeLess());
13090 }
13091
13092 VMA_HEAVY_ASSERT(pMetadata->Validate());
13093 }
13094}
13095
13096void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13097{
13098 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13099 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13100 while(it != pMetadata->m_Suballocations.end())
13101 {
13102 if(it->offset < suballoc.offset)
13103 {
13104 ++it;
13105 }
13106 }
13107 pMetadata->m_Suballocations.insert(it, suballoc);
13108}
13109
13110////////////////////////////////////////////////////////////////////////////////
13111// VmaBlockVectorDefragmentationContext
13112
13113VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13114 VmaAllocator hAllocator,
13115 VmaPool hCustomPool,
13116 VmaBlockVector* pBlockVector,
13117 uint32_t currFrameIndex,
13118 uint32_t algorithmFlags) :
13119 res(VK_SUCCESS),
13120 mutexLocked(false),
13121 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13122 m_hAllocator(hAllocator),
13123 m_hCustomPool(hCustomPool),
13124 m_pBlockVector(pBlockVector),
13125 m_CurrFrameIndex(currFrameIndex),
13126 m_AlgorithmFlags(algorithmFlags),
13127 m_pAlgorithm(VMA_NULL),
13128 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13129 m_AllAllocations(false)
13130{
13131}
13132
13133VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13134{
13135 vma_delete(m_hAllocator, m_pAlgorithm);
13136}
13137
13138void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13139{
13140 AllocInfo info = { hAlloc, pChanged };
13141 m_Allocations.push_back(info);
13142}
13143
13144void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13145{
13146 const bool allAllocations = m_AllAllocations ||
13147 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13148
13149 /********************************
13150 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13151 ********************************/
13152
13153 /*
13154 Fast algorithm is supported only when certain criteria are met:
13155 - VMA_DEBUG_MARGIN is 0.
13156 - All allocations in this block vector are moveable.
13157 - There is no possibility of image/buffer granularity conflict.
13158 */
13159 if(VMA_DEBUG_MARGIN == 0 &&
13160 allAllocations &&
13161 !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13162 {
13163 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13164 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13165 }
13166 else
13167 {
13168 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13169 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13170 }
13171
13172 if(allAllocations)
13173 {
13174 m_pAlgorithm->AddAll();
13175 }
13176 else
13177 {
13178 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13179 {
13180 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13181 }
13182 }
13183}
13184
13185////////////////////////////////////////////////////////////////////////////////
13186// VmaDefragmentationContext
13187
13188VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13189 VmaAllocator hAllocator,
13190 uint32_t currFrameIndex,
13191 uint32_t flags,
13192 VmaDefragmentationStats* pStats) :
13193 m_hAllocator(hAllocator),
13194 m_CurrFrameIndex(currFrameIndex),
13195 m_Flags(flags),
13196 m_pStats(pStats),
13197 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13198{
13199 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13200}
13201
13202VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13203{
13204 for(size_t i = m_CustomPoolContexts.size(); i--; )
13205 {
13206 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13207 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13208 vma_delete(m_hAllocator, pBlockVectorCtx);
13209 }
13210 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13211 {
13212 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13213 if(pBlockVectorCtx)
13214 {
13215 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13216 vma_delete(m_hAllocator, pBlockVectorCtx);
13217 }
13218 }
13219}
13220
13221void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13222{
13223 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13224 {
13225 VmaPool pool = pPools[poolIndex];
13226 VMA_ASSERT(pool);
13227 // Pools with algorithm other than default are not defragmented.
13228 if(pool->m_BlockVector.GetAlgorithm() == 0)
13229 {
13230 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13231
13232 for(size_t i = m_CustomPoolContexts.size(); i--; )
13233 {
13234 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13235 {
13236 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13237 break;
13238 }
13239 }
13240
13241 if(!pBlockVectorDefragCtx)
13242 {
13243 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13244 m_hAllocator,
13245 pool,
13246 &pool->m_BlockVector,
13247 m_CurrFrameIndex,
13248 m_Flags);
13249 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13250 }
13251
13252 pBlockVectorDefragCtx->AddAll();
13253 }
13254 }
13255}
13256
13257void VmaDefragmentationContext_T::AddAllocations(
13258 uint32_t allocationCount,
13259 VmaAllocation* pAllocations,
13260 VkBool32* pAllocationsChanged)
13261{
13262 // Dispatch pAllocations among defragmentators. Create them when necessary.
13263 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13264 {
13265 const VmaAllocation hAlloc = pAllocations[allocIndex];
13266 VMA_ASSERT(hAlloc);
13267 // DedicatedAlloc cannot be defragmented.
13268 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13269 // Lost allocation cannot be defragmented.
13270 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13271 {
13272 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13273
13274 const VmaPool hAllocPool = hAlloc->GetPool();
13275 // This allocation belongs to custom pool.
13276 if(hAllocPool != VK_NULL_HANDLE)
13277 {
13278 // Pools with algorithm other than default are not defragmented.
13279 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13280 {
13281 for(size_t i = m_CustomPoolContexts.size(); i--; )
13282 {
13283 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13284 {
13285 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13286 break;
13287 }
13288 }
13289 if(!pBlockVectorDefragCtx)
13290 {
13291 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13292 m_hAllocator,
13293 hAllocPool,
13294 &hAllocPool->m_BlockVector,
13295 m_CurrFrameIndex,
13296 m_Flags);
13297 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13298 }
13299 }
13300 }
13301 // This allocation belongs to default pool.
13302 else
13303 {
13304 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13305 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13306 if(!pBlockVectorDefragCtx)
13307 {
13308 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13309 m_hAllocator,
13310 VMA_NULL, // hCustomPool
13311 m_hAllocator->m_pBlockVectors[memTypeIndex],
13312 m_CurrFrameIndex,
13313 m_Flags);
13314 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13315 }
13316 }
13317
13318 if(pBlockVectorDefragCtx)
13319 {
13320 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13321 &pAllocationsChanged[allocIndex] : VMA_NULL;
13322 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13323 }
13324 }
13325 }
13326}
13327
13328VkResult VmaDefragmentationContext_T::Defragment(
13329 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13330 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13331 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13332{
13333 if(pStats)
13334 {
13335 memset(pStats, 0, sizeof(VmaDefragmentationStats));
13336 }
13337
13338 if(commandBuffer == VK_NULL_HANDLE)
13339 {
13340 maxGpuBytesToMove = 0;
13341 maxGpuAllocationsToMove = 0;
13342 }
13343
13344 VkResult res = VK_SUCCESS;
13345
13346 // Process default pools.
13347 for(uint32_t memTypeIndex = 0;
13348 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13349 ++memTypeIndex)
13350 {
13351 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13352 if(pBlockVectorCtx)
13353 {
13354 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13355 pBlockVectorCtx->GetBlockVector()->Defragment(
13356 pBlockVectorCtx,
13357 pStats,
13358 maxCpuBytesToMove, maxCpuAllocationsToMove,
13359 maxGpuBytesToMove, maxGpuAllocationsToMove,
13360 commandBuffer);
13361 if(pBlockVectorCtx->res != VK_SUCCESS)
13362 {
13363 res = pBlockVectorCtx->res;
13364 }
13365 }
13366 }
13367
13368 // Process custom pools.
13369 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13370 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13371 ++customCtxIndex)
13372 {
13373 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13374 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13375 pBlockVectorCtx->GetBlockVector()->Defragment(
13376 pBlockVectorCtx,
13377 pStats,
13378 maxCpuBytesToMove, maxCpuAllocationsToMove,
13379 maxGpuBytesToMove, maxGpuAllocationsToMove,
13380 commandBuffer);
13381 if(pBlockVectorCtx->res != VK_SUCCESS)
13382 {
13383 res = pBlockVectorCtx->res;
13384 }
13385 }
13386
13387 return res;
13388}
13389
13390////////////////////////////////////////////////////////////////////////////////
13391// VmaRecorder
13392
13393#if VMA_RECORDING_ENABLED
13394
13395VmaRecorder::VmaRecorder() :
13396 m_UseMutex(true),
13397 m_Flags(0),
13398 m_File(VMA_NULL),
13399 m_Freq(INT64_MAX),
13400 m_StartCounter(INT64_MAX)
13401{
13402}
13403
13404VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13405{
13406 m_UseMutex = useMutex;
13407 m_Flags = settings.flags;
13408
13409 QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13410 QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13411
13412 // Open file for writing.
13413 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13414 if(err != 0)
13415 {
13416 return VK_ERROR_INITIALIZATION_FAILED;
13417 }
13418
13419 // Write header.
13420 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13421 fprintf(m_File, "%s\n", "1,5");
13422
13423 return VK_SUCCESS;
13424}
13425
13426VmaRecorder::~VmaRecorder()
13427{
13428 if(m_File != VMA_NULL)
13429 {
13430 fclose(m_File);
13431 }
13432}
13433
13434void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13435{
13436 CallParams callParams;
13437 GetBasicParams(callParams);
13438
13439 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13440 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13441 Flush();
13442}
13443
13444void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13445{
13446 CallParams callParams;
13447 GetBasicParams(callParams);
13448
13449 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13450 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13451 Flush();
13452}
13453
13454void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13455{
13456 CallParams callParams;
13457 GetBasicParams(callParams);
13458
13459 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13460 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13461 createInfo.memoryTypeIndex,
13462 createInfo.flags,
13463 createInfo.blockSize,
13464 (uint64_t)createInfo.minBlockCount,
13465 (uint64_t)createInfo.maxBlockCount,
13466 createInfo.frameInUseCount,
13467 pool);
13468 Flush();
13469}
13470
13471void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13472{
13473 CallParams callParams;
13474 GetBasicParams(callParams);
13475
13476 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13477 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13478 pool);
13479 Flush();
13480}
13481
13482void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13483 const VkMemoryRequirements& vkMemReq,
13484 const VmaAllocationCreateInfo& createInfo,
13485 VmaAllocation allocation)
13486{
13487 CallParams callParams;
13488 GetBasicParams(callParams);
13489
13490 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13491 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13492 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13493 vkMemReq.size,
13494 vkMemReq.alignment,
13495 vkMemReq.memoryTypeBits,
13496 createInfo.flags,
13497 createInfo.usage,
13498 createInfo.requiredFlags,
13499 createInfo.preferredFlags,
13500 createInfo.memoryTypeBits,
13501 createInfo.pool,
13502 allocation,
13503 userDataStr.GetString());
13504 Flush();
13505}
13506
13507void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13508 const VkMemoryRequirements& vkMemReq,
13509 const VmaAllocationCreateInfo& createInfo,
13510 uint64_t allocationCount,
13511 const VmaAllocation* pAllocations)
13512{
13513 CallParams callParams;
13514 GetBasicParams(callParams);
13515
13516 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13517 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13518 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13519 vkMemReq.size,
13520 vkMemReq.alignment,
13521 vkMemReq.memoryTypeBits,
13522 createInfo.flags,
13523 createInfo.usage,
13524 createInfo.requiredFlags,
13525 createInfo.preferredFlags,
13526 createInfo.memoryTypeBits,
13527 createInfo.pool);
13528 PrintPointerList(allocationCount, pAllocations);
13529 fprintf(m_File, ",%s\n", userDataStr.GetString());
13530 Flush();
13531}
13532
13533void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13534 const VkMemoryRequirements& vkMemReq,
13535 bool requiresDedicatedAllocation,
13536 bool prefersDedicatedAllocation,
13537 const VmaAllocationCreateInfo& createInfo,
13538 VmaAllocation allocation)
13539{
13540 CallParams callParams;
13541 GetBasicParams(callParams);
13542
13543 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13544 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13545 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13546 vkMemReq.size,
13547 vkMemReq.alignment,
13548 vkMemReq.memoryTypeBits,
13549 requiresDedicatedAllocation ? 1 : 0,
13550 prefersDedicatedAllocation ? 1 : 0,
13551 createInfo.flags,
13552 createInfo.usage,
13553 createInfo.requiredFlags,
13554 createInfo.preferredFlags,
13555 createInfo.memoryTypeBits,
13556 createInfo.pool,
13557 allocation,
13558 userDataStr.GetString());
13559 Flush();
13560}
13561
13562void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13563 const VkMemoryRequirements& vkMemReq,
13564 bool requiresDedicatedAllocation,
13565 bool prefersDedicatedAllocation,
13566 const VmaAllocationCreateInfo& createInfo,
13567 VmaAllocation allocation)
13568{
13569 CallParams callParams;
13570 GetBasicParams(callParams);
13571
13572 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13573 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13574 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,
13575 vkMemReq.size,
13576 vkMemReq.alignment,
13577 vkMemReq.memoryTypeBits,
13578 requiresDedicatedAllocation ? 1 : 0,
13579 prefersDedicatedAllocation ? 1 : 0,
13580 createInfo.flags,
13581 createInfo.usage,
13582 createInfo.requiredFlags,
13583 createInfo.preferredFlags,
13584 createInfo.memoryTypeBits,
13585 createInfo.pool,
13586 allocation,
13587 userDataStr.GetString());
13588 Flush();
13589}
13590
13591void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13592 VmaAllocation allocation)
13593{
13594 CallParams callParams;
13595 GetBasicParams(callParams);
13596
13597 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13598 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13599 allocation);
13600 Flush();
13601}
13602
13603void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13604 uint64_t allocationCount,
13605 const VmaAllocation* pAllocations)
13606{
13607 CallParams callParams;
13608 GetBasicParams(callParams);
13609
13610 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13611 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13612 PrintPointerList(allocationCount, pAllocations);
13613 fprintf(m_File, "\n");
13614 Flush();
13615}
13616
13617void VmaRecorder::RecordResizeAllocation(
13618 uint32_t frameIndex,
13619 VmaAllocation allocation,
13620 VkDeviceSize newSize)
13621{
13622 CallParams callParams;
13623 GetBasicParams(callParams);
13624
13625 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13626 fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13627 allocation, newSize);
13628 Flush();
13629}
13630
13631void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13632 VmaAllocation allocation,
13633 const void* pUserData)
13634{
13635 CallParams callParams;
13636 GetBasicParams(callParams);
13637
13638 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13639 UserDataString userDataStr(
13640 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13641 pUserData);
13642 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13643 allocation,
13644 userDataStr.GetString());
13645 Flush();
13646}
13647
13648void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13649 VmaAllocation allocation)
13650{
13651 CallParams callParams;
13652 GetBasicParams(callParams);
13653
13654 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13655 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13656 allocation);
13657 Flush();
13658}
13659
13660void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13661 VmaAllocation allocation)
13662{
13663 CallParams callParams;
13664 GetBasicParams(callParams);
13665
13666 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13667 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13668 allocation);
13669 Flush();
13670}
13671
13672void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13673 VmaAllocation allocation)
13674{
13675 CallParams callParams;
13676 GetBasicParams(callParams);
13677
13678 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13679 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13680 allocation);
13681 Flush();
13682}
13683
13684void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13685 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13686{
13687 CallParams callParams;
13688 GetBasicParams(callParams);
13689
13690 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13691 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13692 allocation,
13693 offset,
13694 size);
13695 Flush();
13696}
13697
13698void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13699 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13700{
13701 CallParams callParams;
13702 GetBasicParams(callParams);
13703
13704 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13705 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13706 allocation,
13707 offset,
13708 size);
13709 Flush();
13710}
13711
13712void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13713 const VkBufferCreateInfo& bufCreateInfo,
13714 const VmaAllocationCreateInfo& allocCreateInfo,
13715 VmaAllocation allocation)
13716{
13717 CallParams callParams;
13718 GetBasicParams(callParams);
13719
13720 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13721 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13722 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,
13723 bufCreateInfo.flags,
13724 bufCreateInfo.size,
13725 bufCreateInfo.usage,
13726 bufCreateInfo.sharingMode,
13727 allocCreateInfo.flags,
13728 allocCreateInfo.usage,
13729 allocCreateInfo.requiredFlags,
13730 allocCreateInfo.preferredFlags,
13731 allocCreateInfo.memoryTypeBits,
13732 allocCreateInfo.pool,
13733 allocation,
13734 userDataStr.GetString());
13735 Flush();
13736}
13737
13738void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13739 const VkImageCreateInfo& imageCreateInfo,
13740 const VmaAllocationCreateInfo& allocCreateInfo,
13741 VmaAllocation allocation)
13742{
13743 CallParams callParams;
13744 GetBasicParams(callParams);
13745
13746 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13747 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13748 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,
13749 imageCreateInfo.flags,
13750 imageCreateInfo.imageType,
13751 imageCreateInfo.format,
13752 imageCreateInfo.extent.width,
13753 imageCreateInfo.extent.height,
13754 imageCreateInfo.extent.depth,
13755 imageCreateInfo.mipLevels,
13756 imageCreateInfo.arrayLayers,
13757 imageCreateInfo.samples,
13758 imageCreateInfo.tiling,
13759 imageCreateInfo.usage,
13760 imageCreateInfo.sharingMode,
13761 imageCreateInfo.initialLayout,
13762 allocCreateInfo.flags,
13763 allocCreateInfo.usage,
13764 allocCreateInfo.requiredFlags,
13765 allocCreateInfo.preferredFlags,
13766 allocCreateInfo.memoryTypeBits,
13767 allocCreateInfo.pool,
13768 allocation,
13769 userDataStr.GetString());
13770 Flush();
13771}
13772
13773void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13774 VmaAllocation allocation)
13775{
13776 CallParams callParams;
13777 GetBasicParams(callParams);
13778
13779 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13780 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13781 allocation);
13782 Flush();
13783}
13784
13785void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13786 VmaAllocation allocation)
13787{
13788 CallParams callParams;
13789 GetBasicParams(callParams);
13790
13791 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13792 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13793 allocation);
13794 Flush();
13795}
13796
13797void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13798 VmaAllocation allocation)
13799{
13800 CallParams callParams;
13801 GetBasicParams(callParams);
13802
13803 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13804 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13805 allocation);
13806 Flush();
13807}
13808
13809void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13810 VmaAllocation allocation)
13811{
13812 CallParams callParams;
13813 GetBasicParams(callParams);
13814
13815 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13816 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13817 allocation);
13818 Flush();
13819}
13820
13821void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13822 VmaPool pool)
13823{
13824 CallParams callParams;
13825 GetBasicParams(callParams);
13826
13827 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13828 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13829 pool);
13830 Flush();
13831}
13832
13833void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13834 const VmaDefragmentationInfo2& info,
13835 VmaDefragmentationContext ctx)
13836{
13837 CallParams callParams;
13838 GetBasicParams(callParams);
13839
13840 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13841 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13842 info.flags);
13843 PrintPointerList(info.allocationCount, info.pAllocations);
13844 fprintf(m_File, ",");
13845 PrintPointerList(info.poolCount, info.pPools);
13846 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13847 info.maxCpuBytesToMove,
13848 info.maxCpuAllocationsToMove,
13849 info.maxGpuBytesToMove,
13850 info.maxGpuAllocationsToMove,
13851 info.commandBuffer,
13852 ctx);
13853 Flush();
13854}
13855
13856void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13857 VmaDefragmentationContext ctx)
13858{
13859 CallParams callParams;
13860 GetBasicParams(callParams);
13861
13862 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13863 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13864 ctx);
13865 Flush();
13866}
13867
13868VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13869{
13870 if(pUserData != VMA_NULL)
13871 {
13872 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13873 {
13874 m_Str = (const char*)pUserData;
13875 }
13876 else
13877 {
13878 sprintf_s(m_PtrStr, "%p", pUserData);
13879 m_Str = m_PtrStr;
13880 }
13881 }
13882 else
13883 {
13884 m_Str = "";
13885 }
13886}
13887
13888void VmaRecorder::WriteConfiguration(
13889 const VkPhysicalDeviceProperties& devProps,
13890 const VkPhysicalDeviceMemoryProperties& memProps,
13891 bool dedicatedAllocationExtensionEnabled)
13892{
13893 fprintf(m_File, "Config,Begin\n");
13894
13895 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13896 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13897 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13898 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13899 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13900 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13901
13902 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13903 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13904 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13905
13906 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13907 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13908 {
13909 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13910 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13911 }
13912 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13913 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13914 {
13915 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13916 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13917 }
13918
13919 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13920
13921 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13922 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13923 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13924 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13925 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13926 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13927 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13928 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13929 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13930
13931 fprintf(m_File, "Config,End\n");
13932}
13933
13934void VmaRecorder::GetBasicParams(CallParams& outParams)
13935{
13936 outParams.threadId = GetCurrentThreadId();
13937
13938 LARGE_INTEGER counter;
13939 QueryPerformanceCounter(&counter);
13940 outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13941}
13942
13943void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13944{
13945 if(count)
13946 {
13947 fprintf(m_File, "%p", pItems[0]);
13948 for(uint64_t i = 1; i < count; ++i)
13949 {
13950 fprintf(m_File, " %p", pItems[i]);
13951 }
13952 }
13953}
13954
13955void VmaRecorder::Flush()
13956{
13957 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13958 {
13959 fflush(m_File);
13960 }
13961}
13962
13963#endif // #if VMA_RECORDING_ENABLED
13964
13965////////////////////////////////////////////////////////////////////////////////
13966// VmaAllocator_T
13967
13968VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13969 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13970 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13971 m_hDevice(pCreateInfo->device),
13972 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13973 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13974 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13975 m_PreferredLargeHeapBlockSize(0),
13976 m_PhysicalDevice(pCreateInfo->physicalDevice),
13977 m_CurrentFrameIndex(0),
13978 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13979 m_NextPoolId(0)
13980#if VMA_RECORDING_ENABLED
13981 ,m_pRecorder(VMA_NULL)
13982#endif
13983{
13984 if(VMA_DEBUG_DETECT_CORRUPTION)
13985 {
13986 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
13987 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
13988 }
13989
13990 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
13991
13992#if !(VMA_DEDICATED_ALLOCATION)
13993 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
13994 {
13995 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
13996 }
13997#endif
13998
13999 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14000 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14001 memset(&m_MemProps, 0, sizeof(m_MemProps));
14002
14003 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14004 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14005
14006 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14007 {
14008 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14009 }
14010
14011 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14012 {
14013 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14014 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14015 }
14016
14017 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14018
14019 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14020 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14021
14022 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14023 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14024 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14025 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14026
14027 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14028 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14029
14030 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14031 {
14032 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14033 {
14034 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14035 if(limit != VK_WHOLE_SIZE)
14036 {
14037 m_HeapSizeLimit[heapIndex] = limit;
14038 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14039 {
14040 m_MemProps.memoryHeaps[heapIndex].size = limit;
14041 }
14042 }
14043 }
14044 }
14045
14046 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14047 {
14048 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14049
14050 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14051 this,
14052 memTypeIndex,
14053 preferredBlockSize,
14054 0,
14055 SIZE_MAX,
14056 GetBufferImageGranularity(),
14057 pCreateInfo->frameInUseCount,
14058 false, // isCustomPool
14059 false, // explicitBlockSize
14060 false); // linearAlgorithm
14061 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14062 // becase minBlockCount is 0.
14063 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14064
14065 }
14066}
14067
14068VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14069{
14070 VkResult res = VK_SUCCESS;
14071
14072 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14073 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14074 {
14075#if VMA_RECORDING_ENABLED
14076 m_pRecorder = vma_new(this, VmaRecorder)();
14077 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14078 if(res != VK_SUCCESS)
14079 {
14080 return res;
14081 }
14082 m_pRecorder->WriteConfiguration(
14083 m_PhysicalDeviceProperties,
14084 m_MemProps,
14085 m_UseKhrDedicatedAllocation);
14086 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14087#else
14088 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14089 return VK_ERROR_FEATURE_NOT_PRESENT;
14090#endif
14091 }
14092
14093 return res;
14094}
14095
14096VmaAllocator_T::~VmaAllocator_T()
14097{
14098#if VMA_RECORDING_ENABLED
14099 if(m_pRecorder != VMA_NULL)
14100 {
14101 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14102 vma_delete(this, m_pRecorder);
14103 }
14104#endif
14105
14106 VMA_ASSERT(m_Pools.empty());
14107
14108 for(size_t i = GetMemoryTypeCount(); i--; )
14109 {
14110 vma_delete(this, m_pDedicatedAllocations[i]);
14111 vma_delete(this, m_pBlockVectors[i]);
14112 }
14113}
14114
14115void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14116{
14117#if VMA_STATIC_VULKAN_FUNCTIONS == 1
14118 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14119 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14120 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14121 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14122 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14123 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14124 m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14125 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14126 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14127 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14128 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14129 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14130 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14131 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14132 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14133 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14134 m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14135#if VMA_DEDICATED_ALLOCATION
14136 if(m_UseKhrDedicatedAllocation)
14137 {
14138 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14139 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14140 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14141 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14142 }
14143#endif // #if VMA_DEDICATED_ALLOCATION
14144#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14145
14146#define VMA_COPY_IF_NOT_NULL(funcName) \
14147 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14148
14149 if(pVulkanFunctions != VMA_NULL)
14150 {
14151 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14152 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14153 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14154 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14155 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14156 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14157 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14158 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14159 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14160 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14161 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14162 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14163 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14164 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14165 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14166 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14167 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14168#if VMA_DEDICATED_ALLOCATION
14169 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14170 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14171#endif
14172 }
14173
14174#undef VMA_COPY_IF_NOT_NULL
14175
14176 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14177 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14178 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14179 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14180 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14181 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14182 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14183 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14184 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14185 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14186 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14187 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14188 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14189 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14190 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14191 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14192 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14193 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14194 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14195#if VMA_DEDICATED_ALLOCATION
14196 if(m_UseKhrDedicatedAllocation)
14197 {
14198 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14199 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14200 }
14201#endif
14202}
14203
14204VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14205{
14206 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14207 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14208 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14209 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14210}
14211
14212VkResult VmaAllocator_T::AllocateMemoryOfType(
14213 VkDeviceSize size,
14214 VkDeviceSize alignment,
14215 bool dedicatedAllocation,
14216 VkBuffer dedicatedBuffer,
14217 VkImage dedicatedImage,
14218 const VmaAllocationCreateInfo& createInfo,
14219 uint32_t memTypeIndex,
14220 VmaSuballocationType suballocType,
14221 size_t allocationCount,
14222 VmaAllocation* pAllocations)
14223{
14224 VMA_ASSERT(pAllocations != VMA_NULL);
14225 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14226
14227 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14228
14229 // If memory type is not HOST_VISIBLE, disable MAPPED.
14230 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14231 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14232 {
14233 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14234 }
14235
14236 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14237 VMA_ASSERT(blockVector);
14238
14239 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14240 bool preferDedicatedMemory =
14241 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14242 dedicatedAllocation ||
14243 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14244 size > preferredBlockSize / 2;
14245
14246 if(preferDedicatedMemory &&
14247 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14248 finalCreateInfo.pool == VK_NULL_HANDLE)
14249 {
14250 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14251 }
14252
14253 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14254 {
14255 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14256 {
14257 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14258 }
14259 else
14260 {
14261 return AllocateDedicatedMemory(
14262 size,
14263 suballocType,
14264 memTypeIndex,
14265 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14266 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14267 finalCreateInfo.pUserData,
14268 dedicatedBuffer,
14269 dedicatedImage,
14270 allocationCount,
14271 pAllocations);
14272 }
14273 }
14274 else
14275 {
14276 VkResult res = blockVector->Allocate(
14277 VK_NULL_HANDLE, // hCurrentPool
14278 m_CurrentFrameIndex.load(),
14279 size,
14280 alignment,
14281 finalCreateInfo,
14282 suballocType,
14283 allocationCount,
14284 pAllocations);
14285 if(res == VK_SUCCESS)
14286 {
14287 return res;
14288 }
14289
14290 // 5. Try dedicated memory.
14291 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14292 {
14293 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14294 }
14295 else
14296 {
14297 res = AllocateDedicatedMemory(
14298 size,
14299 suballocType,
14300 memTypeIndex,
14301 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14302 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14303 finalCreateInfo.pUserData,
14304 dedicatedBuffer,
14305 dedicatedImage,
14306 allocationCount,
14307 pAllocations);
14308 if(res == VK_SUCCESS)
14309 {
14310 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14311 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14312 return VK_SUCCESS;
14313 }
14314 else
14315 {
14316 // Everything failed: Return error code.
14317 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14318 return res;
14319 }
14320 }
14321 }
14322}
14323
14324VkResult VmaAllocator_T::AllocateDedicatedMemory(
14325 VkDeviceSize size,
14326 VmaSuballocationType suballocType,
14327 uint32_t memTypeIndex,
14328 bool map,
14329 bool isUserDataString,
14330 void* pUserData,
14331 VkBuffer dedicatedBuffer,
14332 VkImage dedicatedImage,
14333 size_t allocationCount,
14334 VmaAllocation* pAllocations)
14335{
14336 VMA_ASSERT(allocationCount > 0 && pAllocations);
14337
14338 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14339 allocInfo.memoryTypeIndex = memTypeIndex;
14340 allocInfo.allocationSize = size;
14341
14342#if VMA_DEDICATED_ALLOCATION
14343 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14344 if(m_UseKhrDedicatedAllocation)
14345 {
14346 if(dedicatedBuffer != VK_NULL_HANDLE)
14347 {
14348 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14349 dedicatedAllocInfo.buffer = dedicatedBuffer;
14350 allocInfo.pNext = &dedicatedAllocInfo;
14351 }
14352 else if(dedicatedImage != VK_NULL_HANDLE)
14353 {
14354 dedicatedAllocInfo.image = dedicatedImage;
14355 allocInfo.pNext = &dedicatedAllocInfo;
14356 }
14357 }
14358#endif // #if VMA_DEDICATED_ALLOCATION
14359
14360 size_t allocIndex;
Tony-LunarG390319b2019-03-18 15:54:16 -060014361 VkResult res = VK_SUCCESS;
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060014362 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14363 {
14364 res = AllocateDedicatedMemoryPage(
14365 size,
14366 suballocType,
14367 memTypeIndex,
14368 allocInfo,
14369 map,
14370 isUserDataString,
14371 pUserData,
14372 pAllocations + allocIndex);
14373 if(res != VK_SUCCESS)
14374 {
14375 break;
14376 }
14377 }
14378
14379 if(res == VK_SUCCESS)
14380 {
14381 // Register them in m_pDedicatedAllocations.
14382 {
14383 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14384 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14385 VMA_ASSERT(pDedicatedAllocations);
14386 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14387 {
14388 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14389 }
14390 }
14391
14392 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14393 }
14394 else
14395 {
14396 // Free all already created allocations.
14397 while(allocIndex--)
14398 {
14399 VmaAllocation currAlloc = pAllocations[allocIndex];
14400 VkDeviceMemory hMemory = currAlloc->GetMemory();
14401
14402 /*
14403 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14404 before vkFreeMemory.
14405
14406 if(currAlloc->GetMappedData() != VMA_NULL)
14407 {
14408 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14409 }
14410 */
14411
14412 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14413
14414 currAlloc->SetUserData(this, VMA_NULL);
14415 vma_delete(this, currAlloc);
14416 }
14417
14418 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14419 }
14420
14421 return res;
14422}
14423
14424VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14425 VkDeviceSize size,
14426 VmaSuballocationType suballocType,
14427 uint32_t memTypeIndex,
14428 const VkMemoryAllocateInfo& allocInfo,
14429 bool map,
14430 bool isUserDataString,
14431 void* pUserData,
14432 VmaAllocation* pAllocation)
14433{
14434 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14435 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14436 if(res < 0)
14437 {
14438 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14439 return res;
14440 }
14441
14442 void* pMappedData = VMA_NULL;
14443 if(map)
14444 {
14445 res = (*m_VulkanFunctions.vkMapMemory)(
14446 m_hDevice,
14447 hMemory,
14448 0,
14449 VK_WHOLE_SIZE,
14450 0,
14451 &pMappedData);
14452 if(res < 0)
14453 {
14454 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14455 FreeVulkanMemory(memTypeIndex, size, hMemory);
14456 return res;
14457 }
14458 }
14459
14460 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14461 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14462 (*pAllocation)->SetUserData(this, pUserData);
14463 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14464 {
14465 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14466 }
14467
14468 return VK_SUCCESS;
14469}
14470
14471void VmaAllocator_T::GetBufferMemoryRequirements(
14472 VkBuffer hBuffer,
14473 VkMemoryRequirements& memReq,
14474 bool& requiresDedicatedAllocation,
14475 bool& prefersDedicatedAllocation) const
14476{
14477#if VMA_DEDICATED_ALLOCATION
14478 if(m_UseKhrDedicatedAllocation)
14479 {
14480 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14481 memReqInfo.buffer = hBuffer;
14482
14483 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14484
14485 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14486 memReq2.pNext = &memDedicatedReq;
14487
14488 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14489
14490 memReq = memReq2.memoryRequirements;
14491 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14492 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14493 }
14494 else
14495#endif // #if VMA_DEDICATED_ALLOCATION
14496 {
14497 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14498 requiresDedicatedAllocation = false;
14499 prefersDedicatedAllocation = false;
14500 }
14501}
14502
14503void VmaAllocator_T::GetImageMemoryRequirements(
14504 VkImage hImage,
14505 VkMemoryRequirements& memReq,
14506 bool& requiresDedicatedAllocation,
14507 bool& prefersDedicatedAllocation) const
14508{
14509#if VMA_DEDICATED_ALLOCATION
14510 if(m_UseKhrDedicatedAllocation)
14511 {
14512 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14513 memReqInfo.image = hImage;
14514
14515 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14516
14517 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14518 memReq2.pNext = &memDedicatedReq;
14519
14520 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14521
14522 memReq = memReq2.memoryRequirements;
14523 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14524 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14525 }
14526 else
14527#endif // #if VMA_DEDICATED_ALLOCATION
14528 {
14529 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14530 requiresDedicatedAllocation = false;
14531 prefersDedicatedAllocation = false;
14532 }
14533}
14534
14535VkResult VmaAllocator_T::AllocateMemory(
14536 const VkMemoryRequirements& vkMemReq,
14537 bool requiresDedicatedAllocation,
14538 bool prefersDedicatedAllocation,
14539 VkBuffer dedicatedBuffer,
14540 VkImage dedicatedImage,
14541 const VmaAllocationCreateInfo& createInfo,
14542 VmaSuballocationType suballocType,
14543 size_t allocationCount,
14544 VmaAllocation* pAllocations)
14545{
14546 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14547
14548 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14549
14550 if(vkMemReq.size == 0)
14551 {
14552 return VK_ERROR_VALIDATION_FAILED_EXT;
14553 }
14554 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14555 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14556 {
14557 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14558 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14559 }
14560 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14561 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14562 {
14563 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14564 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14565 }
14566 if(requiresDedicatedAllocation)
14567 {
14568 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14569 {
14570 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14571 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14572 }
14573 if(createInfo.pool != VK_NULL_HANDLE)
14574 {
14575 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14576 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14577 }
14578 }
14579 if((createInfo.pool != VK_NULL_HANDLE) &&
14580 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14581 {
14582 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14583 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14584 }
14585
14586 if(createInfo.pool != VK_NULL_HANDLE)
14587 {
14588 const VkDeviceSize alignmentForPool = VMA_MAX(
14589 vkMemReq.alignment,
14590 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14591 return createInfo.pool->m_BlockVector.Allocate(
14592 createInfo.pool,
14593 m_CurrentFrameIndex.load(),
14594 vkMemReq.size,
14595 alignmentForPool,
14596 createInfo,
14597 suballocType,
14598 allocationCount,
14599 pAllocations);
14600 }
14601 else
14602 {
14603 // Bit mask of memory Vulkan types acceptable for this allocation.
14604 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14605 uint32_t memTypeIndex = UINT32_MAX;
14606 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14607 if(res == VK_SUCCESS)
14608 {
14609 VkDeviceSize alignmentForMemType = VMA_MAX(
14610 vkMemReq.alignment,
14611 GetMemoryTypeMinAlignment(memTypeIndex));
14612
14613 res = AllocateMemoryOfType(
14614 vkMemReq.size,
14615 alignmentForMemType,
14616 requiresDedicatedAllocation || prefersDedicatedAllocation,
14617 dedicatedBuffer,
14618 dedicatedImage,
14619 createInfo,
14620 memTypeIndex,
14621 suballocType,
14622 allocationCount,
14623 pAllocations);
14624 // Succeeded on first try.
14625 if(res == VK_SUCCESS)
14626 {
14627 return res;
14628 }
14629 // Allocation from this memory type failed. Try other compatible memory types.
14630 else
14631 {
14632 for(;;)
14633 {
14634 // Remove old memTypeIndex from list of possibilities.
14635 memoryTypeBits &= ~(1u << memTypeIndex);
14636 // Find alternative memTypeIndex.
14637 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14638 if(res == VK_SUCCESS)
14639 {
14640 alignmentForMemType = VMA_MAX(
14641 vkMemReq.alignment,
14642 GetMemoryTypeMinAlignment(memTypeIndex));
14643
14644 res = AllocateMemoryOfType(
14645 vkMemReq.size,
14646 alignmentForMemType,
14647 requiresDedicatedAllocation || prefersDedicatedAllocation,
14648 dedicatedBuffer,
14649 dedicatedImage,
14650 createInfo,
14651 memTypeIndex,
14652 suballocType,
14653 allocationCount,
14654 pAllocations);
14655 // Allocation from this alternative memory type succeeded.
14656 if(res == VK_SUCCESS)
14657 {
14658 return res;
14659 }
14660 // else: Allocation from this memory type failed. Try next one - next loop iteration.
14661 }
14662 // No other matching memory type index could be found.
14663 else
14664 {
14665 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14666 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14667 }
14668 }
14669 }
14670 }
14671 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14672 else
14673 return res;
14674 }
14675}
14676
14677void VmaAllocator_T::FreeMemory(
14678 size_t allocationCount,
14679 const VmaAllocation* pAllocations)
14680{
14681 VMA_ASSERT(pAllocations);
14682
14683 for(size_t allocIndex = allocationCount; allocIndex--; )
14684 {
14685 VmaAllocation allocation = pAllocations[allocIndex];
14686
14687 if(allocation != VK_NULL_HANDLE)
14688 {
14689 if(TouchAllocation(allocation))
14690 {
14691 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14692 {
14693 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14694 }
14695
14696 switch(allocation->GetType())
14697 {
14698 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14699 {
14700 VmaBlockVector* pBlockVector = VMA_NULL;
14701 VmaPool hPool = allocation->GetPool();
14702 if(hPool != VK_NULL_HANDLE)
14703 {
14704 pBlockVector = &hPool->m_BlockVector;
14705 }
14706 else
14707 {
14708 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14709 pBlockVector = m_pBlockVectors[memTypeIndex];
14710 }
14711 pBlockVector->Free(allocation);
14712 }
14713 break;
14714 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14715 FreeDedicatedMemory(allocation);
14716 break;
14717 default:
14718 VMA_ASSERT(0);
14719 }
14720 }
14721
14722 allocation->SetUserData(this, VMA_NULL);
14723 vma_delete(this, allocation);
14724 }
14725 }
14726}
14727
14728VkResult VmaAllocator_T::ResizeAllocation(
14729 const VmaAllocation alloc,
14730 VkDeviceSize newSize)
14731{
14732 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14733 {
14734 return VK_ERROR_VALIDATION_FAILED_EXT;
14735 }
14736 if(newSize == alloc->GetSize())
14737 {
14738 return VK_SUCCESS;
14739 }
14740
14741 switch(alloc->GetType())
14742 {
14743 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14744 return VK_ERROR_FEATURE_NOT_PRESENT;
14745 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14746 if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14747 {
14748 alloc->ChangeSize(newSize);
14749 VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14750 return VK_SUCCESS;
14751 }
14752 else
14753 {
14754 return VK_ERROR_OUT_OF_POOL_MEMORY;
14755 }
14756 default:
14757 VMA_ASSERT(0);
14758 return VK_ERROR_VALIDATION_FAILED_EXT;
14759 }
14760}
14761
14762void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14763{
14764 // Initialize.
14765 InitStatInfo(pStats->total);
14766 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14767 InitStatInfo(pStats->memoryType[i]);
14768 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14769 InitStatInfo(pStats->memoryHeap[i]);
14770
14771 // Process default pools.
14772 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14773 {
14774 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14775 VMA_ASSERT(pBlockVector);
14776 pBlockVector->AddStats(pStats);
14777 }
14778
14779 // Process custom pools.
14780 {
14781 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14782 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14783 {
14784 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14785 }
14786 }
14787
14788 // Process dedicated allocations.
14789 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14790 {
14791 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14792 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14793 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14794 VMA_ASSERT(pDedicatedAllocVector);
14795 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14796 {
14797 VmaStatInfo allocationStatInfo;
14798 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14799 VmaAddStatInfo(pStats->total, allocationStatInfo);
14800 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14801 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14802 }
14803 }
14804
14805 // Postprocess.
14806 VmaPostprocessCalcStatInfo(pStats->total);
14807 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14808 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14809 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14810 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14811}
14812
14813static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14814
14815VkResult VmaAllocator_T::DefragmentationBegin(
14816 const VmaDefragmentationInfo2& info,
14817 VmaDefragmentationStats* pStats,
14818 VmaDefragmentationContext* pContext)
14819{
14820 if(info.pAllocationsChanged != VMA_NULL)
14821 {
14822 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14823 }
14824
14825 *pContext = vma_new(this, VmaDefragmentationContext_T)(
14826 this, m_CurrentFrameIndex.load(), info.flags, pStats);
14827
14828 (*pContext)->AddPools(info.poolCount, info.pPools);
14829 (*pContext)->AddAllocations(
14830 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14831
14832 VkResult res = (*pContext)->Defragment(
14833 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14834 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14835 info.commandBuffer, pStats);
14836
14837 if(res != VK_NOT_READY)
14838 {
14839 vma_delete(this, *pContext);
14840 *pContext = VMA_NULL;
14841 }
14842
14843 return res;
14844}
14845
14846VkResult VmaAllocator_T::DefragmentationEnd(
14847 VmaDefragmentationContext context)
14848{
14849 vma_delete(this, context);
14850 return VK_SUCCESS;
14851}
14852
14853void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14854{
14855 if(hAllocation->CanBecomeLost())
14856 {
14857 /*
14858 Warning: This is a carefully designed algorithm.
14859 Do not modify unless you really know what you're doing :)
14860 */
14861 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14862 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14863 for(;;)
14864 {
14865 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14866 {
14867 pAllocationInfo->memoryType = UINT32_MAX;
14868 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14869 pAllocationInfo->offset = 0;
14870 pAllocationInfo->size = hAllocation->GetSize();
14871 pAllocationInfo->pMappedData = VMA_NULL;
14872 pAllocationInfo->pUserData = hAllocation->GetUserData();
14873 return;
14874 }
14875 else if(localLastUseFrameIndex == localCurrFrameIndex)
14876 {
14877 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14878 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14879 pAllocationInfo->offset = hAllocation->GetOffset();
14880 pAllocationInfo->size = hAllocation->GetSize();
14881 pAllocationInfo->pMappedData = VMA_NULL;
14882 pAllocationInfo->pUserData = hAllocation->GetUserData();
14883 return;
14884 }
14885 else // Last use time earlier than current time.
14886 {
14887 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14888 {
14889 localLastUseFrameIndex = localCurrFrameIndex;
14890 }
14891 }
14892 }
14893 }
14894 else
14895 {
14896#if VMA_STATS_STRING_ENABLED
14897 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14898 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14899 for(;;)
14900 {
14901 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14902 if(localLastUseFrameIndex == localCurrFrameIndex)
14903 {
14904 break;
14905 }
14906 else // Last use time earlier than current time.
14907 {
14908 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14909 {
14910 localLastUseFrameIndex = localCurrFrameIndex;
14911 }
14912 }
14913 }
14914#endif
14915
14916 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14917 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14918 pAllocationInfo->offset = hAllocation->GetOffset();
14919 pAllocationInfo->size = hAllocation->GetSize();
14920 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14921 pAllocationInfo->pUserData = hAllocation->GetUserData();
14922 }
14923}
14924
14925bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14926{
14927 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14928 if(hAllocation->CanBecomeLost())
14929 {
14930 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14931 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14932 for(;;)
14933 {
14934 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14935 {
14936 return false;
14937 }
14938 else if(localLastUseFrameIndex == localCurrFrameIndex)
14939 {
14940 return true;
14941 }
14942 else // Last use time earlier than current time.
14943 {
14944 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14945 {
14946 localLastUseFrameIndex = localCurrFrameIndex;
14947 }
14948 }
14949 }
14950 }
14951 else
14952 {
14953#if VMA_STATS_STRING_ENABLED
14954 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14955 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14956 for(;;)
14957 {
14958 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14959 if(localLastUseFrameIndex == localCurrFrameIndex)
14960 {
14961 break;
14962 }
14963 else // Last use time earlier than current time.
14964 {
14965 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14966 {
14967 localLastUseFrameIndex = localCurrFrameIndex;
14968 }
14969 }
14970 }
14971#endif
14972
14973 return true;
14974 }
14975}
14976
14977VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
14978{
14979 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
14980
14981 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
14982
14983 if(newCreateInfo.maxBlockCount == 0)
14984 {
14985 newCreateInfo.maxBlockCount = SIZE_MAX;
14986 }
14987 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
14988 {
14989 return VK_ERROR_INITIALIZATION_FAILED;
14990 }
14991
14992 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
14993
14994 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
14995
14996 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
14997 if(res != VK_SUCCESS)
14998 {
14999 vma_delete(this, *pPool);
15000 *pPool = VMA_NULL;
15001 return res;
15002 }
15003
15004 // Add to m_Pools.
15005 {
15006 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15007 (*pPool)->SetId(m_NextPoolId++);
15008 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15009 }
15010
15011 return VK_SUCCESS;
15012}
15013
15014void VmaAllocator_T::DestroyPool(VmaPool pool)
15015{
15016 // Remove from m_Pools.
15017 {
15018 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15019 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15020 VMA_ASSERT(success && "Pool not found in Allocator.");
15021 }
15022
15023 vma_delete(this, pool);
15024}
15025
15026void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15027{
15028 pool->m_BlockVector.GetPoolStats(pPoolStats);
15029}
15030
15031void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15032{
15033 m_CurrentFrameIndex.store(frameIndex);
15034}
15035
15036void VmaAllocator_T::MakePoolAllocationsLost(
15037 VmaPool hPool,
15038 size_t* pLostAllocationCount)
15039{
15040 hPool->m_BlockVector.MakePoolAllocationsLost(
15041 m_CurrentFrameIndex.load(),
15042 pLostAllocationCount);
15043}
15044
15045VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15046{
15047 return hPool->m_BlockVector.CheckCorruption();
15048}
15049
15050VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15051{
15052 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15053
15054 // Process default pools.
15055 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15056 {
15057 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15058 {
15059 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15060 VMA_ASSERT(pBlockVector);
15061 VkResult localRes = pBlockVector->CheckCorruption();
15062 switch(localRes)
15063 {
15064 case VK_ERROR_FEATURE_NOT_PRESENT:
15065 break;
15066 case VK_SUCCESS:
15067 finalRes = VK_SUCCESS;
15068 break;
15069 default:
15070 return localRes;
15071 }
15072 }
15073 }
15074
15075 // Process custom pools.
15076 {
15077 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15078 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15079 {
15080 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15081 {
15082 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15083 switch(localRes)
15084 {
15085 case VK_ERROR_FEATURE_NOT_PRESENT:
15086 break;
15087 case VK_SUCCESS:
15088 finalRes = VK_SUCCESS;
15089 break;
15090 default:
15091 return localRes;
15092 }
15093 }
15094 }
15095 }
15096
15097 return finalRes;
15098}
15099
15100void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15101{
15102 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15103 (*pAllocation)->InitLost();
15104}
15105
15106VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15107{
15108 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15109
15110 VkResult res;
15111 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15112 {
15113 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15114 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15115 {
15116 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15117 if(res == VK_SUCCESS)
15118 {
15119 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15120 }
15121 }
15122 else
15123 {
15124 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15125 }
15126 }
15127 else
15128 {
15129 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15130 }
15131
15132 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15133 {
15134 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15135 }
15136
15137 return res;
15138}
15139
15140void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15141{
15142 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15143 {
15144 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15145 }
15146
15147 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15148
15149 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15150 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15151 {
15152 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15153 m_HeapSizeLimit[heapIndex] += size;
15154 }
15155}
15156
15157VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15158{
15159 if(hAllocation->CanBecomeLost())
15160 {
15161 return VK_ERROR_MEMORY_MAP_FAILED;
15162 }
15163
15164 switch(hAllocation->GetType())
15165 {
15166 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15167 {
15168 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15169 char *pBytes = VMA_NULL;
15170 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15171 if(res == VK_SUCCESS)
15172 {
15173 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15174 hAllocation->BlockAllocMap();
15175 }
15176 return res;
15177 }
15178 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15179 return hAllocation->DedicatedAllocMap(this, ppData);
15180 default:
15181 VMA_ASSERT(0);
15182 return VK_ERROR_MEMORY_MAP_FAILED;
15183 }
15184}
15185
15186void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15187{
15188 switch(hAllocation->GetType())
15189 {
15190 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15191 {
15192 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15193 hAllocation->BlockAllocUnmap();
15194 pBlock->Unmap(this, 1);
15195 }
15196 break;
15197 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15198 hAllocation->DedicatedAllocUnmap(this);
15199 break;
15200 default:
15201 VMA_ASSERT(0);
15202 }
15203}
15204
15205VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15206{
15207 VkResult res = VK_SUCCESS;
15208 switch(hAllocation->GetType())
15209 {
15210 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15211 res = GetVulkanFunctions().vkBindBufferMemory(
15212 m_hDevice,
15213 hBuffer,
15214 hAllocation->GetMemory(),
15215 0); //memoryOffset
15216 break;
15217 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15218 {
15219 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15220 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15221 res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15222 break;
15223 }
15224 default:
15225 VMA_ASSERT(0);
15226 }
15227 return res;
15228}
15229
15230VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15231{
15232 VkResult res = VK_SUCCESS;
15233 switch(hAllocation->GetType())
15234 {
15235 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15236 res = GetVulkanFunctions().vkBindImageMemory(
15237 m_hDevice,
15238 hImage,
15239 hAllocation->GetMemory(),
15240 0); //memoryOffset
15241 break;
15242 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15243 {
15244 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15245 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15246 res = pBlock->BindImageMemory(this, hAllocation, hImage);
15247 break;
15248 }
15249 default:
15250 VMA_ASSERT(0);
15251 }
15252 return res;
15253}
15254
15255void VmaAllocator_T::FlushOrInvalidateAllocation(
15256 VmaAllocation hAllocation,
15257 VkDeviceSize offset, VkDeviceSize size,
15258 VMA_CACHE_OPERATION op)
15259{
15260 const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15261 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15262 {
15263 const VkDeviceSize allocationSize = hAllocation->GetSize();
15264 VMA_ASSERT(offset <= allocationSize);
15265
15266 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15267
15268 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15269 memRange.memory = hAllocation->GetMemory();
15270
15271 switch(hAllocation->GetType())
15272 {
15273 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15274 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15275 if(size == VK_WHOLE_SIZE)
15276 {
15277 memRange.size = allocationSize - memRange.offset;
15278 }
15279 else
15280 {
15281 VMA_ASSERT(offset + size <= allocationSize);
15282 memRange.size = VMA_MIN(
15283 VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15284 allocationSize - memRange.offset);
15285 }
15286 break;
15287
15288 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15289 {
15290 // 1. Still within this allocation.
15291 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15292 if(size == VK_WHOLE_SIZE)
15293 {
15294 size = allocationSize - offset;
15295 }
15296 else
15297 {
15298 VMA_ASSERT(offset + size <= allocationSize);
15299 }
15300 memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15301
15302 // 2. Adjust to whole block.
15303 const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15304 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15305 const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15306 memRange.offset += allocationOffset;
15307 memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15308
15309 break;
15310 }
15311
15312 default:
15313 VMA_ASSERT(0);
15314 }
15315
15316 switch(op)
15317 {
15318 case VMA_CACHE_FLUSH:
15319 (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15320 break;
15321 case VMA_CACHE_INVALIDATE:
15322 (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15323 break;
15324 default:
15325 VMA_ASSERT(0);
15326 }
15327 }
15328 // else: Just ignore this call.
15329}
15330
15331void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15332{
15333 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15334
15335 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15336 {
15337 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15338 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15339 VMA_ASSERT(pDedicatedAllocations);
15340 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15341 VMA_ASSERT(success);
15342 }
15343
15344 VkDeviceMemory hMemory = allocation->GetMemory();
15345
15346 /*
15347 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15348 before vkFreeMemory.
15349
15350 if(allocation->GetMappedData() != VMA_NULL)
15351 {
15352 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15353 }
15354 */
15355
15356 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15357
15358 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15359}
15360
15361void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15362{
15363 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15364 !hAllocation->CanBecomeLost() &&
15365 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15366 {
15367 void* pData = VMA_NULL;
15368 VkResult res = Map(hAllocation, &pData);
15369 if(res == VK_SUCCESS)
15370 {
15371 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15372 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15373 Unmap(hAllocation);
15374 }
15375 else
15376 {
15377 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15378 }
15379 }
15380}
15381
15382#if VMA_STATS_STRING_ENABLED
15383
15384void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15385{
15386 bool dedicatedAllocationsStarted = false;
15387 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15388 {
15389 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15390 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15391 VMA_ASSERT(pDedicatedAllocVector);
15392 if(pDedicatedAllocVector->empty() == false)
15393 {
15394 if(dedicatedAllocationsStarted == false)
15395 {
15396 dedicatedAllocationsStarted = true;
15397 json.WriteString("DedicatedAllocations");
15398 json.BeginObject();
15399 }
15400
15401 json.BeginString("Type ");
15402 json.ContinueString(memTypeIndex);
15403 json.EndString();
15404
15405 json.BeginArray();
15406
15407 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15408 {
15409 json.BeginObject(true);
15410 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15411 hAlloc->PrintParameters(json);
15412 json.EndObject();
15413 }
15414
15415 json.EndArray();
15416 }
15417 }
15418 if(dedicatedAllocationsStarted)
15419 {
15420 json.EndObject();
15421 }
15422
15423 {
15424 bool allocationsStarted = false;
15425 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15426 {
15427 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15428 {
15429 if(allocationsStarted == false)
15430 {
15431 allocationsStarted = true;
15432 json.WriteString("DefaultPools");
15433 json.BeginObject();
15434 }
15435
15436 json.BeginString("Type ");
15437 json.ContinueString(memTypeIndex);
15438 json.EndString();
15439
15440 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15441 }
15442 }
15443 if(allocationsStarted)
15444 {
15445 json.EndObject();
15446 }
15447 }
15448
15449 // Custom pools
15450 {
15451 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15452 const size_t poolCount = m_Pools.size();
15453 if(poolCount > 0)
15454 {
15455 json.WriteString("Pools");
15456 json.BeginObject();
15457 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15458 {
15459 json.BeginString();
15460 json.ContinueString(m_Pools[poolIndex]->GetId());
15461 json.EndString();
15462
15463 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15464 }
15465 json.EndObject();
15466 }
15467 }
15468}
15469
15470#endif // #if VMA_STATS_STRING_ENABLED
15471
15472////////////////////////////////////////////////////////////////////////////////
15473// Public interface
15474
15475VkResult vmaCreateAllocator(
15476 const VmaAllocatorCreateInfo* pCreateInfo,
15477 VmaAllocator* pAllocator)
15478{
15479 VMA_ASSERT(pCreateInfo && pAllocator);
15480 VMA_DEBUG_LOG("vmaCreateAllocator");
15481 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15482 return (*pAllocator)->Init(pCreateInfo);
15483}
15484
15485void vmaDestroyAllocator(
15486 VmaAllocator allocator)
15487{
15488 if(allocator != VK_NULL_HANDLE)
15489 {
15490 VMA_DEBUG_LOG("vmaDestroyAllocator");
15491 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15492 vma_delete(&allocationCallbacks, allocator);
15493 }
15494}
15495
15496void vmaGetPhysicalDeviceProperties(
15497 VmaAllocator allocator,
15498 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15499{
15500 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15501 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15502}
15503
15504void vmaGetMemoryProperties(
15505 VmaAllocator allocator,
15506 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15507{
15508 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15509 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15510}
15511
15512void vmaGetMemoryTypeProperties(
15513 VmaAllocator allocator,
15514 uint32_t memoryTypeIndex,
15515 VkMemoryPropertyFlags* pFlags)
15516{
15517 VMA_ASSERT(allocator && pFlags);
15518 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15519 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15520}
15521
15522void vmaSetCurrentFrameIndex(
15523 VmaAllocator allocator,
15524 uint32_t frameIndex)
15525{
15526 VMA_ASSERT(allocator);
15527 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15528
15529 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15530
15531 allocator->SetCurrentFrameIndex(frameIndex);
15532}
15533
15534void vmaCalculateStats(
15535 VmaAllocator allocator,
15536 VmaStats* pStats)
15537{
15538 VMA_ASSERT(allocator && pStats);
15539 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15540 allocator->CalculateStats(pStats);
15541}
15542
15543#if VMA_STATS_STRING_ENABLED
15544
15545void vmaBuildStatsString(
15546 VmaAllocator allocator,
15547 char** ppStatsString,
15548 VkBool32 detailedMap)
15549{
15550 VMA_ASSERT(allocator && ppStatsString);
15551 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15552
15553 VmaStringBuilder sb(allocator);
15554 {
15555 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15556 json.BeginObject();
15557
15558 VmaStats stats;
15559 allocator->CalculateStats(&stats);
15560
15561 json.WriteString("Total");
15562 VmaPrintStatInfo(json, stats.total);
15563
15564 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15565 {
15566 json.BeginString("Heap ");
15567 json.ContinueString(heapIndex);
15568 json.EndString();
15569 json.BeginObject();
15570
15571 json.WriteString("Size");
15572 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15573
15574 json.WriteString("Flags");
15575 json.BeginArray(true);
15576 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15577 {
15578 json.WriteString("DEVICE_LOCAL");
15579 }
15580 json.EndArray();
15581
15582 if(stats.memoryHeap[heapIndex].blockCount > 0)
15583 {
15584 json.WriteString("Stats");
15585 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15586 }
15587
15588 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15589 {
15590 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15591 {
15592 json.BeginString("Type ");
15593 json.ContinueString(typeIndex);
15594 json.EndString();
15595
15596 json.BeginObject();
15597
15598 json.WriteString("Flags");
15599 json.BeginArray(true);
15600 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15601 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15602 {
15603 json.WriteString("DEVICE_LOCAL");
15604 }
15605 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15606 {
15607 json.WriteString("HOST_VISIBLE");
15608 }
15609 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15610 {
15611 json.WriteString("HOST_COHERENT");
15612 }
15613 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15614 {
15615 json.WriteString("HOST_CACHED");
15616 }
15617 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15618 {
15619 json.WriteString("LAZILY_ALLOCATED");
15620 }
15621 json.EndArray();
15622
15623 if(stats.memoryType[typeIndex].blockCount > 0)
15624 {
15625 json.WriteString("Stats");
15626 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15627 }
15628
15629 json.EndObject();
15630 }
15631 }
15632
15633 json.EndObject();
15634 }
15635 if(detailedMap == VK_TRUE)
15636 {
15637 allocator->PrintDetailedMap(json);
15638 }
15639
15640 json.EndObject();
15641 }
15642
15643 const size_t len = sb.GetLength();
15644 char* const pChars = vma_new_array(allocator, char, len + 1);
15645 if(len > 0)
15646 {
15647 memcpy(pChars, sb.GetData(), len);
15648 }
15649 pChars[len] = '\0';
15650 *ppStatsString = pChars;
15651}
15652
15653void vmaFreeStatsString(
15654 VmaAllocator allocator,
15655 char* pStatsString)
15656{
15657 if(pStatsString != VMA_NULL)
15658 {
15659 VMA_ASSERT(allocator);
15660 size_t len = strlen(pStatsString);
15661 vma_delete_array(allocator, pStatsString, len + 1);
15662 }
15663}
15664
15665#endif // #if VMA_STATS_STRING_ENABLED
15666
15667/*
15668This function is not protected by any mutex because it just reads immutable data.
15669*/
15670VkResult vmaFindMemoryTypeIndex(
15671 VmaAllocator allocator,
15672 uint32_t memoryTypeBits,
15673 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15674 uint32_t* pMemoryTypeIndex)
15675{
15676 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15677 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15678 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15679
15680 if(pAllocationCreateInfo->memoryTypeBits != 0)
15681 {
15682 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15683 }
15684
15685 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15686 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15687
15688 const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15689 if(mapped)
15690 {
15691 preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15692 }
15693
15694 // Convert usage to requiredFlags and preferredFlags.
15695 switch(pAllocationCreateInfo->usage)
15696 {
15697 case VMA_MEMORY_USAGE_UNKNOWN:
15698 break;
15699 case VMA_MEMORY_USAGE_GPU_ONLY:
15700 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15701 {
15702 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15703 }
15704 break;
15705 case VMA_MEMORY_USAGE_CPU_ONLY:
15706 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15707 break;
15708 case VMA_MEMORY_USAGE_CPU_TO_GPU:
15709 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15710 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15711 {
15712 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15713 }
15714 break;
15715 case VMA_MEMORY_USAGE_GPU_TO_CPU:
15716 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15717 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15718 break;
15719 default:
15720 break;
15721 }
15722
15723 *pMemoryTypeIndex = UINT32_MAX;
15724 uint32_t minCost = UINT32_MAX;
15725 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15726 memTypeIndex < allocator->GetMemoryTypeCount();
15727 ++memTypeIndex, memTypeBit <<= 1)
15728 {
15729 // This memory type is acceptable according to memoryTypeBits bitmask.
15730 if((memTypeBit & memoryTypeBits) != 0)
15731 {
15732 const VkMemoryPropertyFlags currFlags =
15733 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15734 // This memory type contains requiredFlags.
15735 if((requiredFlags & ~currFlags) == 0)
15736 {
15737 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15738 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15739 // Remember memory type with lowest cost.
15740 if(currCost < minCost)
15741 {
15742 *pMemoryTypeIndex = memTypeIndex;
15743 if(currCost == 0)
15744 {
15745 return VK_SUCCESS;
15746 }
15747 minCost = currCost;
15748 }
15749 }
15750 }
15751 }
15752 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15753}
15754
15755VkResult vmaFindMemoryTypeIndexForBufferInfo(
15756 VmaAllocator allocator,
15757 const VkBufferCreateInfo* pBufferCreateInfo,
15758 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15759 uint32_t* pMemoryTypeIndex)
15760{
15761 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15762 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15763 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15764 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15765
15766 const VkDevice hDev = allocator->m_hDevice;
15767 VkBuffer hBuffer = VK_NULL_HANDLE;
15768 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15769 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15770 if(res == VK_SUCCESS)
15771 {
15772 VkMemoryRequirements memReq = {};
15773 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15774 hDev, hBuffer, &memReq);
15775
15776 res = vmaFindMemoryTypeIndex(
15777 allocator,
15778 memReq.memoryTypeBits,
15779 pAllocationCreateInfo,
15780 pMemoryTypeIndex);
15781
15782 allocator->GetVulkanFunctions().vkDestroyBuffer(
15783 hDev, hBuffer, allocator->GetAllocationCallbacks());
15784 }
15785 return res;
15786}
15787
15788VkResult vmaFindMemoryTypeIndexForImageInfo(
15789 VmaAllocator allocator,
15790 const VkImageCreateInfo* pImageCreateInfo,
15791 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15792 uint32_t* pMemoryTypeIndex)
15793{
15794 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15795 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15796 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15797 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15798
15799 const VkDevice hDev = allocator->m_hDevice;
15800 VkImage hImage = VK_NULL_HANDLE;
15801 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15802 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15803 if(res == VK_SUCCESS)
15804 {
15805 VkMemoryRequirements memReq = {};
15806 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15807 hDev, hImage, &memReq);
15808
15809 res = vmaFindMemoryTypeIndex(
15810 allocator,
15811 memReq.memoryTypeBits,
15812 pAllocationCreateInfo,
15813 pMemoryTypeIndex);
15814
15815 allocator->GetVulkanFunctions().vkDestroyImage(
15816 hDev, hImage, allocator->GetAllocationCallbacks());
15817 }
15818 return res;
15819}
15820
15821VkResult vmaCreatePool(
15822 VmaAllocator allocator,
15823 const VmaPoolCreateInfo* pCreateInfo,
15824 VmaPool* pPool)
15825{
15826 VMA_ASSERT(allocator && pCreateInfo && pPool);
15827
15828 VMA_DEBUG_LOG("vmaCreatePool");
15829
15830 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15831
15832 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15833
15834#if VMA_RECORDING_ENABLED
15835 if(allocator->GetRecorder() != VMA_NULL)
15836 {
15837 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15838 }
15839#endif
15840
15841 return res;
15842}
15843
15844void vmaDestroyPool(
15845 VmaAllocator allocator,
15846 VmaPool pool)
15847{
15848 VMA_ASSERT(allocator);
15849
15850 if(pool == VK_NULL_HANDLE)
15851 {
15852 return;
15853 }
15854
15855 VMA_DEBUG_LOG("vmaDestroyPool");
15856
15857 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15858
15859#if VMA_RECORDING_ENABLED
15860 if(allocator->GetRecorder() != VMA_NULL)
15861 {
15862 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15863 }
15864#endif
15865
15866 allocator->DestroyPool(pool);
15867}
15868
15869void vmaGetPoolStats(
15870 VmaAllocator allocator,
15871 VmaPool pool,
15872 VmaPoolStats* pPoolStats)
15873{
15874 VMA_ASSERT(allocator && pool && pPoolStats);
15875
15876 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15877
15878 allocator->GetPoolStats(pool, pPoolStats);
15879}
15880
15881void vmaMakePoolAllocationsLost(
15882 VmaAllocator allocator,
15883 VmaPool pool,
15884 size_t* pLostAllocationCount)
15885{
15886 VMA_ASSERT(allocator && pool);
15887
15888 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15889
15890#if VMA_RECORDING_ENABLED
15891 if(allocator->GetRecorder() != VMA_NULL)
15892 {
15893 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15894 }
15895#endif
15896
15897 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15898}
15899
15900VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15901{
15902 VMA_ASSERT(allocator && pool);
15903
15904 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15905
15906 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15907
15908 return allocator->CheckPoolCorruption(pool);
15909}
15910
15911VkResult vmaAllocateMemory(
15912 VmaAllocator allocator,
15913 const VkMemoryRequirements* pVkMemoryRequirements,
15914 const VmaAllocationCreateInfo* pCreateInfo,
15915 VmaAllocation* pAllocation,
15916 VmaAllocationInfo* pAllocationInfo)
15917{
15918 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15919
15920 VMA_DEBUG_LOG("vmaAllocateMemory");
15921
15922 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15923
15924 VkResult result = allocator->AllocateMemory(
15925 *pVkMemoryRequirements,
15926 false, // requiresDedicatedAllocation
15927 false, // prefersDedicatedAllocation
15928 VK_NULL_HANDLE, // dedicatedBuffer
15929 VK_NULL_HANDLE, // dedicatedImage
15930 *pCreateInfo,
15931 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15932 1, // allocationCount
15933 pAllocation);
15934
15935#if VMA_RECORDING_ENABLED
15936 if(allocator->GetRecorder() != VMA_NULL)
15937 {
15938 allocator->GetRecorder()->RecordAllocateMemory(
15939 allocator->GetCurrentFrameIndex(),
15940 *pVkMemoryRequirements,
15941 *pCreateInfo,
15942 *pAllocation);
15943 }
15944#endif
15945
15946 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15947 {
15948 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15949 }
15950
15951 return result;
15952}
15953
15954VkResult vmaAllocateMemoryPages(
15955 VmaAllocator allocator,
15956 const VkMemoryRequirements* pVkMemoryRequirements,
15957 const VmaAllocationCreateInfo* pCreateInfo,
15958 size_t allocationCount,
15959 VmaAllocation* pAllocations,
15960 VmaAllocationInfo* pAllocationInfo)
15961{
15962 if(allocationCount == 0)
15963 {
15964 return VK_SUCCESS;
15965 }
15966
15967 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15968
15969 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15970
15971 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15972
15973 VkResult result = allocator->AllocateMemory(
15974 *pVkMemoryRequirements,
15975 false, // requiresDedicatedAllocation
15976 false, // prefersDedicatedAllocation
15977 VK_NULL_HANDLE, // dedicatedBuffer
15978 VK_NULL_HANDLE, // dedicatedImage
15979 *pCreateInfo,
15980 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15981 allocationCount,
15982 pAllocations);
15983
15984#if VMA_RECORDING_ENABLED
15985 if(allocator->GetRecorder() != VMA_NULL)
15986 {
15987 allocator->GetRecorder()->RecordAllocateMemoryPages(
15988 allocator->GetCurrentFrameIndex(),
15989 *pVkMemoryRequirements,
15990 *pCreateInfo,
15991 (uint64_t)allocationCount,
15992 pAllocations);
15993 }
15994#endif
15995
15996 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15997 {
15998 for(size_t i = 0; i < allocationCount; ++i)
15999 {
16000 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16001 }
16002 }
16003
16004 return result;
16005}
16006
16007VkResult vmaAllocateMemoryForBuffer(
16008 VmaAllocator allocator,
16009 VkBuffer buffer,
16010 const VmaAllocationCreateInfo* pCreateInfo,
16011 VmaAllocation* pAllocation,
16012 VmaAllocationInfo* pAllocationInfo)
16013{
16014 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16015
16016 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16017
16018 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16019
16020 VkMemoryRequirements vkMemReq = {};
16021 bool requiresDedicatedAllocation = false;
16022 bool prefersDedicatedAllocation = false;
16023 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16024 requiresDedicatedAllocation,
16025 prefersDedicatedAllocation);
16026
16027 VkResult result = allocator->AllocateMemory(
16028 vkMemReq,
16029 requiresDedicatedAllocation,
16030 prefersDedicatedAllocation,
16031 buffer, // dedicatedBuffer
16032 VK_NULL_HANDLE, // dedicatedImage
16033 *pCreateInfo,
16034 VMA_SUBALLOCATION_TYPE_BUFFER,
16035 1, // allocationCount
16036 pAllocation);
16037
16038#if VMA_RECORDING_ENABLED
16039 if(allocator->GetRecorder() != VMA_NULL)
16040 {
16041 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16042 allocator->GetCurrentFrameIndex(),
16043 vkMemReq,
16044 requiresDedicatedAllocation,
16045 prefersDedicatedAllocation,
16046 *pCreateInfo,
16047 *pAllocation);
16048 }
16049#endif
16050
16051 if(pAllocationInfo && result == VK_SUCCESS)
16052 {
16053 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16054 }
16055
16056 return result;
16057}
16058
16059VkResult vmaAllocateMemoryForImage(
16060 VmaAllocator allocator,
16061 VkImage image,
16062 const VmaAllocationCreateInfo* pCreateInfo,
16063 VmaAllocation* pAllocation,
16064 VmaAllocationInfo* pAllocationInfo)
16065{
16066 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16067
16068 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16069
16070 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16071
16072 VkMemoryRequirements vkMemReq = {};
16073 bool requiresDedicatedAllocation = false;
16074 bool prefersDedicatedAllocation = false;
16075 allocator->GetImageMemoryRequirements(image, vkMemReq,
16076 requiresDedicatedAllocation, prefersDedicatedAllocation);
16077
16078 VkResult result = allocator->AllocateMemory(
16079 vkMemReq,
16080 requiresDedicatedAllocation,
16081 prefersDedicatedAllocation,
16082 VK_NULL_HANDLE, // dedicatedBuffer
16083 image, // dedicatedImage
16084 *pCreateInfo,
16085 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16086 1, // allocationCount
16087 pAllocation);
16088
16089#if VMA_RECORDING_ENABLED
16090 if(allocator->GetRecorder() != VMA_NULL)
16091 {
16092 allocator->GetRecorder()->RecordAllocateMemoryForImage(
16093 allocator->GetCurrentFrameIndex(),
16094 vkMemReq,
16095 requiresDedicatedAllocation,
16096 prefersDedicatedAllocation,
16097 *pCreateInfo,
16098 *pAllocation);
16099 }
16100#endif
16101
16102 if(pAllocationInfo && result == VK_SUCCESS)
16103 {
16104 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16105 }
16106
16107 return result;
16108}
16109
16110void vmaFreeMemory(
16111 VmaAllocator allocator,
16112 VmaAllocation allocation)
16113{
16114 VMA_ASSERT(allocator);
16115
16116 if(allocation == VK_NULL_HANDLE)
16117 {
16118 return;
16119 }
16120
16121 VMA_DEBUG_LOG("vmaFreeMemory");
16122
16123 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16124
16125#if VMA_RECORDING_ENABLED
16126 if(allocator->GetRecorder() != VMA_NULL)
16127 {
16128 allocator->GetRecorder()->RecordFreeMemory(
16129 allocator->GetCurrentFrameIndex(),
16130 allocation);
16131 }
16132#endif
16133
16134 allocator->FreeMemory(
16135 1, // allocationCount
16136 &allocation);
16137}
16138
16139void vmaFreeMemoryPages(
16140 VmaAllocator allocator,
16141 size_t allocationCount,
16142 VmaAllocation* pAllocations)
16143{
16144 if(allocationCount == 0)
16145 {
16146 return;
16147 }
16148
16149 VMA_ASSERT(allocator);
16150
16151 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16152
16153 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16154
16155#if VMA_RECORDING_ENABLED
16156 if(allocator->GetRecorder() != VMA_NULL)
16157 {
16158 allocator->GetRecorder()->RecordFreeMemoryPages(
16159 allocator->GetCurrentFrameIndex(),
16160 (uint64_t)allocationCount,
16161 pAllocations);
16162 }
16163#endif
16164
16165 allocator->FreeMemory(allocationCount, pAllocations);
16166}
16167
16168VkResult vmaResizeAllocation(
16169 VmaAllocator allocator,
16170 VmaAllocation allocation,
16171 VkDeviceSize newSize)
16172{
16173 VMA_ASSERT(allocator && allocation);
16174
16175 VMA_DEBUG_LOG("vmaResizeAllocation");
16176
16177 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16178
16179#if VMA_RECORDING_ENABLED
16180 if(allocator->GetRecorder() != VMA_NULL)
16181 {
16182 allocator->GetRecorder()->RecordResizeAllocation(
16183 allocator->GetCurrentFrameIndex(),
16184 allocation,
16185 newSize);
16186 }
16187#endif
16188
16189 return allocator->ResizeAllocation(allocation, newSize);
16190}
16191
16192void vmaGetAllocationInfo(
16193 VmaAllocator allocator,
16194 VmaAllocation allocation,
16195 VmaAllocationInfo* pAllocationInfo)
16196{
16197 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16198
16199 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16200
16201#if VMA_RECORDING_ENABLED
16202 if(allocator->GetRecorder() != VMA_NULL)
16203 {
16204 allocator->GetRecorder()->RecordGetAllocationInfo(
16205 allocator->GetCurrentFrameIndex(),
16206 allocation);
16207 }
16208#endif
16209
16210 allocator->GetAllocationInfo(allocation, pAllocationInfo);
16211}
16212
16213VkBool32 vmaTouchAllocation(
16214 VmaAllocator allocator,
16215 VmaAllocation allocation)
16216{
16217 VMA_ASSERT(allocator && allocation);
16218
16219 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16220
16221#if VMA_RECORDING_ENABLED
16222 if(allocator->GetRecorder() != VMA_NULL)
16223 {
16224 allocator->GetRecorder()->RecordTouchAllocation(
16225 allocator->GetCurrentFrameIndex(),
16226 allocation);
16227 }
16228#endif
16229
16230 return allocator->TouchAllocation(allocation);
16231}
16232
16233void vmaSetAllocationUserData(
16234 VmaAllocator allocator,
16235 VmaAllocation allocation,
16236 void* pUserData)
16237{
16238 VMA_ASSERT(allocator && allocation);
16239
16240 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16241
16242 allocation->SetUserData(allocator, pUserData);
16243
16244#if VMA_RECORDING_ENABLED
16245 if(allocator->GetRecorder() != VMA_NULL)
16246 {
16247 allocator->GetRecorder()->RecordSetAllocationUserData(
16248 allocator->GetCurrentFrameIndex(),
16249 allocation,
16250 pUserData);
16251 }
16252#endif
16253}
16254
16255void vmaCreateLostAllocation(
16256 VmaAllocator allocator,
16257 VmaAllocation* pAllocation)
16258{
16259 VMA_ASSERT(allocator && pAllocation);
16260
16261 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16262
16263 allocator->CreateLostAllocation(pAllocation);
16264
16265#if VMA_RECORDING_ENABLED
16266 if(allocator->GetRecorder() != VMA_NULL)
16267 {
16268 allocator->GetRecorder()->RecordCreateLostAllocation(
16269 allocator->GetCurrentFrameIndex(),
16270 *pAllocation);
16271 }
16272#endif
16273}
16274
16275VkResult vmaMapMemory(
16276 VmaAllocator allocator,
16277 VmaAllocation allocation,
16278 void** ppData)
16279{
16280 VMA_ASSERT(allocator && allocation && ppData);
16281
16282 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16283
16284 VkResult res = allocator->Map(allocation, ppData);
16285
16286#if VMA_RECORDING_ENABLED
16287 if(allocator->GetRecorder() != VMA_NULL)
16288 {
16289 allocator->GetRecorder()->RecordMapMemory(
16290 allocator->GetCurrentFrameIndex(),
16291 allocation);
16292 }
16293#endif
16294
16295 return res;
16296}
16297
16298void vmaUnmapMemory(
16299 VmaAllocator allocator,
16300 VmaAllocation allocation)
16301{
16302 VMA_ASSERT(allocator && allocation);
16303
16304 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16305
16306#if VMA_RECORDING_ENABLED
16307 if(allocator->GetRecorder() != VMA_NULL)
16308 {
16309 allocator->GetRecorder()->RecordUnmapMemory(
16310 allocator->GetCurrentFrameIndex(),
16311 allocation);
16312 }
16313#endif
16314
16315 allocator->Unmap(allocation);
16316}
16317
16318void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16319{
16320 VMA_ASSERT(allocator && allocation);
16321
16322 VMA_DEBUG_LOG("vmaFlushAllocation");
16323
16324 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16325
16326 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16327
16328#if VMA_RECORDING_ENABLED
16329 if(allocator->GetRecorder() != VMA_NULL)
16330 {
16331 allocator->GetRecorder()->RecordFlushAllocation(
16332 allocator->GetCurrentFrameIndex(),
16333 allocation, offset, size);
16334 }
16335#endif
16336}
16337
16338void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16339{
16340 VMA_ASSERT(allocator && allocation);
16341
16342 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16343
16344 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16345
16346 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16347
16348#if VMA_RECORDING_ENABLED
16349 if(allocator->GetRecorder() != VMA_NULL)
16350 {
16351 allocator->GetRecorder()->RecordInvalidateAllocation(
16352 allocator->GetCurrentFrameIndex(),
16353 allocation, offset, size);
16354 }
16355#endif
16356}
16357
16358VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16359{
16360 VMA_ASSERT(allocator);
16361
16362 VMA_DEBUG_LOG("vmaCheckCorruption");
16363
16364 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16365
16366 return allocator->CheckCorruption(memoryTypeBits);
16367}
16368
16369VkResult vmaDefragment(
16370 VmaAllocator allocator,
16371 VmaAllocation* pAllocations,
16372 size_t allocationCount,
16373 VkBool32* pAllocationsChanged,
16374 const VmaDefragmentationInfo *pDefragmentationInfo,
16375 VmaDefragmentationStats* pDefragmentationStats)
16376{
16377 // Deprecated interface, reimplemented using new one.
16378
16379 VmaDefragmentationInfo2 info2 = {};
16380 info2.allocationCount = (uint32_t)allocationCount;
16381 info2.pAllocations = pAllocations;
16382 info2.pAllocationsChanged = pAllocationsChanged;
16383 if(pDefragmentationInfo != VMA_NULL)
16384 {
16385 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16386 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16387 }
16388 else
16389 {
16390 info2.maxCpuAllocationsToMove = UINT32_MAX;
16391 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16392 }
16393 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16394
16395 VmaDefragmentationContext ctx;
16396 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16397 if(res == VK_NOT_READY)
16398 {
16399 res = vmaDefragmentationEnd( allocator, ctx);
16400 }
16401 return res;
16402}
16403
16404VkResult vmaDefragmentationBegin(
16405 VmaAllocator allocator,
16406 const VmaDefragmentationInfo2* pInfo,
16407 VmaDefragmentationStats* pStats,
16408 VmaDefragmentationContext *pContext)
16409{
16410 VMA_ASSERT(allocator && pInfo && pContext);
16411
16412 // Degenerate case: Nothing to defragment.
16413 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16414 {
16415 return VK_SUCCESS;
16416 }
16417
16418 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16419 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16420 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16421 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16422
16423 VMA_DEBUG_LOG("vmaDefragmentationBegin");
16424
16425 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16426
16427 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16428
16429#if VMA_RECORDING_ENABLED
16430 if(allocator->GetRecorder() != VMA_NULL)
16431 {
16432 allocator->GetRecorder()->RecordDefragmentationBegin(
16433 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16434 }
16435#endif
16436
16437 return res;
16438}
16439
16440VkResult vmaDefragmentationEnd(
16441 VmaAllocator allocator,
16442 VmaDefragmentationContext context)
16443{
16444 VMA_ASSERT(allocator);
16445
16446 VMA_DEBUG_LOG("vmaDefragmentationEnd");
16447
16448 if(context != VK_NULL_HANDLE)
16449 {
16450 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16451
16452#if VMA_RECORDING_ENABLED
16453 if(allocator->GetRecorder() != VMA_NULL)
16454 {
16455 allocator->GetRecorder()->RecordDefragmentationEnd(
16456 allocator->GetCurrentFrameIndex(), context);
16457 }
16458#endif
16459
16460 return allocator->DefragmentationEnd(context);
16461 }
16462 else
16463 {
16464 return VK_SUCCESS;
16465 }
16466}
16467
16468VkResult vmaBindBufferMemory(
16469 VmaAllocator allocator,
16470 VmaAllocation allocation,
16471 VkBuffer buffer)
16472{
16473 VMA_ASSERT(allocator && allocation && buffer);
16474
16475 VMA_DEBUG_LOG("vmaBindBufferMemory");
16476
16477 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16478
16479 return allocator->BindBufferMemory(allocation, buffer);
16480}
16481
16482VkResult vmaBindImageMemory(
16483 VmaAllocator allocator,
16484 VmaAllocation allocation,
16485 VkImage image)
16486{
16487 VMA_ASSERT(allocator && allocation && image);
16488
16489 VMA_DEBUG_LOG("vmaBindImageMemory");
16490
16491 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16492
16493 return allocator->BindImageMemory(allocation, image);
16494}
16495
16496VkResult vmaCreateBuffer(
16497 VmaAllocator allocator,
16498 const VkBufferCreateInfo* pBufferCreateInfo,
16499 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16500 VkBuffer* pBuffer,
16501 VmaAllocation* pAllocation,
16502 VmaAllocationInfo* pAllocationInfo)
16503{
16504 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16505
16506 if(pBufferCreateInfo->size == 0)
16507 {
16508 return VK_ERROR_VALIDATION_FAILED_EXT;
16509 }
16510
16511 VMA_DEBUG_LOG("vmaCreateBuffer");
16512
16513 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16514
16515 *pBuffer = VK_NULL_HANDLE;
16516 *pAllocation = VK_NULL_HANDLE;
16517
16518 // 1. Create VkBuffer.
16519 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16520 allocator->m_hDevice,
16521 pBufferCreateInfo,
16522 allocator->GetAllocationCallbacks(),
16523 pBuffer);
16524 if(res >= 0)
16525 {
16526 // 2. vkGetBufferMemoryRequirements.
16527 VkMemoryRequirements vkMemReq = {};
16528 bool requiresDedicatedAllocation = false;
16529 bool prefersDedicatedAllocation = false;
16530 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16531 requiresDedicatedAllocation, prefersDedicatedAllocation);
16532
16533 // Make sure alignment requirements for specific buffer usages reported
16534 // in Physical Device Properties are included in alignment reported by memory requirements.
16535 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16536 {
16537 VMA_ASSERT(vkMemReq.alignment %
16538 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16539 }
16540 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16541 {
16542 VMA_ASSERT(vkMemReq.alignment %
16543 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16544 }
16545 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16546 {
16547 VMA_ASSERT(vkMemReq.alignment %
16548 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16549 }
16550
16551 // 3. Allocate memory using allocator.
16552 res = allocator->AllocateMemory(
16553 vkMemReq,
16554 requiresDedicatedAllocation,
16555 prefersDedicatedAllocation,
16556 *pBuffer, // dedicatedBuffer
16557 VK_NULL_HANDLE, // dedicatedImage
16558 *pAllocationCreateInfo,
16559 VMA_SUBALLOCATION_TYPE_BUFFER,
16560 1, // allocationCount
16561 pAllocation);
16562
16563#if VMA_RECORDING_ENABLED
16564 if(allocator->GetRecorder() != VMA_NULL)
16565 {
16566 allocator->GetRecorder()->RecordCreateBuffer(
16567 allocator->GetCurrentFrameIndex(),
16568 *pBufferCreateInfo,
16569 *pAllocationCreateInfo,
16570 *pAllocation);
16571 }
16572#endif
16573
16574 if(res >= 0)
16575 {
16576 // 3. Bind buffer with memory.
16577 res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16578 if(res >= 0)
16579 {
16580 // All steps succeeded.
16581 #if VMA_STATS_STRING_ENABLED
16582 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16583 #endif
16584 if(pAllocationInfo != VMA_NULL)
16585 {
16586 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16587 }
16588
16589 return VK_SUCCESS;
16590 }
16591 allocator->FreeMemory(
16592 1, // allocationCount
16593 pAllocation);
16594 *pAllocation = VK_NULL_HANDLE;
16595 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16596 *pBuffer = VK_NULL_HANDLE;
16597 return res;
16598 }
16599 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16600 *pBuffer = VK_NULL_HANDLE;
16601 return res;
16602 }
16603 return res;
16604}
16605
16606void vmaDestroyBuffer(
16607 VmaAllocator allocator,
16608 VkBuffer buffer,
16609 VmaAllocation allocation)
16610{
16611 VMA_ASSERT(allocator);
16612
16613 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16614 {
16615 return;
16616 }
16617
16618 VMA_DEBUG_LOG("vmaDestroyBuffer");
16619
16620 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16621
16622#if VMA_RECORDING_ENABLED
16623 if(allocator->GetRecorder() != VMA_NULL)
16624 {
16625 allocator->GetRecorder()->RecordDestroyBuffer(
16626 allocator->GetCurrentFrameIndex(),
16627 allocation);
16628 }
16629#endif
16630
16631 if(buffer != VK_NULL_HANDLE)
16632 {
16633 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16634 }
16635
16636 if(allocation != VK_NULL_HANDLE)
16637 {
16638 allocator->FreeMemory(
16639 1, // allocationCount
16640 &allocation);
16641 }
16642}
16643
16644VkResult vmaCreateImage(
16645 VmaAllocator allocator,
16646 const VkImageCreateInfo* pImageCreateInfo,
16647 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16648 VkImage* pImage,
16649 VmaAllocation* pAllocation,
16650 VmaAllocationInfo* pAllocationInfo)
16651{
16652 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16653
16654 if(pImageCreateInfo->extent.width == 0 ||
16655 pImageCreateInfo->extent.height == 0 ||
16656 pImageCreateInfo->extent.depth == 0 ||
16657 pImageCreateInfo->mipLevels == 0 ||
16658 pImageCreateInfo->arrayLayers == 0)
16659 {
16660 return VK_ERROR_VALIDATION_FAILED_EXT;
16661 }
16662
16663 VMA_DEBUG_LOG("vmaCreateImage");
16664
16665 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16666
16667 *pImage = VK_NULL_HANDLE;
16668 *pAllocation = VK_NULL_HANDLE;
16669
16670 // 1. Create VkImage.
16671 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16672 allocator->m_hDevice,
16673 pImageCreateInfo,
16674 allocator->GetAllocationCallbacks(),
16675 pImage);
16676 if(res >= 0)
16677 {
16678 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16679 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16680 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16681
16682 // 2. Allocate memory using allocator.
16683 VkMemoryRequirements vkMemReq = {};
16684 bool requiresDedicatedAllocation = false;
16685 bool prefersDedicatedAllocation = false;
16686 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16687 requiresDedicatedAllocation, prefersDedicatedAllocation);
16688
16689 res = allocator->AllocateMemory(
16690 vkMemReq,
16691 requiresDedicatedAllocation,
16692 prefersDedicatedAllocation,
16693 VK_NULL_HANDLE, // dedicatedBuffer
16694 *pImage, // dedicatedImage
16695 *pAllocationCreateInfo,
16696 suballocType,
16697 1, // allocationCount
16698 pAllocation);
16699
16700#if VMA_RECORDING_ENABLED
16701 if(allocator->GetRecorder() != VMA_NULL)
16702 {
16703 allocator->GetRecorder()->RecordCreateImage(
16704 allocator->GetCurrentFrameIndex(),
16705 *pImageCreateInfo,
16706 *pAllocationCreateInfo,
16707 *pAllocation);
16708 }
16709#endif
16710
16711 if(res >= 0)
16712 {
16713 // 3. Bind image with memory.
16714 res = allocator->BindImageMemory(*pAllocation, *pImage);
16715 if(res >= 0)
16716 {
16717 // All steps succeeded.
16718 #if VMA_STATS_STRING_ENABLED
16719 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16720 #endif
16721 if(pAllocationInfo != VMA_NULL)
16722 {
16723 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16724 }
16725
16726 return VK_SUCCESS;
16727 }
16728 allocator->FreeMemory(
16729 1, // allocationCount
16730 pAllocation);
16731 *pAllocation = VK_NULL_HANDLE;
16732 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16733 *pImage = VK_NULL_HANDLE;
16734 return res;
16735 }
16736 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16737 *pImage = VK_NULL_HANDLE;
16738 return res;
16739 }
16740 return res;
16741}
16742
16743void vmaDestroyImage(
16744 VmaAllocator allocator,
16745 VkImage image,
16746 VmaAllocation allocation)
16747{
16748 VMA_ASSERT(allocator);
16749
16750 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16751 {
16752 return;
16753 }
16754
16755 VMA_DEBUG_LOG("vmaDestroyImage");
16756
16757 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16758
16759#if VMA_RECORDING_ENABLED
16760 if(allocator->GetRecorder() != VMA_NULL)
16761 {
16762 allocator->GetRecorder()->RecordDestroyImage(
16763 allocator->GetCurrentFrameIndex(),
16764 allocation);
16765 }
16766#endif
16767
16768 if(image != VK_NULL_HANDLE)
16769 {
16770 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16771 }
16772 if(allocation != VK_NULL_HANDLE)
16773 {
16774 allocator->FreeMemory(
16775 1, // allocationCount
16776 &allocation);
16777 }
16778}
Tony-LunarG390319b2019-03-18 15:54:16 -060016779#if defined(__GNUC__)
16780#pragma GCC diagnostic pop
16781#endif
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060016782#endif // #ifdef VMA_IMPLEMENTATION