blob: a2f7a1bb0243c39a178ca35b968d3f1242a2091a [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
23#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24#define AMD_VULKAN_MEMORY_ALLOCATOR_H
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30/** \mainpage Vulkan Memory Allocator
31
32<b>Version 2.2.0</b> (2018-12-13)
33
34Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
35License: MIT
36
37Documentation of all members: vk_mem_alloc.h
38
39\section main_table_of_contents Table of contents
40
41- <b>User guide</b>
42 - \subpage quick_start
43 - [Project setup](@ref quick_start_project_setup)
44 - [Initialization](@ref quick_start_initialization)
45 - [Resource allocation](@ref quick_start_resource_allocation)
46 - \subpage choosing_memory_type
47 - [Usage](@ref choosing_memory_type_usage)
48 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
49 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
50 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
51 - \subpage memory_mapping
52 - [Mapping functions](@ref memory_mapping_mapping_functions)
53 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
54 - [Cache control](@ref memory_mapping_cache_control)
55 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
56 - \subpage custom_memory_pools
57 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58 - [Linear allocation algorithm](@ref linear_algorithm)
59 - [Free-at-once](@ref linear_algorithm_free_at_once)
60 - [Stack](@ref linear_algorithm_stack)
61 - [Double stack](@ref linear_algorithm_double_stack)
62 - [Ring buffer](@ref linear_algorithm_ring_buffer)
63 - [Buddy allocation algorithm](@ref buddy_algorithm)
64 - \subpage defragmentation
65 - [Defragmenting CPU memory](@ref defragmentation_cpu)
66 - [Defragmenting GPU memory](@ref defragmentation_gpu)
67 - [Additional notes](@ref defragmentation_additional_notes)
68 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
69 - \subpage lost_allocations
70 - \subpage statistics
71 - [Numeric statistics](@ref statistics_numeric_statistics)
72 - [JSON dump](@ref statistics_json_dump)
73 - \subpage allocation_annotation
74 - [Allocation user data](@ref allocation_user_data)
75 - [Allocation names](@ref allocation_names)
76 - \subpage debugging_memory_usage
77 - [Memory initialization](@ref debugging_memory_usage_initialization)
78 - [Margins](@ref debugging_memory_usage_margins)
79 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
80 - \subpage record_and_replay
81- \subpage usage_patterns
82 - [Simple patterns](@ref usage_patterns_simple)
83 - [Advanced patterns](@ref usage_patterns_advanced)
84- \subpage configuration
85 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
86 - [Custom host memory allocator](@ref custom_memory_allocator)
87 - [Device memory allocation callbacks](@ref allocation_callbacks)
88 - [Device heap memory limit](@ref heap_memory_limit)
89 - \subpage vk_khr_dedicated_allocation
90- \subpage general_considerations
91 - [Thread safety](@ref general_considerations_thread_safety)
92 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
93 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
94 - [Features not supported](@ref general_considerations_features_not_supported)
95
96\section main_see_also See also
97
98- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
99- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
100
101
102
103
104\page quick_start Quick start
105
106\section quick_start_project_setup Project setup
107
108Vulkan Memory Allocator comes in form of a single header file.
109You don't need to build it as a separate library project.
110You can add this file directly to your project and submit it to code repository next to your other source files.
111
112"Single header" doesn't mean that everything is contained in C/C++ declarations,
113like it tends to be in case of inline functions or C++ templates.
114It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
115If you don't do it properly, you will get linker errors.
116
117To do it properly:
118
119-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
120 This includes declarations of all members of the library.
121-# In exacly one CPP file define following macro before this include.
122 It enables also internal definitions.
123
124\code
125#define VMA_IMPLEMENTATION
126#include "vk_mem_alloc.h"
127\endcode
128
129It may be a good idea to create dedicated CPP file just for this purpose.
130
131Note on language: This library is written in C++, but has C-compatible interface.
132Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
133implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
134
135Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
136includes `<windows.h>` on Windows. If you need some specific macros defined
137before including these headers (like `WIN32_LEAN_AND_MEAN` or
138`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
139them before every `#include` of this library.
140
141
142\section quick_start_initialization Initialization
143
144At program startup:
145
146-# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
147-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
148 calling vmaCreateAllocator().
149
150\code
151VmaAllocatorCreateInfo allocatorInfo = {};
152allocatorInfo.physicalDevice = physicalDevice;
153allocatorInfo.device = device;
154
155VmaAllocator allocator;
156vmaCreateAllocator(&allocatorInfo, &allocator);
157\endcode
158
159\section quick_start_resource_allocation Resource allocation
160
161When you want to create a buffer or image:
162
163-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
164-# Fill VmaAllocationCreateInfo structure.
165-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
166 already allocated and bound to it.
167
168\code
169VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
170bufferInfo.size = 65536;
171bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
172
173VmaAllocationCreateInfo allocInfo = {};
174allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
175
176VkBuffer buffer;
177VmaAllocation allocation;
178vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
179\endcode
180
181Don't forget to destroy your objects when no longer needed:
182
183\code
184vmaDestroyBuffer(allocator, buffer, allocation);
185vmaDestroyAllocator(allocator);
186\endcode
187
188
189\page choosing_memory_type Choosing memory type
190
191Physical devices in Vulkan support various combinations of memory heaps and
192types. Help with choosing correct and optimal memory type for your specific
193resource is one of the key features of this library. You can use it by filling
194appropriate members of VmaAllocationCreateInfo structure, as described below.
195You can also combine multiple methods.
196
197-# If you just want to find memory type index that meets your requirements, you
198 can use function vmaFindMemoryTypeIndex().
199-# If you want to allocate a region of device memory without association with any
200 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
201 this function is not recommended and usually not needed.
202-# If you already have a buffer or an image created, you want to allocate memory
203 for it and then you will bind it yourself, you can use function
204 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
205 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
206-# If you want to create a buffer or an image, allocate memory for it and bind
207 them together, all in one call, you can use function vmaCreateBuffer(),
208 vmaCreateImage(). This is the recommended way to use this library.
209
210When using 3. or 4., the library internally queries Vulkan for memory types
211supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
212and uses only one of these types.
213
214If no memory type can be found that meets all the requirements, these functions
215return `VK_ERROR_FEATURE_NOT_PRESENT`.
216
217You can leave VmaAllocationCreateInfo structure completely filled with zeros.
218It means no requirements are specified for memory type.
219It is valid, although not very useful.
220
221\section choosing_memory_type_usage Usage
222
223The easiest way to specify memory requirements is to fill member
224VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
225It defines high level, common usage types.
226For more details, see description of this enum.
227
228For example, if you want to create a uniform buffer that will be filled using
229transfer only once or infrequently and used for rendering every frame, you can
230do it using following code:
231
232\code
233VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
234bufferInfo.size = 65536;
235bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
236
237VmaAllocationCreateInfo allocInfo = {};
238allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
239
240VkBuffer buffer;
241VmaAllocation allocation;
242vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
243\endcode
244
245\section choosing_memory_type_required_preferred_flags Required and preferred flags
246
247You can specify more detailed requirements by filling members
248VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
249with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
250if you want to create a buffer that will be persistently mapped on host (so it
251must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
252use following code:
253
254\code
255VmaAllocationCreateInfo allocInfo = {};
256allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
257allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
258allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
259
260VkBuffer buffer;
261VmaAllocation allocation;
262vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
263\endcode
264
265A memory type is chosen that has all the required flags and as many preferred
266flags set as possible.
267
268If you use VmaAllocationCreateInfo::usage, it is just internally converted to
269a set of required and preferred flags.
270
271\section choosing_memory_type_explicit_memory_types Explicit memory types
272
273If you inspected memory types available on the physical device and you have
274a preference for memory types that you want to use, you can fill member
275VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
276means that a memory type with that index is allowed to be used for the
277allocation. Special value 0, just like `UINT32_MAX`, means there are no
278restrictions to memory type index.
279
280Please note that this member is NOT just a memory type index.
281Still you can use it to choose just one, specific memory type.
282For example, if you already determined that your buffer should be created in
283memory type 2, use following code:
284
285\code
286uint32_t memoryTypeIndex = 2;
287
288VmaAllocationCreateInfo allocInfo = {};
289allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
290
291VkBuffer buffer;
292VmaAllocation allocation;
293vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
294\endcode
295
296\section choosing_memory_type_custom_memory_pools Custom memory pools
297
298If you allocate from custom memory pool, all the ways of specifying memory
299requirements described above are not applicable and the aforementioned members
300of VmaAllocationCreateInfo structure are ignored. Memory type is selected
301explicitly when creating the pool and then used to make all the allocations from
302that pool. For further details, see \ref custom_memory_pools.
303
304
305\page memory_mapping Memory mapping
306
307To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
308to be able to read from it or write to it in CPU code.
309Mapping is possible only of memory allocated from a memory type that has
310`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
311Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
312You can use them directly with memory allocated by this library,
313but it is not recommended because of following issue:
314Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
315This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
316Because of this, Vulkan Memory Allocator provides following facilities:
317
318\section memory_mapping_mapping_functions Mapping functions
319
320The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
321They are safer and more convenient to use than standard Vulkan functions.
322You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
323You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
324The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
325For further details, see description of vmaMapMemory() function.
326Example:
327
328\code
329// Having these objects initialized:
330
331struct ConstantBuffer
332{
333 ...
334};
335ConstantBuffer constantBufferData;
336
337VmaAllocator allocator;
338VkBuffer constantBuffer;
339VmaAllocation constantBufferAllocation;
340
341// You can map and fill your buffer using following code:
342
343void* mappedData;
344vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
345memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
346vmaUnmapMemory(allocator, constantBufferAllocation);
347\endcode
348
349When mapping, you may see a warning from Vulkan validation layer similar to this one:
350
351<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>
352
353It happens because the library maps entire `VkDeviceMemory` block, where different
354types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
355You can safely ignore it if you are sure you access only memory of the intended
356object that you wanted to map.
357
358
359\section memory_mapping_persistently_mapped_memory Persistently mapped memory
360
361Kepping your memory persistently mapped is generally OK in Vulkan.
362You don't need to unmap it before using its data on the GPU.
363The library provides a special feature designed for that:
364Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
365VmaAllocationCreateInfo::flags stay mapped all the time,
366so you can just access CPU pointer to it any time
367without a need to call any "map" or "unmap" function.
368Example:
369
370\code
371VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
372bufCreateInfo.size = sizeof(ConstantBuffer);
373bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
374
375VmaAllocationCreateInfo allocCreateInfo = {};
376allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
377allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
378
379VkBuffer buf;
380VmaAllocation alloc;
381VmaAllocationInfo allocInfo;
382vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
383
384// Buffer is already mapped. You can access its memory.
385memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
386\endcode
387
388There are some exceptions though, when you should consider mapping memory only for a short period of time:
389
390- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
391 device is discrete AMD GPU,
392 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
393 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
394 then whenever a memory block allocated from this memory type stays mapped
395 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
396 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
397 matter if that particular memory block is actually used by the command buffer
398 being submitted.
399- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
400 which requires unmapping before GPU can see updated texture.
401- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
402
403\section memory_mapping_cache_control Cache control
404
405Memory in Vulkan doesn't need to be unmapped before using it on GPU,
406but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
407you need to manually invalidate cache before reading of mapped pointer
408and flush cache after writing to mapped pointer.
409Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
410`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
411functions that refer to given allocation object: vmaFlushAllocation(),
412vmaInvalidateAllocation().
413
414Regions of memory specified for flush/invalidate must be aligned to
415`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
416In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
417within blocks are aligned to this value, so their offsets are always multiply of
418`nonCoherentAtomSize` and two different allocations never share same "line" of this size.
419
420Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
421
422Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
423currently provide `HOST_COHERENT` flag on all memory types that are
424`HOST_VISIBLE`, so on this platform you may not need to bother.
425
426\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
427
428It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
429despite it wasn't explicitly requested.
430For example, application may work on integrated graphics with unified memory (like Intel) or
431allocation from video memory might have failed, so the library chose system memory as fallback.
432
433You can detect this case and map such allocation to access its memory on CPU directly,
434instead of launching a transfer operation.
435In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
436and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
437
438\code
439VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
440bufCreateInfo.size = sizeof(ConstantBuffer);
441bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
442
443VmaAllocationCreateInfo allocCreateInfo = {};
444allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
445allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
446
447VkBuffer buf;
448VmaAllocation alloc;
449VmaAllocationInfo allocInfo;
450vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
451
452VkMemoryPropertyFlags memFlags;
453vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
454if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
455{
456 // Allocation ended up in mappable memory. You can map it and access it directly.
457 void* mappedData;
458 vmaMapMemory(allocator, alloc, &mappedData);
459 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
460 vmaUnmapMemory(allocator, alloc);
461}
462else
463{
464 // Allocation ended up in non-mappable memory.
465 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
466}
467\endcode
468
469You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
470that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
471If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
472If not, the flag is just ignored.
473Example:
474
475\code
476VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
477bufCreateInfo.size = sizeof(ConstantBuffer);
478bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
479
480VmaAllocationCreateInfo allocCreateInfo = {};
481allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
482allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
483
484VkBuffer buf;
485VmaAllocation alloc;
486VmaAllocationInfo allocInfo;
487vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
488
489if(allocInfo.pUserData != nullptr)
490{
491 // Allocation ended up in mappable memory.
492 // It's persistently mapped. You can access it directly.
493 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
494}
495else
496{
497 // Allocation ended up in non-mappable memory.
498 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
499}
500\endcode
501
502
503\page custom_memory_pools Custom memory pools
504
505A memory pool contains a number of `VkDeviceMemory` blocks.
506The library automatically creates and manages default pool for each memory type available on the device.
507Default memory pool automatically grows in size.
508Size of allocated blocks is also variable and managed automatically.
509
510You can create custom pool and allocate memory out of it.
511It can be useful if you want to:
512
513- Keep certain kind of allocations separate from others.
514- Enforce particular, fixed size of Vulkan memory blocks.
515- Limit maximum amount of Vulkan memory allocated for that pool.
516- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
517
518To use custom memory pools:
519
520-# Fill VmaPoolCreateInfo structure.
521-# Call vmaCreatePool() to obtain #VmaPool handle.
522-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
523 You don't need to specify any other parameters of this structure, like `usage`.
524
525Example:
526
527\code
528// Create a pool that can have at most 2 blocks, 128 MiB each.
529VmaPoolCreateInfo poolCreateInfo = {};
530poolCreateInfo.memoryTypeIndex = ...
531poolCreateInfo.blockSize = 128ull * 1024 * 1024;
532poolCreateInfo.maxBlockCount = 2;
533
534VmaPool pool;
535vmaCreatePool(allocator, &poolCreateInfo, &pool);
536
537// Allocate a buffer out of it.
538VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
539bufCreateInfo.size = 1024;
540bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
541
542VmaAllocationCreateInfo allocCreateInfo = {};
543allocCreateInfo.pool = pool;
544
545VkBuffer buf;
546VmaAllocation alloc;
547VmaAllocationInfo allocInfo;
548vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
549\endcode
550
551You have to free all allocations made from this pool before destroying it.
552
553\code
554vmaDestroyBuffer(allocator, buf, alloc);
555vmaDestroyPool(allocator, pool);
556\endcode
557
558\section custom_memory_pools_MemTypeIndex Choosing memory type index
559
560When creating a pool, you must explicitly specify memory type index.
561To find the one suitable for your buffers or images, you can use helper functions
562vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
563You need to provide structures with example parameters of buffers or images
564that you are going to create in that pool.
565
566\code
567VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
568exampleBufCreateInfo.size = 1024; // Whatever.
569exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
570
571VmaAllocationCreateInfo allocCreateInfo = {};
572allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
573
574uint32_t memTypeIndex;
575vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
576
577VmaPoolCreateInfo poolCreateInfo = {};
578poolCreateInfo.memoryTypeIndex = memTypeIndex;
579// ...
580\endcode
581
582When creating buffers/images allocated in that pool, provide following parameters:
583
584- `VkBufferCreateInfo`: Prefer to pass same parameters as above.
585 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
586 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
587 or the other way around.
588- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
589 Other members are ignored anyway.
590
591\section linear_algorithm Linear allocation algorithm
592
593Each Vulkan memory block managed by this library has accompanying metadata that
594keeps track of used and unused regions. By default, the metadata structure and
595algorithm tries to find best place for new allocations among free regions to
596optimize memory usage. This way you can allocate and free objects in any order.
597
598![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
599
600Sometimes there is a need to use simpler, linear allocation algorithm. You can
601create custom pool that uses such algorithm by adding flag
602#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
603#VmaPool object. Then an alternative metadata management is used. It always
604creates new allocations after last one and doesn't reuse free regions after
605allocations freed in the middle. It results in better allocation performance and
606less memory consumed by metadata.
607
608![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
609
610With this one flag, you can create a custom pool that can be used in many ways:
611free-at-once, stack, double stack, and ring buffer. See below for details.
612
613\subsection linear_algorithm_free_at_once Free-at-once
614
615In a pool that uses linear algorithm, you still need to free all the allocations
616individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
617them in any order. New allocations are always made after last one - free space
618in the middle is not reused. However, when you release all the allocation and
619the pool becomes empty, allocation starts from the beginning again. This way you
620can use linear algorithm to speed up creation of allocations that you are going
621to release all at once.
622
623![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
624
625This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
626value that allows multiple memory blocks.
627
628\subsection linear_algorithm_stack Stack
629
630When you free an allocation that was created last, its space can be reused.
631Thanks to this, if you always release allocations in the order opposite to their
632creation (LIFO - Last In First Out), you can achieve behavior of a stack.
633
634![Stack](../gfx/Linear_allocator_4_stack.png)
635
636This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
637value that allows multiple memory blocks.
638
639\subsection linear_algorithm_double_stack Double stack
640
641The space reserved by a custom pool with linear algorithm may be used by two
642stacks:
643
644- First, default one, growing up from offset 0.
645- Second, "upper" one, growing down from the end towards lower offsets.
646
647To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
648to VmaAllocationCreateInfo::flags.
649
650![Double stack](../gfx/Linear_allocator_7_double_stack.png)
651
652Double stack is available only in pools with one memory block -
653VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
654
655When the two stacks' ends meet so there is not enough space between them for a
656new allocation, such allocation fails with usual
657`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
658
659\subsection linear_algorithm_ring_buffer Ring buffer
660
661When you free some allocations from the beginning and there is not enough free space
662for a new one at the end of a pool, allocator's "cursor" wraps around to the
663beginning and starts allocation there. Thanks to this, if you always release
664allocations in the same order as you created them (FIFO - First In First Out),
665you can achieve behavior of a ring buffer / queue.
666
667![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
668
669Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
670If there is not enough free space for a new allocation, but existing allocations
671from the front of the queue can become lost, they become lost and the allocation
672succeeds.
673
674![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
675
676Ring buffer is available only in pools with one memory block -
677VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
678
679\section buddy_algorithm Buddy allocation algorithm
680
681There is another allocation algorithm that can be used with custom pools, called
682"buddy". Its internal data structure is based on a tree of blocks, each having
683size that is a power of two and a half of its parent's size. When you want to
684allocate memory of certain size, a free node in the tree is located. If it's too
685large, it is recursively split into two halves (called "buddies"). However, if
686requested allocation size is not a power of two, the size of a tree node is
687aligned up to the nearest power of two and the remaining space is wasted. When
688two buddy nodes become free, they are merged back into one larger node.
689
690![Buddy allocator](../gfx/Buddy_allocator.png)
691
692The advantage of buddy allocation algorithm over default algorithm is faster
693allocation and deallocation, as well as smaller external fragmentation. The
694disadvantage is more wasted space (internal fragmentation).
695
696For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
697or other sources that describe this concept in general.
698
699To use buddy allocation algorithm with a custom pool, add flag
700#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
701#VmaPool object.
702
703Several limitations apply to pools that use buddy algorithm:
704
705- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
706 Otherwise, only largest power of two smaller than the size is used for
707 allocations. The remaining space always stays unused.
708- [Margins](@ref debugging_memory_usage_margins) and
709 [corruption detection](@ref debugging_memory_usage_corruption_detection)
710 don't work in such pools.
711- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
712 use them, but they never become lost. Support may be added in the future.
713- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
714 such pool.
715
716\page defragmentation Defragmentation
717
718Interleaved allocations and deallocations of many objects of varying size can
719cause fragmentation over time, which can lead to a situation where the library is unable
720to find a continuous range of free memory for a new allocation despite there is
721enough free space, just scattered across many small free ranges between existing
722allocations.
723
724To mitigate this problem, you can use defragmentation feature:
725structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
726Given set of allocations,
727this function can move them to compact used memory, ensure more continuous free
728space and possibly also free some `VkDeviceMemory` blocks.
729
730What the defragmentation does is:
731
732- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
733 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
734 VmaAllocationInfo::offset changes. You must query them again using
735 vmaGetAllocationInfo() if you need them.
736- Moves actual data in memory.
737
738What it doesn't do, so you need to do it yourself:
739
740- Recreate buffers and images that were bound to allocations that were defragmented and
741 bind them with their new places in memory.
742 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
743 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
744 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
745 destroy or create allocation objects!
746- Recreate views and update descriptors that point to these buffers and images.
747
748\section defragmentation_cpu Defragmenting CPU memory
749
750Following example demonstrates how you can run defragmentation on CPU.
751Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
752Others are ignored.
753
754The way it works is:
755
756- It temporarily maps entire memory blocks when necessary.
757- It moves data using `memmove()` function.
758
759\code
760// Given following variables already initialized:
761VkDevice device;
762VmaAllocator allocator;
763std::vector<VkBuffer> buffers;
764std::vector<VmaAllocation> allocations;
765
766
767const uint32_t allocCount = (uint32_t)allocations.size();
768std::vector<VkBool32> allocationsChanged(allocCount);
769
770VmaDefragmentationInfo2 defragInfo = {};
771defragInfo.allocationCount = allocCount;
772defragInfo.pAllocations = allocations.data();
773defragInfo.pAllocationsChanged = allocationsChanged.data();
774defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
775defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
776
777VmaDefragmentationContext defragCtx;
778vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
779vmaDefragmentationEnd(allocator, defragCtx);
780
781for(uint32_t i = 0; i < allocCount; ++i)
782{
783 if(allocationsChanged[i])
784 {
785 // Destroy buffer that is immutably bound to memory region which is no longer valid.
786 vkDestroyBuffer(device, buffers[i], nullptr);
787
788 // Create new buffer with same parameters.
789 VkBufferCreateInfo bufferInfo = ...;
790 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
791
792 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
793
794 // Bind new buffer to new memory region. Data contained in it is already moved.
795 VmaAllocationInfo allocInfo;
796 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
797 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
798 }
799}
800\endcode
801
802Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
803This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
804has been modified during defragmentation.
805You can pass null, but you then need to query every allocation passed to defragmentation
806for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
807
808If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
809you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
810instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
811to defragment all allocations in given pools.
812You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
813You can also combine both methods.
814
815\section defragmentation_gpu Defragmenting GPU memory
816
817It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
818To do that, you need to pass a command buffer that meets requirements as described in
819VmaDefragmentationInfo2::commandBuffer. The way it works is:
820
821- It creates temporary buffers and binds them to entire memory blocks when necessary.
822- It issues `vkCmdCopyBuffer()` to passed command buffer.
823
824Example:
825
826\code
827// Given following variables already initialized:
828VkDevice device;
829VmaAllocator allocator;
830VkCommandBuffer commandBuffer;
831std::vector<VkBuffer> buffers;
832std::vector<VmaAllocation> allocations;
833
834
835const uint32_t allocCount = (uint32_t)allocations.size();
836std::vector<VkBool32> allocationsChanged(allocCount);
837
838VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
839vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
840
841VmaDefragmentationInfo2 defragInfo = {};
842defragInfo.allocationCount = allocCount;
843defragInfo.pAllocations = allocations.data();
844defragInfo.pAllocationsChanged = allocationsChanged.data();
845defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
846defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
847defragInfo.commandBuffer = commandBuffer;
848
849VmaDefragmentationContext defragCtx;
850vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
851
852vkEndCommandBuffer(commandBuffer);
853
854// Submit commandBuffer.
855// Wait for a fence that ensures commandBuffer execution finished.
856
857vmaDefragmentationEnd(allocator, defragCtx);
858
859for(uint32_t i = 0; i < allocCount; ++i)
860{
861 if(allocationsChanged[i])
862 {
863 // Destroy buffer that is immutably bound to memory region which is no longer valid.
864 vkDestroyBuffer(device, buffers[i], nullptr);
865
866 // Create new buffer with same parameters.
867 VkBufferCreateInfo bufferInfo = ...;
868 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
869
870 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
871
872 // Bind new buffer to new memory region. Data contained in it is already moved.
873 VmaAllocationInfo allocInfo;
874 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
875 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
876 }
877}
878\endcode
879
880You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
881The library automatically chooses best method to defragment each memory pool.
882
883You may try not to block your entire program to wait until defragmentation finishes,
884but do it in the background, as long as you carefully fullfill requirements described
885in function vmaDefragmentationBegin().
886
887\section defragmentation_additional_notes Additional notes
888
889While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
890See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
891
892If you defragment allocations bound to images, these images should be created with
893`VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
894parameters and pointing to data copied to another memory region will interpret
895its contents consistently. Otherwise you may experience corrupted data on some
896implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
897
898If you defragment allocations bound to images, new images to be bound to new
899memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
900and then transitioned to their original layout from before defragmentation using
901an image memory barrier.
902
903Please don't expect memory to be fully compacted after defragmentation.
904Algorithms inside are based on some heuristics that try to maximize number of Vulkan
905memory blocks to make totally empty to release them, as well as to maximimze continuous
906empty space inside remaining blocks, while minimizing the number and size of allocations that
907need to be moved. Some fragmentation may still remain - this is normal.
908
909\section defragmentation_custom_algorithm Writing custom defragmentation algorithm
910
911If you want to implement your own, custom defragmentation algorithm,
912there is infrastructure prepared for that,
913but it is not exposed through the library API - you need to hack its source code.
914Here are steps needed to do this:
915
916-# Main thing you need to do is to define your own class derived from base abstract
917 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
918 See definition and comments of this class for details.
919-# Your code needs to interact with device memory block metadata.
920 If you need more access to its data than it's provided by its public interface,
921 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
922-# If you want to create a flag that would enable your algorithm or pass some additional
923 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
924 VmaDefragmentationInfo2::flags.
925-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
926 of your new class whenever needed.
927
928
929\page lost_allocations Lost allocations
930
931If your game oversubscribes video memory, if may work OK in previous-generation
932graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
933paged to system RAM. In Vulkan you can't do it because when you run out of
934memory, an allocation just fails. If you have more data (e.g. textures) that can
935fit into VRAM and you don't need it all at once, you may want to upload them to
936GPU on demand and "push out" ones that are not used for a long time to make room
937for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
938cache. Vulkan Memory Allocator can help you with that by supporting a concept of
939"lost allocations".
940
941To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
942flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
943such allocation in every new frame, you need to query it if it's not lost.
944To check it, call vmaTouchAllocation().
945If the allocation is lost, you should not use it or buffer/image bound to it.
946You mustn't forget to destroy this allocation and this buffer/image.
947vmaGetAllocationInfo() can also be used for checking status of the allocation.
948Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
949
950To create an allocation that can make some other allocations lost to make room
951for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
952usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
953#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
954
955Warning! Current implementation uses quite naive, brute force algorithm,
956which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
957flag quite slow. A new, more optimal algorithm and data structure to speed this
958up is planned for the future.
959
960<b>Q: When interleaving creation of new allocations with usage of existing ones,
961how do you make sure that an allocation won't become lost while it's used in the
962current frame?</b>
963
964It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
965status/parameters and checks whether it's not lost, but when it's not, it also
966atomically marks it as used in the current frame, which makes it impossible to
967become lost in that frame. It uses lockless algorithm, so it works fast and
968doesn't involve locking any internal mutex.
969
970<b>Q: What if my allocation may still be in use by the GPU when it's rendering a
971previous frame while I already submit new frame on the CPU?</b>
972
973You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
974become lost for a number of additional frames back from the current one by
975specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
976memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
977
978<b>Q: How do you inform the library when new frame starts?</b>
979
980You need to call function vmaSetCurrentFrameIndex().
981
982Example code:
983
984\code
985struct MyBuffer
986{
987 VkBuffer m_Buf = nullptr;
988 VmaAllocation m_Alloc = nullptr;
989
990 // Called when the buffer is really needed in the current frame.
991 void EnsureBuffer();
992};
993
994void MyBuffer::EnsureBuffer()
995{
996 // Buffer has been created.
997 if(m_Buf != VK_NULL_HANDLE)
998 {
999 // Check if its allocation is not lost + mark it as used in current frame.
1000 if(vmaTouchAllocation(allocator, m_Alloc))
1001 {
1002 // It's all OK - safe to use m_Buf.
1003 return;
1004 }
1005 }
1006
1007 // Buffer not yet exists or lost - destroy and recreate it.
1008
1009 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1010
1011 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1012 bufCreateInfo.size = 1024;
1013 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1014
1015 VmaAllocationCreateInfo allocCreateInfo = {};
1016 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1017 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1018 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1019
1020 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1021}
1022\endcode
1023
1024When using lost allocations, you may see some Vulkan validation layer warnings
1025about overlapping regions of memory bound to different kinds of buffers and
1026images. This is still valid as long as you implement proper handling of lost
1027allocations (like in the example above) and don't use them.
1028
1029You can create an allocation that is already in lost state from the beginning using function
1030vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1031
1032You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1033in a specified custom pool to lost state.
1034Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1035cannot become lost.
1036
1037<b>Q: Can I touch allocation that cannot become lost?</b>
1038
1039Yes, although it has no visible effect.
1040Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1041also for allocations that cannot become lost, but the only way to observe it is to dump
1042internal allocator state using vmaBuildStatsString().
1043You can use this feature for debugging purposes to explicitly mark allocations that you use
1044in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1045
1046
1047\page statistics Statistics
1048
1049This library contains functions that return information about its internal state,
1050especially the amount of memory allocated from Vulkan.
1051Please keep in mind that these functions need to traverse all internal data structures
1052to gather these information, so they may be quite time-consuming.
1053Don't call them too often.
1054
1055\section statistics_numeric_statistics Numeric statistics
1056
1057You can query for overall statistics of the allocator using function vmaCalculateStats().
1058Information are returned using structure #VmaStats.
1059It contains #VmaStatInfo - number of allocated blocks, number of allocations
1060(occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1061number of bytes used and unused (but still allocated from Vulkan) and other information.
1062They are summed across memory heaps, memory types and total for whole allocator.
1063
1064You can query for statistics of a custom pool using function vmaGetPoolStats().
1065Information are returned using structure #VmaPoolStats.
1066
1067You can query for information about specific allocation using function vmaGetAllocationInfo().
1068It fill structure #VmaAllocationInfo.
1069
1070\section statistics_json_dump JSON dump
1071
1072You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1073The result is guaranteed to be correct JSON.
1074It uses ANSI encoding.
1075Any strings provided by user (see [Allocation names](@ref allocation_names))
1076are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1077this JSON string can be treated as using this encoding.
1078It must be freed using function vmaFreeStatsString().
1079
1080The format of this JSON string is not part of official documentation of the library,
1081but it will not change in backward-incompatible way without increasing library major version number
1082and appropriate mention in changelog.
1083
1084The JSON string contains all the data that can be obtained using vmaCalculateStats().
1085It can also contain detailed map of allocated memory blocks and their regions -
1086free and occupied by allocations.
1087This allows e.g. to visualize the memory or assess fragmentation.
1088
1089
1090\page allocation_annotation Allocation names and user data
1091
1092\section allocation_user_data Allocation user data
1093
1094You can annotate allocations with your own information, e.g. for debugging purposes.
1095To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1096an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1097some handle, index, key, ordinal number or any other value that would associate
1098the allocation with your custom metadata.
1099
1100\code
1101VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1102// Fill bufferInfo...
1103
1104MyBufferMetadata* pMetadata = CreateBufferMetadata();
1105
1106VmaAllocationCreateInfo allocCreateInfo = {};
1107allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1108allocCreateInfo.pUserData = pMetadata;
1109
1110VkBuffer buffer;
1111VmaAllocation allocation;
1112vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1113\endcode
1114
1115The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1116
1117\code
1118VmaAllocationInfo allocInfo;
1119vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1120MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1121\endcode
1122
1123It can also be changed using function vmaSetAllocationUserData().
1124
1125Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1126vmaBuildStatsString(), in hexadecimal form.
1127
1128\section allocation_names Allocation names
1129
1130There is alternative mode available where `pUserData` pointer is used to point to
1131a null-terminated string, giving a name to the allocation. To use this mode,
1132set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1133Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1134vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1135The library creates internal copy of the string, so the pointer you pass doesn't need
1136to be valid for whole lifetime of the allocation. You can free it after the call.
1137
1138\code
1139VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1140// Fill imageInfo...
1141
1142std::string imageName = "Texture: ";
1143imageName += fileName;
1144
1145VmaAllocationCreateInfo allocCreateInfo = {};
1146allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1147allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1148allocCreateInfo.pUserData = imageName.c_str();
1149
1150VkImage image;
1151VmaAllocation allocation;
1152vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1153\endcode
1154
1155The value of `pUserData` pointer of the allocation will be different than the one
1156you passed when setting allocation's name - pointing to a buffer managed
1157internally that holds copy of the string.
1158
1159\code
1160VmaAllocationInfo allocInfo;
1161vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1162const char* imageName = (const char*)allocInfo.pUserData;
1163printf("Image name: %s\n", imageName);
1164\endcode
1165
1166That string is also printed in JSON report created by vmaBuildStatsString().
1167
1168
1169\page debugging_memory_usage Debugging incorrect memory usage
1170
1171If you suspect a bug with memory usage, like usage of uninitialized memory or
1172memory being overwritten out of bounds of an allocation,
1173you can use debug features of this library to verify this.
1174
1175\section debugging_memory_usage_initialization Memory initialization
1176
1177If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1178you can enable automatic memory initialization to verify this.
1179To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1180
1181\code
1182#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1183#include "vk_mem_alloc.h"
1184\endcode
1185
1186It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1187Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1188Memory is automatically mapped and unmapped if necessary.
1189
1190If you find these values while debugging your program, good chances are that you incorrectly
1191read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1192
1193Memory initialization works only with memory types that are `HOST_VISIBLE`.
1194It works also with dedicated allocations.
1195It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1196as they cannot be mapped.
1197
1198\section debugging_memory_usage_margins Margins
1199
1200By default, allocations are laid out in memory blocks next to each other if possible
1201(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1202
1203![Allocations without margin](../gfx/Margins_1.png)
1204
1205Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1206number of bytes as a margin before and after every allocation.
1207
1208\code
1209#define VMA_DEBUG_MARGIN 16
1210#include "vk_mem_alloc.h"
1211\endcode
1212
1213![Allocations with margin](../gfx/Margins_2.png)
1214
1215If your bug goes away after enabling margins, it means it may be caused by memory
1216being overwritten outside of allocation boundaries. It is not 100% certain though.
1217Change in application behavior may also be caused by different order and distribution
1218of allocations across memory blocks after margins are applied.
1219
1220The margin is applied also before first and after last allocation in a block.
1221It may occur only once between two adjacent allocations.
1222
1223Margins work with all types of memory.
1224
1225Margin is applied only to allocations made out of memory blocks and not to dedicated
1226allocations, which have their own memory block of specific size.
1227It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1228or those automatically decided to put into dedicated allocations, e.g. due to its
1229large size or recommended by VK_KHR_dedicated_allocation extension.
1230Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1231
1232Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1233
1234Note that enabling margins increases memory usage and fragmentation.
1235
1236\section debugging_memory_usage_corruption_detection Corruption detection
1237
1238You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1239of contents of the margins.
1240
1241\code
1242#define VMA_DEBUG_MARGIN 16
1243#define VMA_DEBUG_DETECT_CORRUPTION 1
1244#include "vk_mem_alloc.h"
1245\endcode
1246
1247When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1248(it must be multiply of 4) before and after every allocation is filled with a magic number.
1249This idea is also know as "canary".
1250Memory is automatically mapped and unmapped if necessary.
1251
1252This number is validated automatically when the allocation is destroyed.
1253If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1254It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1255which indicates a serious bug.
1256
1257You can also explicitly request checking margins of all allocations in all memory blocks
1258that belong to specified memory types by using function vmaCheckCorruption(),
1259or in memory blocks that belong to specified custom pool, by using function
1260vmaCheckPoolCorruption().
1261
1262Margin validation (corruption detection) works only for memory types that are
1263`HOST_VISIBLE` and `HOST_COHERENT`.
1264
1265
1266\page record_and_replay Record and replay
1267
1268\section record_and_replay_introduction Introduction
1269
1270While using the library, sequence of calls to its functions together with their
1271parameters can be recorded to a file and later replayed using standalone player
1272application. It can be useful to:
1273
1274- Test correctness - check if same sequence of calls will not cause crash or
1275 failures on a target platform.
1276- Gather statistics - see number of allocations, peak memory usage, number of
1277 calls etc.
1278- Benchmark performance - see how much time it takes to replay the whole
1279 sequence.
1280
1281\section record_and_replay_usage Usage
1282
1283<b>To record sequence of calls to a file:</b> Fill in
1284VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1285object. File is opened and written during whole lifetime of the allocator.
1286
1287<b>To replay file:</b> Use VmaReplay - standalone command-line program.
1288Precompiled binary can be found in "bin" directory.
1289Its source can be found in "src/VmaReplay" directory.
1290Its project is generated by Premake.
1291Command line syntax is printed when the program is launched without parameters.
1292Basic usage:
1293
1294 VmaReplay.exe MyRecording.csv
1295
1296<b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1297It's a human-readable, text file in CSV format (Comma Separated Values).
1298
1299\section record_and_replay_additional_considerations Additional considerations
1300
1301- Replaying file that was recorded on a different GPU (with different parameters
1302 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1303 set of memory heaps and types) may give different performance and memory usage
1304 results, as well as issue some warnings and errors.
1305- Current implementation of recording in VMA, as well as VmaReplay application, is
1306 coded and tested only on Windows. Inclusion of recording code is driven by
1307 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1308 add. Contributions are welcomed.
1309- Currently calls to vmaDefragment() function are not recorded.
1310
1311
1312\page usage_patterns Recommended usage patterns
1313
1314See also slides from talk:
1315[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)
1316
1317
1318\section usage_patterns_simple Simple patterns
1319
1320\subsection usage_patterns_simple_render_targets Render targets
1321
1322<b>When:</b>
1323Any resources that you frequently write and read on GPU,
1324e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1325images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1326
1327<b>What to do:</b>
1328Create them in video memory that is fastest to access from GPU using
1329#VMA_MEMORY_USAGE_GPU_ONLY.
1330
1331Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1332and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1333especially if they are large or if you plan to destroy and recreate them e.g. when
1334display resolution changes.
1335Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1336
1337\subsection usage_patterns_simple_immutable_resources Immutable resources
1338
1339<b>When:</b>
1340Any resources that you fill on CPU only once (aka "immutable") or infrequently
1341and then read frequently on GPU,
1342e.g. textures, vertex and index buffers, constant buffers that don't change often.
1343
1344<b>What to do:</b>
1345Create them in video memory that is fastest to access from GPU using
1346#VMA_MEMORY_USAGE_GPU_ONLY.
1347
1348To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1349in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1350and submit a transfer from it to the GPU resource.
1351You can keep the staging copy if you need it for another upload transfer in the future.
1352If you don't, you can destroy it or reuse this buffer for uploading different resource
1353after the transfer finishes.
1354
1355Prefer to create just buffers in system memory rather than images, even for uploading textures.
1356Use `vkCmdCopyBufferToImage()`.
1357Dont use images with `VK_IMAGE_TILING_LINEAR`.
1358
1359\subsection usage_patterns_dynamic_resources Dynamic resources
1360
1361<b>When:</b>
1362Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1363written on CPU, read on GPU.
1364
1365<b>What to do:</b>
1366Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1367You can map it and write to it directly on CPU, as well as read from it on GPU.
1368
1369This is a more complex situation. Different solutions are possible,
1370and the best one depends on specific GPU type, but you can use this simple approach for the start.
1371Prefer to write to such resource sequentially (e.g. using `memcpy`).
1372Don't perform random access or any reads from it on CPU, as it may be very slow.
1373
1374\subsection usage_patterns_readback Readback
1375
1376<b>When:</b>
1377Resources that contain data written by GPU that you want to read back on CPU,
1378e.g. results of some computations.
1379
1380<b>What to do:</b>
1381Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1382You can write to them directly on GPU, as well as map and read them on CPU.
1383
1384\section usage_patterns_advanced Advanced patterns
1385
1386\subsection usage_patterns_integrated_graphics Detecting integrated graphics
1387
1388You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1389by detecting it in Vulkan.
1390To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1391`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1392When you find it, you can assume that memory is unified and all memory types are comparably fast
1393to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1394
1395You can then sum up sizes of all available memory heaps and treat them as useful for
1396your GPU resources, instead of only `DEVICE_LOCAL` ones.
1397You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1398directly instead of submitting explicit transfer (see below).
1399
1400\subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1401
1402For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1403
1404-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1405 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1406-# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1407 read it directly on GPU.
1408-# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1409 read it directly on GPU.
1410
1411Which solution is the most efficient depends on your resource and especially on the GPU.
1412It is best to measure it and then make the decision.
1413Some general recommendations:
1414
1415- On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1416 related to using a second copy and making transfer.
1417- For small resources (e.g. constant buffers) use (2).
1418 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1419 Even if the resource ends up in system memory, its data may be cached on GPU after first
1420 fetch over PCIe bus.
1421- For larger resources (e.g. textures), decide between (1) and (2).
1422 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1423 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1424
1425Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1426solutions are possible:
1427
1428-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1429 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1430-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1431 map it and read it on CPU.
1432
1433You should take some measurements to decide which option is faster in case of your specific
1434resource.
1435
1436If you don't want to specialize your code for specific types of GPUs, you can still make
1437an simple optimization for cases when your resource ends up in mappable memory to use it
1438directly in this case instead of creating CPU-side staging copy.
1439For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1440
1441
1442\page configuration Configuration
1443
1444Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1445before each include of this file or change directly in this file to provide
1446your own implementation of basic facilities like assert, `min()` and `max()` functions,
1447mutex, atomic etc.
1448The library uses its own implementation of containers by default, but you can switch to using
1449STL containers instead.
1450
1451\section config_Vulkan_functions Pointers to Vulkan functions
1452
1453The library uses Vulkan functions straight from the `vulkan.h` header by default.
1454If you want to provide your own pointers to these functions, e.g. fetched using
1455`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1456
1457-# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1458-# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1459
1460\section custom_memory_allocator Custom host memory allocator
1461
1462If you use custom allocator for CPU memory rather than default operator `new`
1463and `delete` from C++, you can make this library using your allocator as well
1464by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1465functions will be passed to Vulkan, as well as used by the library itself to
1466make any CPU-side allocations.
1467
1468\section allocation_callbacks Device memory allocation callbacks
1469
1470The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1471You can setup callbacks to be informed about these calls, e.g. for the purpose
1472of gathering some statistics. To do it, fill optional member
1473VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1474
1475\section heap_memory_limit Device heap memory limit
1476
1477If you want to test how your program behaves with limited amount of Vulkan device
1478memory available without switching your graphics card to one that really has
1479smaller VRAM, you can use a feature of this library intended for this purpose.
1480To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1481
1482
1483
1484\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1485
1486VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1487performance on some GPUs. It augments Vulkan API with possibility to query
1488driver whether it prefers particular buffer or image to have its own, dedicated
1489allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1490to do some internal optimizations.
1491
1492The extension is supported by this library. It will be used automatically when
1493enabled. To enable it:
1494
14951 . When creating Vulkan device, check if following 2 device extensions are
1496supported (call `vkEnumerateDeviceExtensionProperties()`).
1497If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1498
1499- VK_KHR_get_memory_requirements2
1500- VK_KHR_dedicated_allocation
1501
1502If you enabled these extensions:
1503
15042 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1505your #VmaAllocator`to inform the library that you enabled required extensions
1506and you want the library to use them.
1507
1508\code
1509allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1510
1511vmaCreateAllocator(&allocatorInfo, &allocator);
1512\endcode
1513
1514That's all. The extension will be automatically used whenever you create a
1515buffer using vmaCreateBuffer() or image using vmaCreateImage().
1516
1517When using the extension together with Vulkan Validation Layer, you will receive
1518warnings like this:
1519
1520 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1521
1522It is OK, you should just ignore it. It happens because you use function
1523`vkGetBufferMemoryRequirements2KHR()` instead of standard
1524`vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1525unaware of it.
1526
1527To learn more about this extension, see:
1528
1529- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1530- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1531
1532
1533
1534\page general_considerations General considerations
1535
1536\section general_considerations_thread_safety Thread safety
1537
1538- The library has no global state, so separate #VmaAllocator objects can be used
1539 independently.
1540 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1541- By default, all calls to functions that take #VmaAllocator as first parameter
1542 are safe to call from multiple threads simultaneously because they are
1543 synchronized internally when needed.
1544- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1545 flag, calls to functions that take such #VmaAllocator object must be
1546 synchronized externally.
1547- Access to a #VmaAllocation object must be externally synchronized. For example,
1548 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1549 threads at the same time if you pass the same #VmaAllocation object to these
1550 functions.
1551
1552\section general_considerations_validation_layer_warnings Validation layer warnings
1553
1554When using this library, you can meet following types of warnings issued by
1555Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1556to just ignore them.
1557
1558- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1559 - It happens when VK_KHR_dedicated_allocation extension is enabled.
1560 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1561- *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.*
1562 - It happens when you map a buffer or image, because the library maps entire
1563 `VkDeviceMemory` block, where different types of images and buffers may end
1564 up together, especially on GPUs with unified memory like Intel.
1565- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1566 - It happens when you use lost allocations, and a new image or buffer is
1567 created in place of an existing object that bacame lost.
1568 - It may happen also when you use [defragmentation](@ref defragmentation).
1569
1570\section general_considerations_allocation_algorithm Allocation algorithm
1571
1572The library uses following algorithm for allocation, in order:
1573
1574-# Try to find free range of memory in existing blocks.
1575-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1576-# If failed, try to create such block with size/2, size/4, size/8.
1577-# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1578 specified, try to find space in existing blocks, possilby making some other
1579 allocations lost.
1580-# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1581 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1582-# If failed, choose other memory type that meets the requirements specified in
1583 VmaAllocationCreateInfo and go to point 1.
1584-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1585
1586\section general_considerations_features_not_supported Features not supported
1587
1588Features deliberately excluded from the scope of this library:
1589
1590- Data transfer. Uploading (straming) and downloading data of buffers and images
1591 between CPU and GPU memory and related synchronization is responsibility of the user.
1592- Allocations for imported/exported external memory. They tend to require
1593 explicit memory type index and dedicated allocation anyway, so they don't
1594 interact with main features of this library. Such special purpose allocations
1595 should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1596- Recreation of buffers and images. Although the library has functions for
1597 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1598 recreate these objects yourself after defragmentation. That's because the big
1599 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1600 #VmaAllocation object.
1601- Handling CPU memory allocation failures. When dynamically creating small C++
1602 objects in CPU memory (not Vulkan memory), allocation failures are not checked
1603 and handled gracefully, because that would complicate code significantly and
1604 is usually not needed in desktop PC applications anyway.
1605- Code free of any compiler warnings. Maintaining the library to compile and
1606 work correctly on so many different platforms is hard enough. Being free of
1607 any warnings, on any version of any compiler, is simply not feasible.
1608- This is a C++ library with C interface.
1609 Bindings or ports to any other programming languages are welcomed as external projects and
1610 are not going to be included into this repository.
1611
1612*/
1613
1614/*
1615Define this macro to 0/1 to disable/enable support for recording functionality,
1616available through VmaAllocatorCreateInfo::pRecordSettings.
1617*/
1618#ifndef VMA_RECORDING_ENABLED
1619 #ifdef _WIN32
1620 #define VMA_RECORDING_ENABLED 1
1621 #else
1622 #define VMA_RECORDING_ENABLED 0
1623 #endif
1624#endif
1625
1626#ifndef NOMINMAX
1627 #define NOMINMAX // For windows.h
1628#endif
1629
1630#ifndef VULKAN_H_
1631 #include <vulkan/vulkan.h>
1632#endif
1633
1634#if VMA_RECORDING_ENABLED
1635 #include <windows.h>
1636#endif
1637
1638#if !defined(VMA_DEDICATED_ALLOCATION)
1639 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1640 #define VMA_DEDICATED_ALLOCATION 1
1641 #else
1642 #define VMA_DEDICATED_ALLOCATION 0
1643 #endif
1644#endif
1645
1646/** \struct VmaAllocator
1647\brief Represents main object of this library initialized.
1648
1649Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1650Call function vmaDestroyAllocator() to destroy it.
1651
1652It is recommended to create just one object of this type per `VkDevice` object,
1653right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1654*/
1655VK_DEFINE_HANDLE(VmaAllocator)
1656
1657/// Callback function called after successful vkAllocateMemory.
1658typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1659 VmaAllocator allocator,
1660 uint32_t memoryType,
1661 VkDeviceMemory memory,
1662 VkDeviceSize size);
1663/// Callback function called before vkFreeMemory.
1664typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1665 VmaAllocator allocator,
1666 uint32_t memoryType,
1667 VkDeviceMemory memory,
1668 VkDeviceSize size);
1669
1670/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1671
1672Provided for informative purpose, e.g. to gather statistics about number of
1673allocations or total amount of memory allocated in Vulkan.
1674
1675Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1676*/
1677typedef struct VmaDeviceMemoryCallbacks {
1678 /// Optional, can be null.
1679 PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1680 /// Optional, can be null.
1681 PFN_vmaFreeDeviceMemoryFunction pfnFree;
1682} VmaDeviceMemoryCallbacks;
1683
1684/// Flags for created #VmaAllocator.
1685typedef enum VmaAllocatorCreateFlagBits {
1686 /** \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.
1687
1688 Using this flag may increase performance because internal mutexes are not used.
1689 */
1690 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1691 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1692
1693 Using this extenion will automatically allocate dedicated blocks of memory for
1694 some buffers and images instead of suballocating place for them out of bigger
1695 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1696 flag) when it is recommended by the driver. It may improve performance on some
1697 GPUs.
1698
1699 You may set this flag only if you found out that following device extensions are
1700 supported, you enabled them while creating Vulkan device passed as
1701 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1702 library:
1703
1704 - VK_KHR_get_memory_requirements2
1705 - VK_KHR_dedicated_allocation
1706
1707When this flag is set, you can experience following warnings reported by Vulkan
1708validation layer. You can ignore them.
1709
1710> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1711 */
1712 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1713
1714 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1715} VmaAllocatorCreateFlagBits;
1716typedef VkFlags VmaAllocatorCreateFlags;
1717
1718/** \brief Pointers to some Vulkan functions - a subset used by the library.
1719
1720Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1721*/
1722typedef struct VmaVulkanFunctions {
1723 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1724 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1725 PFN_vkAllocateMemory vkAllocateMemory;
1726 PFN_vkFreeMemory vkFreeMemory;
1727 PFN_vkMapMemory vkMapMemory;
1728 PFN_vkUnmapMemory vkUnmapMemory;
1729 PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1730 PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1731 PFN_vkBindBufferMemory vkBindBufferMemory;
1732 PFN_vkBindImageMemory vkBindImageMemory;
1733 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1734 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1735 PFN_vkCreateBuffer vkCreateBuffer;
1736 PFN_vkDestroyBuffer vkDestroyBuffer;
1737 PFN_vkCreateImage vkCreateImage;
1738 PFN_vkDestroyImage vkDestroyImage;
1739 PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1740#if VMA_DEDICATED_ALLOCATION
1741 PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1742 PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1743#endif
1744} VmaVulkanFunctions;
1745
1746/// Flags to be used in VmaRecordSettings::flags.
1747typedef enum VmaRecordFlagBits {
1748 /** \brief Enables flush after recording every function call.
1749
1750 Enable it if you expect your application to crash, which may leave recording file truncated.
1751 It may degrade performance though.
1752 */
1753 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1754
1755 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1756} VmaRecordFlagBits;
1757typedef VkFlags VmaRecordFlags;
1758
1759/// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1760typedef struct VmaRecordSettings
1761{
1762 /// Flags for recording. Use #VmaRecordFlagBits enum.
1763 VmaRecordFlags flags;
1764 /** \brief Path to the file that should be written by the recording.
1765
1766 Suggested extension: "csv".
1767 If the file already exists, it will be overwritten.
1768 It will be opened for the whole time #VmaAllocator object is alive.
1769 If opening this file fails, creation of the whole allocator object fails.
1770 */
1771 const char* pFilePath;
1772} VmaRecordSettings;
1773
1774/// Description of a Allocator to be created.
1775typedef struct VmaAllocatorCreateInfo
1776{
1777 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1778 VmaAllocatorCreateFlags flags;
1779 /// Vulkan physical device.
1780 /** It must be valid throughout whole lifetime of created allocator. */
1781 VkPhysicalDevice physicalDevice;
1782 /// Vulkan device.
1783 /** It must be valid throughout whole lifetime of created allocator. */
1784 VkDevice device;
1785 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1786 /** Set to 0 to use default, which is currently 256 MiB. */
1787 VkDeviceSize preferredLargeHeapBlockSize;
1788 /// Custom CPU memory allocation callbacks. Optional.
1789 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1790 const VkAllocationCallbacks* pAllocationCallbacks;
1791 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1792 /** Optional, can be null. */
1793 const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1794 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1795
1796 This value is used only when you make allocations with
1797 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1798 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1799
1800 For example, if you double-buffer your command buffers, so resources used for
1801 rendering in previous frame may still be in use by the GPU at the moment you
1802 allocate resources needed for the current frame, set this value to 1.
1803
1804 If you want to allow any allocations other than used in the current frame to
1805 become lost, set this value to 0.
1806 */
1807 uint32_t frameInUseCount;
1808 /** \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.
1809
1810 If not NULL, it must be a pointer to an array of
1811 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1812 maximum number of bytes that can be allocated out of particular Vulkan memory
1813 heap.
1814
1815 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1816 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1817
1818 If there is a limit defined for a heap:
1819
1820 - If user tries to allocate more memory from that heap using this allocator,
1821 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1822 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1823 value of this limit will be reported instead when using vmaGetMemoryProperties().
1824
1825 Warning! Using this feature may not be equivalent to installing a GPU with
1826 smaller amount of memory, because graphics driver doesn't necessary fail new
1827 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1828 exceeded. It may return success and just silently migrate some device memory
1829 blocks to system RAM. This driver behavior can also be controlled using
1830 VK_AMD_memory_overallocation_behavior extension.
1831 */
1832 const VkDeviceSize* pHeapSizeLimit;
1833 /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1834
1835 If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1836 you can pass null as this member, because the library will fetch pointers to
1837 Vulkan functions internally in a static way, like:
1838
1839 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1840
1841 Fill this member if you want to provide your own pointers to Vulkan functions,
1842 e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1843 */
1844 const VmaVulkanFunctions* pVulkanFunctions;
1845 /** \brief Parameters for recording of VMA calls. Can be null.
1846
1847 If not null, it enables recording of calls to VMA functions to a file.
1848 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1849 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1850 */
1851 const VmaRecordSettings* pRecordSettings;
1852} VmaAllocatorCreateInfo;
1853
1854/// Creates Allocator object.
1855VkResult vmaCreateAllocator(
1856 const VmaAllocatorCreateInfo* pCreateInfo,
1857 VmaAllocator* pAllocator);
1858
1859/// Destroys allocator object.
1860void vmaDestroyAllocator(
1861 VmaAllocator allocator);
1862
1863/**
1864PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1865You can access it here, without fetching it again on your own.
1866*/
1867void vmaGetPhysicalDeviceProperties(
1868 VmaAllocator allocator,
1869 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1870
1871/**
1872PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1873You can access it here, without fetching it again on your own.
1874*/
1875void vmaGetMemoryProperties(
1876 VmaAllocator allocator,
1877 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1878
1879/**
1880\brief Given Memory Type Index, returns Property Flags of this memory type.
1881
1882This is just a convenience function. Same information can be obtained using
1883vmaGetMemoryProperties().
1884*/
1885void vmaGetMemoryTypeProperties(
1886 VmaAllocator allocator,
1887 uint32_t memoryTypeIndex,
1888 VkMemoryPropertyFlags* pFlags);
1889
1890/** \brief Sets index of the current frame.
1891
1892This function must be used if you make allocations with
1893#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1894#VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1895when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1896become lost in the current frame.
1897*/
1898void vmaSetCurrentFrameIndex(
1899 VmaAllocator allocator,
1900 uint32_t frameIndex);
1901
1902/** \brief Calculated statistics of memory usage in entire allocator.
1903*/
1904typedef struct VmaStatInfo
1905{
1906 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1907 uint32_t blockCount;
1908 /// Number of #VmaAllocation allocation objects allocated.
1909 uint32_t allocationCount;
1910 /// Number of free ranges of memory between allocations.
1911 uint32_t unusedRangeCount;
1912 /// Total number of bytes occupied by all allocations.
1913 VkDeviceSize usedBytes;
1914 /// Total number of bytes occupied by unused ranges.
1915 VkDeviceSize unusedBytes;
1916 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1917 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1918} VmaStatInfo;
1919
1920/// General statistics from current state of Allocator.
1921typedef struct VmaStats
1922{
1923 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1924 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1925 VmaStatInfo total;
1926} VmaStats;
1927
1928/// Retrieves statistics from current state of the Allocator.
1929void vmaCalculateStats(
1930 VmaAllocator allocator,
1931 VmaStats* pStats);
1932
1933#define VMA_STATS_STRING_ENABLED 1
1934
1935#if VMA_STATS_STRING_ENABLED
1936
1937/// Builds and returns statistics as string in JSON format.
1938/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1939*/
1940void vmaBuildStatsString(
1941 VmaAllocator allocator,
1942 char** ppStatsString,
1943 VkBool32 detailedMap);
1944
1945void vmaFreeStatsString(
1946 VmaAllocator allocator,
1947 char* pStatsString);
1948
1949#endif // #if VMA_STATS_STRING_ENABLED
1950
1951/** \struct VmaPool
1952\brief Represents custom memory pool
1953
1954Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1955Call function vmaDestroyPool() to destroy it.
1956
1957For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1958*/
1959VK_DEFINE_HANDLE(VmaPool)
1960
1961typedef enum VmaMemoryUsage
1962{
1963 /** No intended memory usage specified.
1964 Use other members of VmaAllocationCreateInfo to specify your requirements.
1965 */
1966 VMA_MEMORY_USAGE_UNKNOWN = 0,
1967 /** Memory will be used on device only, so fast access from the device is preferred.
1968 It usually means device-local GPU (video) memory.
1969 No need to be mappable on host.
1970 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1971
1972 Usage:
1973
1974 - Resources written and read by device, e.g. images used as attachments.
1975 - Resources transferred from host once (immutable) or infrequently and read by
1976 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1977 (constant) buffers, and majority of other types of resources used on GPU.
1978
1979 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1980 In such case, you are free to map it.
1981 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1982 */
1983 VMA_MEMORY_USAGE_GPU_ONLY = 1,
1984 /** Memory will be mappable on host.
1985 It usually means CPU (system) memory.
1986 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1987 CPU access is typically uncached. Writes may be write-combined.
1988 Resources created in this pool may still be accessible to the device, but access to them can be slow.
1989 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
1990
1991 Usage: Staging copy of resources used as transfer source.
1992 */
1993 VMA_MEMORY_USAGE_CPU_ONLY = 2,
1994 /**
1995 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
1996 CPU access is typically uncached. Writes may be write-combined.
1997
1998 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
1999 */
2000 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2001 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2002 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2003
2004 Usage:
2005
2006 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2007 - 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.
2008 */
2009 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2010 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2011} VmaMemoryUsage;
2012
2013/// Flags to be passed as VmaAllocationCreateInfo::flags.
2014typedef enum VmaAllocationCreateFlagBits {
2015 /** \brief Set this flag if the allocation should have its own memory block.
2016
2017 Use it for special, big resources, like fullscreen images used as attachments.
2018
2019 This flag must also be used for host visible resources that you want to map
2020 simultaneously because otherwise they might end up as regions of the same
2021 `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2022 simultaneously is illegal.
2023
2024 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2025 */
2026 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2027
2028 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2029
2030 If new allocation cannot be placed in any of the existing blocks, allocation
2031 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2032
2033 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2034 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2035
2036 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2037 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2038 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2039
2040 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2041
2042 Is it valid to use this flag for allocation made from memory type that is not
2043 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2044 useful if you need an allocation that is efficient to use on GPU
2045 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2046 support it (e.g. Intel GPU).
2047
2048 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2049 */
2050 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2051 /** Allocation created with this flag can become lost as a result of another
2052 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2053 must check it before use.
2054
2055 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2056 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2057
2058 For details about supporting lost allocations, see Lost Allocations
2059 chapter of User Guide on Main Page.
2060
2061 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2062 */
2063 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2064 /** While creating allocation using this flag, other allocations that were
2065 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2066
2067 For details about supporting lost allocations, see Lost Allocations
2068 chapter of User Guide on Main Page.
2069 */
2070 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2071 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2072 null-terminated string. Instead of copying pointer value, a local copy of the
2073 string is made and stored in allocation's `pUserData`. The string is automatically
2074 freed together with the allocation. It is also used in vmaBuildStatsString().
2075 */
2076 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2077 /** Allocation will be created from upper stack in a double stack pool.
2078
2079 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2080 */
2081 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2082
2083 /** Allocation strategy that chooses smallest possible free range for the
2084 allocation.
2085 */
2086 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2087 /** Allocation strategy that chooses biggest possible free range for the
2088 allocation.
2089 */
2090 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2091 /** Allocation strategy that chooses first suitable free range for the
2092 allocation.
2093
2094 "First" doesn't necessarily means the one with smallest offset in memory,
2095 but rather the one that is easiest and fastest to find.
2096 */
2097 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2098
2099 /** Allocation strategy that tries to minimize memory usage.
2100 */
2101 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2102 /** Allocation strategy that tries to minimize allocation time.
2103 */
2104 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2105 /** Allocation strategy that tries to minimize memory fragmentation.
2106 */
2107 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2108
2109 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2110 */
2111 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2112 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2113 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2114 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2115
2116 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2117} VmaAllocationCreateFlagBits;
2118typedef VkFlags VmaAllocationCreateFlags;
2119
2120typedef struct VmaAllocationCreateInfo
2121{
2122 /// Use #VmaAllocationCreateFlagBits enum.
2123 VmaAllocationCreateFlags flags;
2124 /** \brief Intended usage of memory.
2125
2126 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2127 If `pool` is not null, this member is ignored.
2128 */
2129 VmaMemoryUsage usage;
2130 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2131
2132 Leave 0 if you specify memory requirements in other way. \n
2133 If `pool` is not null, this member is ignored.*/
2134 VkMemoryPropertyFlags requiredFlags;
2135 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2136
2137 Set to 0 if no additional flags are prefered. \n
2138 If `pool` is not null, this member is ignored. */
2139 VkMemoryPropertyFlags preferredFlags;
2140 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2141
2142 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2143 it meets other requirements specified by this structure, with no further
2144 restrictions on memory type index. \n
2145 If `pool` is not null, this member is ignored.
2146 */
2147 uint32_t memoryTypeBits;
2148 /** \brief Pool that this allocation should be created in.
2149
2150 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2151 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2152 */
2153 VmaPool pool;
2154 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2155
2156 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2157 null or pointer to a null-terminated string. The string will be then copied to
2158 internal buffer, so it doesn't need to be valid after allocation call.
2159 */
2160 void* pUserData;
2161} VmaAllocationCreateInfo;
2162
2163/**
2164\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2165
2166This algorithm tries to find a memory type that:
2167
2168- Is allowed by memoryTypeBits.
2169- Contains all the flags from pAllocationCreateInfo->requiredFlags.
2170- Matches intended usage.
2171- Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2172
2173\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2174from this function or any other allocating function probably means that your
2175device doesn't support any memory type with requested features for the specific
2176type of resource you want to use it for. Please check parameters of your
2177resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2178*/
2179VkResult vmaFindMemoryTypeIndex(
2180 VmaAllocator allocator,
2181 uint32_t memoryTypeBits,
2182 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2183 uint32_t* pMemoryTypeIndex);
2184
2185/**
2186\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2187
2188It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2189It internally creates a temporary, dummy buffer that never has memory bound.
2190It is just a convenience function, equivalent to calling:
2191
2192- `vkCreateBuffer`
2193- `vkGetBufferMemoryRequirements`
2194- `vmaFindMemoryTypeIndex`
2195- `vkDestroyBuffer`
2196*/
2197VkResult vmaFindMemoryTypeIndexForBufferInfo(
2198 VmaAllocator allocator,
2199 const VkBufferCreateInfo* pBufferCreateInfo,
2200 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2201 uint32_t* pMemoryTypeIndex);
2202
2203/**
2204\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2205
2206It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2207It internally creates a temporary, dummy image that never has memory bound.
2208It is just a convenience function, equivalent to calling:
2209
2210- `vkCreateImage`
2211- `vkGetImageMemoryRequirements`
2212- `vmaFindMemoryTypeIndex`
2213- `vkDestroyImage`
2214*/
2215VkResult vmaFindMemoryTypeIndexForImageInfo(
2216 VmaAllocator allocator,
2217 const VkImageCreateInfo* pImageCreateInfo,
2218 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2219 uint32_t* pMemoryTypeIndex);
2220
2221/// Flags to be passed as VmaPoolCreateInfo::flags.
2222typedef enum VmaPoolCreateFlagBits {
2223 /** \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.
2224
2225 This is an optional optimization flag.
2226
2227 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2228 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2229 knows exact type of your allocations so it can handle Buffer-Image Granularity
2230 in the optimal way.
2231
2232 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2233 exact type of such allocations is not known, so allocator must be conservative
2234 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2235 (wasted memory). In that case, if you can make sure you always allocate only
2236 buffers and linear images or only optimal images out of this pool, use this flag
2237 to make allocator disregard Buffer-Image Granularity and so make allocations
2238 faster and more optimal.
2239 */
2240 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2241
2242 /** \brief Enables alternative, linear allocation algorithm in this pool.
2243
2244 Specify this flag to enable linear allocation algorithm, which always creates
2245 new allocations after last one and doesn't reuse space from allocations freed in
2246 between. It trades memory consumption for simplified algorithm and data
2247 structure, which has better performance and uses less memory for metadata.
2248
2249 By using this flag, you can achieve behavior of free-at-once, stack,
2250 ring buffer, and double stack. For details, see documentation chapter
2251 \ref linear_algorithm.
2252
2253 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2254
2255 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2256 */
2257 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2258
2259 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2260
2261 It operates on a tree of blocks, each having size that is a power of two and
2262 a half of its parent's size. Comparing to default algorithm, this one provides
2263 faster allocation and deallocation and decreased external fragmentation,
2264 at the expense of more memory wasted (internal fragmentation).
2265
2266 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2267 */
2268 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2269
2270 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2271 */
2272 VMA_POOL_CREATE_ALGORITHM_MASK =
2273 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2274 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2275
2276 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2277} VmaPoolCreateFlagBits;
2278typedef VkFlags VmaPoolCreateFlags;
2279
2280/** \brief Describes parameter of created #VmaPool.
2281*/
2282typedef struct VmaPoolCreateInfo {
2283 /** \brief Vulkan memory type index to allocate this pool from.
2284 */
2285 uint32_t memoryTypeIndex;
2286 /** \brief Use combination of #VmaPoolCreateFlagBits.
2287 */
2288 VmaPoolCreateFlags flags;
2289 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2290
2291 Specify nonzero to set explicit, constant size of memory blocks used by this
2292 pool.
2293
2294 Leave 0 to use default and let the library manage block sizes automatically.
2295 Sizes of particular blocks may vary.
2296 */
2297 VkDeviceSize blockSize;
2298 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2299
2300 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2301 */
2302 size_t minBlockCount;
2303 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2304
2305 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2306
2307 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2308 throughout whole lifetime of this pool.
2309 */
2310 size_t maxBlockCount;
2311 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2312
2313 This value is used only when you make allocations with
2314 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2315 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2316
2317 For example, if you double-buffer your command buffers, so resources used for
2318 rendering in previous frame may still be in use by the GPU at the moment you
2319 allocate resources needed for the current frame, set this value to 1.
2320
2321 If you want to allow any allocations other than used in the current frame to
2322 become lost, set this value to 0.
2323 */
2324 uint32_t frameInUseCount;
2325} VmaPoolCreateInfo;
2326
2327/** \brief Describes parameter of existing #VmaPool.
2328*/
2329typedef struct VmaPoolStats {
2330 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2331 */
2332 VkDeviceSize size;
2333 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2334 */
2335 VkDeviceSize unusedSize;
2336 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2337 */
2338 size_t allocationCount;
2339 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2340 */
2341 size_t unusedRangeCount;
2342 /** \brief Size of the largest continuous free memory region available for new allocation.
2343
2344 Making a new allocation of that size is not guaranteed to succeed because of
2345 possible additional margin required to respect alignment and buffer/image
2346 granularity.
2347 */
2348 VkDeviceSize unusedRangeSizeMax;
2349 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2350 */
2351 size_t blockCount;
2352} VmaPoolStats;
2353
2354/** \brief Allocates Vulkan device memory and creates #VmaPool object.
2355
2356@param allocator Allocator object.
2357@param pCreateInfo Parameters of pool to create.
2358@param[out] pPool Handle to created pool.
2359*/
2360VkResult vmaCreatePool(
2361 VmaAllocator allocator,
2362 const VmaPoolCreateInfo* pCreateInfo,
2363 VmaPool* pPool);
2364
2365/** \brief Destroys #VmaPool object and frees Vulkan device memory.
2366*/
2367void vmaDestroyPool(
2368 VmaAllocator allocator,
2369 VmaPool pool);
2370
2371/** \brief Retrieves statistics of existing #VmaPool object.
2372
2373@param allocator Allocator object.
2374@param pool Pool object.
2375@param[out] pPoolStats Statistics of specified pool.
2376*/
2377void vmaGetPoolStats(
2378 VmaAllocator allocator,
2379 VmaPool pool,
2380 VmaPoolStats* pPoolStats);
2381
2382/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2383
2384@param allocator Allocator object.
2385@param pool Pool.
2386@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2387*/
2388void vmaMakePoolAllocationsLost(
2389 VmaAllocator allocator,
2390 VmaPool pool,
2391 size_t* pLostAllocationCount);
2392
2393/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2394
2395Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2396`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2397`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2398
2399Possible return values:
2400
2401- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2402- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2403- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2404 `VMA_ASSERT` is also fired in that case.
2405- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2406*/
2407VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2408
2409/** \struct VmaAllocation
2410\brief Represents single memory allocation.
2411
2412It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2413plus unique offset.
2414
2415There are multiple ways to create such object.
2416You need to fill structure VmaAllocationCreateInfo.
2417For more information see [Choosing memory type](@ref choosing_memory_type).
2418
2419Although the library provides convenience functions that create Vulkan buffer or image,
2420allocate memory for it and bind them together,
2421binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2422Allocation object can exist without buffer/image bound,
2423binding can be done manually by the user, and destruction of it can be done
2424independently of destruction of the allocation.
2425
2426The object also remembers its size and some other information.
2427To retrieve this information, use function vmaGetAllocationInfo() and inspect
2428returned structure VmaAllocationInfo.
2429
2430Some kinds allocations can be in lost state.
2431For more information, see [Lost allocations](@ref lost_allocations).
2432*/
2433VK_DEFINE_HANDLE(VmaAllocation)
2434
2435/** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2436*/
2437typedef struct VmaAllocationInfo {
2438 /** \brief Memory type index that this allocation was allocated from.
2439
2440 It never changes.
2441 */
2442 uint32_t memoryType;
2443 /** \brief Handle to Vulkan memory object.
2444
2445 Same memory object can be shared by multiple allocations.
2446
2447 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2448
2449 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2450 */
2451 VkDeviceMemory deviceMemory;
2452 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2453
2454 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2455 */
2456 VkDeviceSize offset;
2457 /** \brief Size of this allocation, in bytes.
2458
2459 It never changes, unless allocation is lost.
2460 */
2461 VkDeviceSize size;
2462 /** \brief Pointer to the beginning of this allocation as mapped data.
2463
2464 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2465 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2466
2467 It can change after call to vmaMapMemory(), vmaUnmapMemory().
2468 It can also change after call to vmaDefragment() if this allocation is passed to the function.
2469 */
2470 void* pMappedData;
2471 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2472
2473 It can change after call to vmaSetAllocationUserData() for this allocation.
2474 */
2475 void* pUserData;
2476} VmaAllocationInfo;
2477
2478/** \brief General purpose memory allocation.
2479
2480@param[out] pAllocation Handle to allocated memory.
2481@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2482
2483You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2484
2485It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2486vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2487*/
2488VkResult vmaAllocateMemory(
2489 VmaAllocator allocator,
2490 const VkMemoryRequirements* pVkMemoryRequirements,
2491 const VmaAllocationCreateInfo* pCreateInfo,
2492 VmaAllocation* pAllocation,
2493 VmaAllocationInfo* pAllocationInfo);
2494
2495/** \brief General purpose memory allocation for multiple allocation objects at once.
2496
2497@param allocator Allocator object.
2498@param pVkMemoryRequirements Memory requirements for each allocation.
2499@param pCreateInfo Creation parameters for each alloction.
2500@param allocationCount Number of allocations to make.
2501@param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2502@param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2503
2504You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2505
2506Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2507It is just a general purpose allocation function able to make multiple allocations at once.
2508It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2509
2510All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2511If any allocation fails, all allocations already made within this function call are also freed, so that when
2512returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2513*/
2514VkResult vmaAllocateMemoryPages(
2515 VmaAllocator allocator,
2516 const VkMemoryRequirements* pVkMemoryRequirements,
2517 const VmaAllocationCreateInfo* pCreateInfo,
2518 size_t allocationCount,
2519 VmaAllocation* pAllocations,
2520 VmaAllocationInfo* pAllocationInfo);
2521
2522/**
2523@param[out] pAllocation Handle to allocated memory.
2524@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2525
2526You should free the memory using vmaFreeMemory().
2527*/
2528VkResult vmaAllocateMemoryForBuffer(
2529 VmaAllocator allocator,
2530 VkBuffer buffer,
2531 const VmaAllocationCreateInfo* pCreateInfo,
2532 VmaAllocation* pAllocation,
2533 VmaAllocationInfo* pAllocationInfo);
2534
2535/// Function similar to vmaAllocateMemoryForBuffer().
2536VkResult vmaAllocateMemoryForImage(
2537 VmaAllocator allocator,
2538 VkImage image,
2539 const VmaAllocationCreateInfo* pCreateInfo,
2540 VmaAllocation* pAllocation,
2541 VmaAllocationInfo* pAllocationInfo);
2542
2543/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2544
2545Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2546*/
2547void vmaFreeMemory(
2548 VmaAllocator allocator,
2549 VmaAllocation allocation);
2550
2551/** \brief Frees memory and destroys multiple allocations.
2552
2553Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2554It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2555vmaAllocateMemoryPages() and other functions.
2556It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2557
2558Allocations in `pAllocations` array can come from any memory pools and types.
2559Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2560*/
2561void vmaFreeMemoryPages(
2562 VmaAllocator allocator,
2563 size_t allocationCount,
2564 VmaAllocation* pAllocations);
2565
2566/** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2567
2568Tries to change allocation's size without moving or reallocating it.
2569You can both shrink and grow allocation size.
2570When growing, it succeeds only when the allocation belongs to a memory block with enough
2571free space after it.
2572
2573Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2574Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2575
2576After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2577All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2578
2579- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2580- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2581- Resizing dedicated allocations, as well as allocations created in pools that use linear
2582 or buddy algorithm, is not supported.
2583 The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2584 Support may be added in the future.
2585*/
2586VkResult vmaResizeAllocation(
2587 VmaAllocator allocator,
2588 VmaAllocation allocation,
2589 VkDeviceSize newSize);
2590
2591/** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2592
2593Current paramters of given allocation are returned in `pAllocationInfo`.
2594
2595This function also atomically "touches" allocation - marks it as used in current frame,
2596just like vmaTouchAllocation().
2597If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2598
2599Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2600you can avoid calling it too often.
2601
2602- You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2603 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2604 (e.g. due to defragmentation or allocation becoming lost).
2605- If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2606*/
2607void vmaGetAllocationInfo(
2608 VmaAllocator allocator,
2609 VmaAllocation allocation,
2610 VmaAllocationInfo* pAllocationInfo);
2611
2612/** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2613
2614If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2615this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2616It then also atomically "touches" the allocation - marks it as used in current frame,
2617so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2618
2619If the allocation is in lost state, the function returns `VK_FALSE`.
2620Memory of such allocation, as well as buffer or image bound to it, should not be used.
2621Lost allocation and the buffer/image still need to be destroyed.
2622
2623If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2624this function always returns `VK_TRUE`.
2625*/
2626VkBool32 vmaTouchAllocation(
2627 VmaAllocator allocator,
2628 VmaAllocation allocation);
2629
2630/** \brief Sets pUserData in given allocation to new value.
2631
2632If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2633pUserData must be either null, or pointer to a null-terminated string. The function
2634makes local copy of the string and sets it as allocation's `pUserData`. String
2635passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2636you can free it after this call. String previously pointed by allocation's
2637pUserData is freed from memory.
2638
2639If the flag was not used, the value of pointer `pUserData` is just copied to
2640allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2641as a pointer, ordinal number or some handle to you own data.
2642*/
2643void vmaSetAllocationUserData(
2644 VmaAllocator allocator,
2645 VmaAllocation allocation,
2646 void* pUserData);
2647
2648/** \brief Creates new allocation that is in lost state from the beginning.
2649
2650It can be useful if you need a dummy, non-null allocation.
2651
2652You still need to destroy created object using vmaFreeMemory().
2653
2654Returned allocation is not tied to any specific memory pool or memory type and
2655not bound to any image or buffer. It has size = 0. It cannot be turned into
2656a real, non-empty allocation.
2657*/
2658void vmaCreateLostAllocation(
2659 VmaAllocator allocator,
2660 VmaAllocation* pAllocation);
2661
2662/** \brief Maps memory represented by given allocation and returns pointer to it.
2663
2664Maps memory represented by given allocation to make it accessible to CPU code.
2665When succeeded, `*ppData` contains pointer to first byte of this memory.
2666If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2667correctly offseted to the beginning of region assigned to this particular
2668allocation.
2669
2670Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2671function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2672multiple times simultaneously, it is safe to call this function on allocations
2673assigned to the same memory block. Actual Vulkan memory will be mapped on first
2674mapping and unmapped on last unmapping.
2675
2676If the function succeeded, you must call vmaUnmapMemory() to unmap the
2677allocation when mapping is no longer needed or before freeing the allocation, at
2678the latest.
2679
2680It also safe to call this function multiple times on the same allocation. You
2681must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2682
2683It is also safe to call this function on allocation created with
2684#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2685You must still call vmaUnmapMemory() same number of times as you called
2686vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2687"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2688
2689This function fails when used on allocation made in memory type that is not
2690`HOST_VISIBLE`.
2691
2692This function always fails when called for allocation that was created with
2693#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2694mapped.
2695*/
2696VkResult vmaMapMemory(
2697 VmaAllocator allocator,
2698 VmaAllocation allocation,
2699 void** ppData);
2700
2701/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2702
2703For details, see description of vmaMapMemory().
2704*/
2705void vmaUnmapMemory(
2706 VmaAllocator allocator,
2707 VmaAllocation allocation);
2708
2709/** \brief Flushes memory of given allocation.
2710
2711Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2712
2713- `offset` must be relative to the beginning of allocation.
2714- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2715- `offset` and `size` don't have to be aligned.
2716 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2717- If `size` is 0, this call is ignored.
2718- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2719 this call is ignored.
2720*/
2721void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2722
2723/** \brief Invalidates memory of given allocation.
2724
2725Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2726
2727- `offset` must be relative to the beginning of allocation.
2728- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2729- `offset` and `size` don't have to be aligned.
2730 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2731- If `size` is 0, this call is ignored.
2732- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2733 this call is ignored.
2734*/
2735void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2736
2737/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2738
2739@param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2740
2741Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2742`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2743`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2744
2745Possible return values:
2746
2747- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2748- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2749- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2750 `VMA_ASSERT` is also fired in that case.
2751- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2752*/
2753VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2754
2755/** \struct VmaDefragmentationContext
2756\brief Represents Opaque object that represents started defragmentation process.
2757
2758Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2759Call function vmaDefragmentationEnd() to destroy it.
2760*/
2761VK_DEFINE_HANDLE(VmaDefragmentationContext)
2762
2763/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2764typedef enum VmaDefragmentationFlagBits {
2765 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2766} VmaDefragmentationFlagBits;
2767typedef VkFlags VmaDefragmentationFlags;
2768
2769/** \brief Parameters for defragmentation.
2770
2771To be used with function vmaDefragmentationBegin().
2772*/
2773typedef struct VmaDefragmentationInfo2 {
2774 /** \brief Reserved for future use. Should be 0.
2775 */
2776 VmaDefragmentationFlags flags;
2777 /** \brief Number of allocations in `pAllocations` array.
2778 */
2779 uint32_t allocationCount;
2780 /** \brief Pointer to array of allocations that can be defragmented.
2781
2782 The array should have `allocationCount` elements.
2783 The array should not contain nulls.
2784 Elements in the array should be unique - same allocation cannot occur twice.
2785 It is safe to pass allocations that are in the lost state - they are ignored.
2786 All allocations not present in this array are considered non-moveable during this defragmentation.
2787 */
2788 VmaAllocation* pAllocations;
2789 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2790
2791 The array should have `allocationCount` elements.
2792 You can pass null if you are not interested in this information.
2793 */
2794 VkBool32* pAllocationsChanged;
2795 /** \brief Numer of pools in `pPools` array.
2796 */
2797 uint32_t poolCount;
2798 /** \brief Either null or pointer to array of pools to be defragmented.
2799
2800 All the allocations in the specified pools can be moved during defragmentation
2801 and there is no way to check if they were really moved as in `pAllocationsChanged`,
2802 so you must query all the allocations in all these pools for new `VkDeviceMemory`
2803 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2804 and images bound to them.
2805
2806 The array should have `poolCount` elements.
2807 The array should not contain nulls.
2808 Elements in the array should be unique - same pool cannot occur twice.
2809
2810 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2811 It might be more efficient.
2812 */
2813 VmaPool* pPools;
2814 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2815
2816 `VK_WHOLE_SIZE` means no limit.
2817 */
2818 VkDeviceSize maxCpuBytesToMove;
2819 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2820
2821 `UINT32_MAX` means no limit.
2822 */
2823 uint32_t maxCpuAllocationsToMove;
2824 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2825
2826 `VK_WHOLE_SIZE` means no limit.
2827 */
2828 VkDeviceSize maxGpuBytesToMove;
2829 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2830
2831 `UINT32_MAX` means no limit.
2832 */
2833 uint32_t maxGpuAllocationsToMove;
2834 /** \brief Optional. Command buffer where GPU copy commands will be posted.
2835
2836 If not null, it must be a valid command buffer handle that supports Transfer queue type.
2837 It must be in the recording state and outside of a render pass instance.
2838 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2839
2840 Passing null means that only CPU defragmentation will be performed.
2841 */
2842 VkCommandBuffer commandBuffer;
2843} VmaDefragmentationInfo2;
2844
2845/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2846
2847\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2848*/
2849typedef struct VmaDefragmentationInfo {
2850 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2851
2852 Default is `VK_WHOLE_SIZE`, which means no limit.
2853 */
2854 VkDeviceSize maxBytesToMove;
2855 /** \brief Maximum number of allocations that can be moved to different place.
2856
2857 Default is `UINT32_MAX`, which means no limit.
2858 */
2859 uint32_t maxAllocationsToMove;
2860} VmaDefragmentationInfo;
2861
2862/** \brief Statistics returned by function vmaDefragment(). */
2863typedef struct VmaDefragmentationStats {
2864 /// Total number of bytes that have been copied while moving allocations to different places.
2865 VkDeviceSize bytesMoved;
2866 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2867 VkDeviceSize bytesFreed;
2868 /// Number of allocations that have been moved to different places.
2869 uint32_t allocationsMoved;
2870 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2871 uint32_t deviceMemoryBlocksFreed;
2872} VmaDefragmentationStats;
2873
2874/** \brief Begins defragmentation process.
2875
2876@param allocator Allocator object.
2877@param pInfo Structure filled with parameters of defragmentation.
2878@param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2879@param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2880@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.
2881
2882Use this function instead of old, deprecated vmaDefragment().
2883
2884Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2885
2886- You should not use any of allocations passed as `pInfo->pAllocations` or
2887 any allocations that belong to pools passed as `pInfo->pPools`,
2888 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2889 their data.
2890- Some mutexes protecting internal data structures may be locked, so trying to
2891 make or free any allocations, bind buffers or images, map memory, or launch
2892 another simultaneous defragmentation in between may cause stall (when done on
2893 another thread) or deadlock (when done on the same thread), unless you are
2894 100% sure that defragmented allocations are in different pools.
2895- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2896 They become valid after call to vmaDefragmentationEnd().
2897- If `pInfo->commandBuffer` is not null, you must submit that command buffer
2898 and make sure it finished execution before calling vmaDefragmentationEnd().
2899*/
2900VkResult vmaDefragmentationBegin(
2901 VmaAllocator allocator,
2902 const VmaDefragmentationInfo2* pInfo,
2903 VmaDefragmentationStats* pStats,
2904 VmaDefragmentationContext *pContext);
2905
2906/** \brief Ends defragmentation process.
2907
2908Use this function to finish defragmentation started by vmaDefragmentationBegin().
2909It is safe to pass `context == null`. The function then does nothing.
2910*/
2911VkResult vmaDefragmentationEnd(
2912 VmaAllocator allocator,
2913 VmaDefragmentationContext context);
2914
2915/** \brief Deprecated. Compacts memory by moving allocations.
2916
2917@param pAllocations Array of allocations that can be moved during this compation.
2918@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2919@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.
2920@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2921@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2922@return `VK_SUCCESS` if completed, negative error code in case of error.
2923
2924\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2925
2926This function works by moving allocations to different places (different
2927`VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2928usage. Only allocations that are in `pAllocations` array can be moved. All other
2929allocations are considered nonmovable in this call. Basic rules:
2930
2931- Only allocations made in memory types that have
2932 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2933 flags can be compacted. You may pass other allocations but it makes no sense -
2934 these will never be moved.
2935- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2936 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2937 passed to this function that come from such pools are ignored.
2938- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2939 created as dedicated allocations for any other reason are also ignored.
2940- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2941 flag can be compacted. If not persistently mapped, memory will be mapped
2942 temporarily inside this function if needed.
2943- You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2944
2945The function also frees empty `VkDeviceMemory` blocks.
2946
2947Warning: This function may be time-consuming, so you shouldn't call it too often
2948(like after every resource creation/destruction).
2949You can call it on special occasions (like when reloading a game level or
2950when you just destroyed a lot of objects). Calling it every frame may be OK, but
2951you should measure that on your platform.
2952
2953For more information, see [Defragmentation](@ref defragmentation) chapter.
2954*/
2955VkResult vmaDefragment(
2956 VmaAllocator allocator,
2957 VmaAllocation* pAllocations,
2958 size_t allocationCount,
2959 VkBool32* pAllocationsChanged,
2960 const VmaDefragmentationInfo *pDefragmentationInfo,
2961 VmaDefragmentationStats* pDefragmentationStats);
2962
2963/** \brief Binds buffer to allocation.
2964
2965Binds specified buffer to region of memory represented by specified allocation.
2966Gets `VkDeviceMemory` handle and offset from the allocation.
2967If you want to create a buffer, allocate memory for it and bind them together separately,
2968you should use this function for binding instead of standard `vkBindBufferMemory()`,
2969because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2970allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2971(which is illegal in Vulkan).
2972
2973It is recommended to use function vmaCreateBuffer() instead of this one.
2974*/
2975VkResult vmaBindBufferMemory(
2976 VmaAllocator allocator,
2977 VmaAllocation allocation,
2978 VkBuffer buffer);
2979
2980/** \brief Binds image to allocation.
2981
2982Binds specified image to region of memory represented by specified allocation.
2983Gets `VkDeviceMemory` handle and offset from the allocation.
2984If you want to create an image, allocate memory for it and bind them together separately,
2985you should use this function for binding instead of standard `vkBindImageMemory()`,
2986because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2987allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2988(which is illegal in Vulkan).
2989
2990It is recommended to use function vmaCreateImage() instead of this one.
2991*/
2992VkResult vmaBindImageMemory(
2993 VmaAllocator allocator,
2994 VmaAllocation allocation,
2995 VkImage image);
2996
2997/**
2998@param[out] pBuffer Buffer that was created.
2999@param[out] pAllocation Allocation that was created.
3000@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3001
3002This function automatically:
3003
3004-# Creates buffer.
3005-# Allocates appropriate memory for it.
3006-# Binds the buffer with the memory.
3007
3008If any of these operations fail, buffer and allocation are not created,
3009returned value is negative error code, *pBuffer and *pAllocation are null.
3010
3011If the function succeeded, you must destroy both buffer and allocation when you
3012no longer need them using either convenience function vmaDestroyBuffer() or
3013separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3014
3015If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3016VK_KHR_dedicated_allocation extension is used internally to query driver whether
3017it requires or prefers the new buffer to have dedicated allocation. If yes,
3018and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3019and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3020allocation for this buffer, just like when using
3021VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3022*/
3023VkResult vmaCreateBuffer(
3024 VmaAllocator allocator,
3025 const VkBufferCreateInfo* pBufferCreateInfo,
3026 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3027 VkBuffer* pBuffer,
3028 VmaAllocation* pAllocation,
3029 VmaAllocationInfo* pAllocationInfo);
3030
3031/** \brief Destroys Vulkan buffer and frees allocated memory.
3032
3033This is just a convenience function equivalent to:
3034
3035\code
3036vkDestroyBuffer(device, buffer, allocationCallbacks);
3037vmaFreeMemory(allocator, allocation);
3038\endcode
3039
3040It it safe to pass null as buffer and/or allocation.
3041*/
3042void vmaDestroyBuffer(
3043 VmaAllocator allocator,
3044 VkBuffer buffer,
3045 VmaAllocation allocation);
3046
3047/// Function similar to vmaCreateBuffer().
3048VkResult vmaCreateImage(
3049 VmaAllocator allocator,
3050 const VkImageCreateInfo* pImageCreateInfo,
3051 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3052 VkImage* pImage,
3053 VmaAllocation* pAllocation,
3054 VmaAllocationInfo* pAllocationInfo);
3055
3056/** \brief Destroys Vulkan image and frees allocated memory.
3057
3058This is just a convenience function equivalent to:
3059
3060\code
3061vkDestroyImage(device, image, allocationCallbacks);
3062vmaFreeMemory(allocator, allocation);
3063\endcode
3064
3065It it safe to pass null as image and/or allocation.
3066*/
3067void vmaDestroyImage(
3068 VmaAllocator allocator,
3069 VkImage image,
3070 VmaAllocation allocation);
3071
3072#ifdef __cplusplus
3073}
3074#endif
3075
3076#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3077
3078// For Visual Studio IntelliSense.
3079#if defined(__cplusplus) && defined(__INTELLISENSE__)
3080#define VMA_IMPLEMENTATION
3081#endif
3082
3083#ifdef VMA_IMPLEMENTATION
3084#undef VMA_IMPLEMENTATION
3085
3086#include <cstdint>
3087#include <cstdlib>
3088#include <cstring>
3089
3090/*******************************************************************************
3091CONFIGURATION SECTION
3092
3093Define some of these macros before each #include of this header or change them
3094here if you need other then default behavior depending on your environment.
3095*/
3096
3097/*
3098Define this macro to 1 to make the library fetch pointers to Vulkan functions
3099internally, like:
3100
3101 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3102
3103Define to 0 if you are going to provide you own pointers to Vulkan functions via
3104VmaAllocatorCreateInfo::pVulkanFunctions.
3105*/
3106#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3107#define VMA_STATIC_VULKAN_FUNCTIONS 1
3108#endif
3109
3110// Define this macro to 1 to make the library use STL containers instead of its own implementation.
3111//#define VMA_USE_STL_CONTAINERS 1
3112
3113/* Set this macro to 1 to make the library including and using STL containers:
3114std::pair, std::vector, std::list, std::unordered_map.
3115
3116Set it to 0 or undefined to make the library using its own implementation of
3117the containers.
3118*/
3119#if VMA_USE_STL_CONTAINERS
3120 #define VMA_USE_STL_VECTOR 1
3121 #define VMA_USE_STL_UNORDERED_MAP 1
3122 #define VMA_USE_STL_LIST 1
3123#endif
3124
3125#ifndef VMA_USE_STL_SHARED_MUTEX
3126 // Minimum Visual Studio 2015 Update 2
3127 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
3128 #define VMA_USE_STL_SHARED_MUTEX 1
3129 #endif
3130#endif
3131
3132#if VMA_USE_STL_VECTOR
3133 #include <vector>
3134#endif
3135
3136#if VMA_USE_STL_UNORDERED_MAP
3137 #include <unordered_map>
3138#endif
3139
3140#if VMA_USE_STL_LIST
3141 #include <list>
3142#endif
3143
3144/*
3145Following headers are used in this CONFIGURATION section only, so feel free to
3146remove them if not needed.
3147*/
3148#include <cassert> // for assert
3149#include <algorithm> // for min, max
3150#include <mutex>
3151#include <atomic> // for std::atomic
3152
3153#ifndef VMA_NULL
3154 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3155 #define VMA_NULL nullptr
3156#endif
3157
3158#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3159#include <cstdlib>
3160void *aligned_alloc(size_t alignment, size_t size)
3161{
3162 // alignment must be >= sizeof(void*)
3163 if(alignment < sizeof(void*))
3164 {
3165 alignment = sizeof(void*);
3166 }
3167
3168 return memalign(alignment, size);
3169}
3170#elif defined(__APPLE__) || defined(__ANDROID__)
3171#include <cstdlib>
3172void *aligned_alloc(size_t alignment, size_t size)
3173{
3174 // alignment must be >= sizeof(void*)
3175 if(alignment < sizeof(void*))
3176 {
3177 alignment = sizeof(void*);
3178 }
3179
3180 void *pointer;
3181 if(posix_memalign(&pointer, alignment, size) == 0)
3182 return pointer;
3183 return VMA_NULL;
3184}
3185#endif
3186
3187// If your compiler is not compatible with C++11 and definition of
3188// aligned_alloc() function is missing, uncommeting following line may help:
3189
3190//#include <malloc.h>
3191
3192// Normal assert to check for programmer's errors, especially in Debug configuration.
3193#ifndef VMA_ASSERT
3194 #ifdef _DEBUG
3195 #define VMA_ASSERT(expr) assert(expr)
3196 #else
3197 #define VMA_ASSERT(expr)
3198 #endif
3199#endif
3200
3201// Assert that will be called very often, like inside data structures e.g. operator[].
3202// Making it non-empty can make program slow.
3203#ifndef VMA_HEAVY_ASSERT
3204 #ifdef _DEBUG
3205 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3206 #else
3207 #define VMA_HEAVY_ASSERT(expr)
3208 #endif
3209#endif
3210
3211#ifndef VMA_ALIGN_OF
3212 #define VMA_ALIGN_OF(type) (__alignof(type))
3213#endif
3214
3215#ifndef VMA_SYSTEM_ALIGNED_MALLOC
3216 #if defined(_WIN32)
3217 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3218 #else
3219 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3220 #endif
3221#endif
3222
3223#ifndef VMA_SYSTEM_FREE
3224 #if defined(_WIN32)
3225 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
3226 #else
3227 #define VMA_SYSTEM_FREE(ptr) free(ptr)
3228 #endif
3229#endif
3230
3231#ifndef VMA_MIN
3232 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
3233#endif
3234
3235#ifndef VMA_MAX
3236 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
3237#endif
3238
3239#ifndef VMA_SWAP
3240 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
3241#endif
3242
3243#ifndef VMA_SORT
3244 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3245#endif
3246
3247#ifndef VMA_DEBUG_LOG
3248 #define VMA_DEBUG_LOG(format, ...)
3249 /*
3250 #define VMA_DEBUG_LOG(format, ...) do { \
3251 printf(format, __VA_ARGS__); \
3252 printf("\n"); \
3253 } while(false)
3254 */
3255#endif
3256
3257// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3258#if VMA_STATS_STRING_ENABLED
3259 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3260 {
3261 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3262 }
3263 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3264 {
3265 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3266 }
3267 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3268 {
3269 snprintf(outStr, strLen, "%p", ptr);
3270 }
3271#endif
3272
3273#ifndef VMA_MUTEX
3274 class VmaMutex
3275 {
3276 public:
3277 void Lock() { m_Mutex.lock(); }
3278 void Unlock() { m_Mutex.unlock(); }
3279 private:
3280 std::mutex m_Mutex;
3281 };
3282 #define VMA_MUTEX VmaMutex
3283#endif
3284
3285// Read-write mutex, where "read" is shared access, "write" is exclusive access.
3286#ifndef VMA_RW_MUTEX
3287 #if VMA_USE_STL_SHARED_MUTEX
3288 // Use std::shared_mutex from C++17.
3289 #include <shared_mutex>
3290 class VmaRWMutex
3291 {
3292 public:
3293 void LockRead() { m_Mutex.lock_shared(); }
3294 void UnlockRead() { m_Mutex.unlock_shared(); }
3295 void LockWrite() { m_Mutex.lock(); }
3296 void UnlockWrite() { m_Mutex.unlock(); }
3297 private:
3298 std::shared_mutex m_Mutex;
3299 };
3300 #define VMA_RW_MUTEX VmaRWMutex
3301 #elif defined(_WIN32)
3302 // Use SRWLOCK from WinAPI.
3303 class VmaRWMutex
3304 {
3305 public:
3306 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
3307 void LockRead() { AcquireSRWLockShared(&m_Lock); }
3308 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
3309 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
3310 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3311 private:
3312 SRWLOCK m_Lock;
3313 };
3314 #define VMA_RW_MUTEX VmaRWMutex
3315 #else
3316 // Less efficient fallback: Use normal mutex.
3317 class VmaRWMutex
3318 {
3319 public:
3320 void LockRead() { m_Mutex.Lock(); }
3321 void UnlockRead() { m_Mutex.Unlock(); }
3322 void LockWrite() { m_Mutex.Lock(); }
3323 void UnlockWrite() { m_Mutex.Unlock(); }
3324 private:
3325 VMA_MUTEX m_Mutex;
3326 };
3327 #define VMA_RW_MUTEX VmaRWMutex
3328 #endif // #if VMA_USE_STL_SHARED_MUTEX
3329#endif // #ifndef VMA_RW_MUTEX
3330
3331/*
3332If providing your own implementation, you need to implement a subset of std::atomic:
3333
3334- Constructor(uint32_t desired)
3335- uint32_t load() const
3336- void store(uint32_t desired)
3337- bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3338*/
3339#ifndef VMA_ATOMIC_UINT32
3340 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3341#endif
3342
3343#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3344 /**
3345 Every allocation will have its own memory block.
3346 Define to 1 for debugging purposes only.
3347 */
3348 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3349#endif
3350
3351#ifndef VMA_DEBUG_ALIGNMENT
3352 /**
3353 Minimum alignment of all allocations, in bytes.
3354 Set to more than 1 for debugging purposes only. Must be power of two.
3355 */
3356 #define VMA_DEBUG_ALIGNMENT (1)
3357#endif
3358
3359#ifndef VMA_DEBUG_MARGIN
3360 /**
3361 Minimum margin before and after every allocation, in bytes.
3362 Set nonzero for debugging purposes only.
3363 */
3364 #define VMA_DEBUG_MARGIN (0)
3365#endif
3366
3367#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3368 /**
3369 Define this macro to 1 to automatically fill new allocations and destroyed
3370 allocations with some bit pattern.
3371 */
3372 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3373#endif
3374
3375#ifndef VMA_DEBUG_DETECT_CORRUPTION
3376 /**
3377 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3378 enable writing magic value to the margin before and after every allocation and
3379 validating it, so that memory corruptions (out-of-bounds writes) are detected.
3380 */
3381 #define VMA_DEBUG_DETECT_CORRUPTION (0)
3382#endif
3383
3384#ifndef VMA_DEBUG_GLOBAL_MUTEX
3385 /**
3386 Set this to 1 for debugging purposes only, to enable single mutex protecting all
3387 entry calls to the library. Can be useful for debugging multithreading issues.
3388 */
3389 #define VMA_DEBUG_GLOBAL_MUTEX (0)
3390#endif
3391
3392#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3393 /**
3394 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3395 Set to more than 1 for debugging purposes only. Must be power of two.
3396 */
3397 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3398#endif
3399
3400#ifndef VMA_SMALL_HEAP_MAX_SIZE
3401 /// Maximum size of a memory heap in Vulkan to consider it "small".
3402 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3403#endif
3404
3405#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3406 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3407 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3408#endif
3409
3410#ifndef VMA_CLASS_NO_COPY
3411 #define VMA_CLASS_NO_COPY(className) \
3412 private: \
3413 className(const className&) = delete; \
3414 className& operator=(const className&) = delete;
3415#endif
3416
3417static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3418
3419// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3420static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3421
3422static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3423static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3424
3425/*******************************************************************************
3426END OF CONFIGURATION
3427*/
3428
3429static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3430
3431static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3432 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3433
3434// Returns number of bits set to 1 in (v).
3435static inline uint32_t VmaCountBitsSet(uint32_t v)
3436{
3437 uint32_t c = v - ((v >> 1) & 0x55555555);
3438 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3439 c = ((c >> 4) + c) & 0x0F0F0F0F;
3440 c = ((c >> 8) + c) & 0x00FF00FF;
3441 c = ((c >> 16) + c) & 0x0000FFFF;
3442 return c;
3443}
3444
3445// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3446// Use types like uint32_t, uint64_t as T.
3447template <typename T>
3448static inline T VmaAlignUp(T val, T align)
3449{
3450 return (val + align - 1) / align * align;
3451}
3452// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3453// Use types like uint32_t, uint64_t as T.
3454template <typename T>
3455static inline T VmaAlignDown(T val, T align)
3456{
3457 return val / align * align;
3458}
3459
3460// Division with mathematical rounding to nearest number.
3461template <typename T>
3462static inline T VmaRoundDiv(T x, T y)
3463{
3464 return (x + (y / (T)2)) / y;
3465}
3466
3467/*
3468Returns true if given number is a power of two.
3469T must be unsigned integer number or signed integer but always nonnegative.
3470For 0 returns true.
3471*/
3472template <typename T>
3473inline bool VmaIsPow2(T x)
3474{
3475 return (x & (x-1)) == 0;
3476}
3477
3478// Returns smallest power of 2 greater or equal to v.
3479static inline uint32_t VmaNextPow2(uint32_t v)
3480{
3481 v--;
3482 v |= v >> 1;
3483 v |= v >> 2;
3484 v |= v >> 4;
3485 v |= v >> 8;
3486 v |= v >> 16;
3487 v++;
3488 return v;
3489}
3490static inline uint64_t VmaNextPow2(uint64_t v)
3491{
3492 v--;
3493 v |= v >> 1;
3494 v |= v >> 2;
3495 v |= v >> 4;
3496 v |= v >> 8;
3497 v |= v >> 16;
3498 v |= v >> 32;
3499 v++;
3500 return v;
3501}
3502
3503// Returns largest power of 2 less or equal to v.
3504static inline uint32_t VmaPrevPow2(uint32_t v)
3505{
3506 v |= v >> 1;
3507 v |= v >> 2;
3508 v |= v >> 4;
3509 v |= v >> 8;
3510 v |= v >> 16;
3511 v = v ^ (v >> 1);
3512 return v;
3513}
3514static inline uint64_t VmaPrevPow2(uint64_t v)
3515{
3516 v |= v >> 1;
3517 v |= v >> 2;
3518 v |= v >> 4;
3519 v |= v >> 8;
3520 v |= v >> 16;
3521 v |= v >> 32;
3522 v = v ^ (v >> 1);
3523 return v;
3524}
3525
3526static inline bool VmaStrIsEmpty(const char* pStr)
3527{
3528 return pStr == VMA_NULL || *pStr == '\0';
3529}
3530
3531static const char* VmaAlgorithmToStr(uint32_t algorithm)
3532{
3533 switch(algorithm)
3534 {
3535 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3536 return "Linear";
3537 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3538 return "Buddy";
3539 case 0:
3540 return "Default";
3541 default:
3542 VMA_ASSERT(0);
3543 return "";
3544 }
3545}
3546
3547#ifndef VMA_SORT
3548
3549template<typename Iterator, typename Compare>
3550Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3551{
3552 Iterator centerValue = end; --centerValue;
3553 Iterator insertIndex = beg;
3554 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3555 {
3556 if(cmp(*memTypeIndex, *centerValue))
3557 {
3558 if(insertIndex != memTypeIndex)
3559 {
3560 VMA_SWAP(*memTypeIndex, *insertIndex);
3561 }
3562 ++insertIndex;
3563 }
3564 }
3565 if(insertIndex != centerValue)
3566 {
3567 VMA_SWAP(*insertIndex, *centerValue);
3568 }
3569 return insertIndex;
3570}
3571
3572template<typename Iterator, typename Compare>
3573void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3574{
3575 if(beg < end)
3576 {
3577 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3578 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3579 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3580 }
3581}
3582
3583#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3584
3585#endif // #ifndef VMA_SORT
3586
3587/*
3588Returns true if two memory blocks occupy overlapping pages.
3589ResourceA must be in less memory offset than ResourceB.
3590
3591Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3592chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3593*/
3594static inline bool VmaBlocksOnSamePage(
3595 VkDeviceSize resourceAOffset,
3596 VkDeviceSize resourceASize,
3597 VkDeviceSize resourceBOffset,
3598 VkDeviceSize pageSize)
3599{
3600 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3601 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3602 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3603 VkDeviceSize resourceBStart = resourceBOffset;
3604 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3605 return resourceAEndPage == resourceBStartPage;
3606}
3607
3608enum VmaSuballocationType
3609{
3610 VMA_SUBALLOCATION_TYPE_FREE = 0,
3611 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3612 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3613 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3614 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3615 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3616 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3617};
3618
3619/*
3620Returns true if given suballocation types could conflict and must respect
3621VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3622or linear image and another one is optimal image. If type is unknown, behave
3623conservatively.
3624*/
3625static inline bool VmaIsBufferImageGranularityConflict(
3626 VmaSuballocationType suballocType1,
3627 VmaSuballocationType suballocType2)
3628{
3629 if(suballocType1 > suballocType2)
3630 {
3631 VMA_SWAP(suballocType1, suballocType2);
3632 }
3633
3634 switch(suballocType1)
3635 {
3636 case VMA_SUBALLOCATION_TYPE_FREE:
3637 return false;
3638 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3639 return true;
3640 case VMA_SUBALLOCATION_TYPE_BUFFER:
3641 return
3642 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3643 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3644 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3645 return
3646 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3647 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3648 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3649 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3650 return
3651 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3652 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3653 return false;
3654 default:
3655 VMA_ASSERT(0);
3656 return true;
3657 }
3658}
3659
3660static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3661{
3662 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3663 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3664 for(size_t i = 0; i < numberCount; ++i, ++pDst)
3665 {
3666 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3667 }
3668}
3669
3670static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3671{
3672 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3673 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3674 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3675 {
3676 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3677 {
3678 return false;
3679 }
3680 }
3681 return true;
3682}
3683
3684// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3685struct VmaMutexLock
3686{
3687 VMA_CLASS_NO_COPY(VmaMutexLock)
3688public:
3689 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3690 m_pMutex(useMutex ? &mutex : VMA_NULL)
3691 { if(m_pMutex) { m_pMutex->Lock(); } }
3692 ~VmaMutexLock()
3693 { if(m_pMutex) { m_pMutex->Unlock(); } }
3694private:
3695 VMA_MUTEX* m_pMutex;
3696};
3697
3698// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3699struct VmaMutexLockRead
3700{
3701 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3702public:
3703 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3704 m_pMutex(useMutex ? &mutex : VMA_NULL)
3705 { if(m_pMutex) { m_pMutex->LockRead(); } }
3706 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3707private:
3708 VMA_RW_MUTEX* m_pMutex;
3709};
3710
3711// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3712struct VmaMutexLockWrite
3713{
3714 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3715public:
3716 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3717 m_pMutex(useMutex ? &mutex : VMA_NULL)
3718 { if(m_pMutex) { m_pMutex->LockWrite(); } }
3719 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3720private:
3721 VMA_RW_MUTEX* m_pMutex;
3722};
3723
3724#if VMA_DEBUG_GLOBAL_MUTEX
3725 static VMA_MUTEX gDebugGlobalMutex;
3726 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3727#else
3728 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3729#endif
3730
3731// Minimum size of a free suballocation to register it in the free suballocation collection.
3732static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3733
3734/*
3735Performs binary search and returns iterator to first element that is greater or
3736equal to (key), according to comparison (cmp).
3737
3738Cmp should return true if first argument is less than second argument.
3739
3740Returned value is the found element, if present in the collection or place where
3741new element with value (key) should be inserted.
3742*/
3743template <typename CmpLess, typename IterT, typename KeyT>
3744static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3745{
3746 size_t down = 0, up = (end - beg);
3747 while(down < up)
3748 {
3749 const size_t mid = (down + up) / 2;
3750 if(cmp(*(beg+mid), key))
3751 {
3752 down = mid + 1;
3753 }
3754 else
3755 {
3756 up = mid;
3757 }
3758 }
3759 return beg + down;
3760}
3761
3762/*
3763Returns true if all pointers in the array are not-null and unique.
3764Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3765T must be pointer type, e.g. VmaAllocation, VmaPool.
3766*/
3767template<typename T>
3768static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3769{
3770 for(uint32_t i = 0; i < count; ++i)
3771 {
3772 const T iPtr = arr[i];
3773 if(iPtr == VMA_NULL)
3774 {
3775 return false;
3776 }
3777 for(uint32_t j = i + 1; j < count; ++j)
3778 {
3779 if(iPtr == arr[j])
3780 {
3781 return false;
3782 }
3783 }
3784 }
3785 return true;
3786}
3787
3788////////////////////////////////////////////////////////////////////////////////
3789// Memory allocation
3790
3791static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3792{
3793 if((pAllocationCallbacks != VMA_NULL) &&
3794 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3795 {
3796 return (*pAllocationCallbacks->pfnAllocation)(
3797 pAllocationCallbacks->pUserData,
3798 size,
3799 alignment,
3800 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3801 }
3802 else
3803 {
3804 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3805 }
3806}
3807
3808static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3809{
3810 if((pAllocationCallbacks != VMA_NULL) &&
3811 (pAllocationCallbacks->pfnFree != VMA_NULL))
3812 {
3813 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3814 }
3815 else
3816 {
3817 VMA_SYSTEM_FREE(ptr);
3818 }
3819}
3820
3821template<typename T>
3822static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3823{
3824 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3825}
3826
3827template<typename T>
3828static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3829{
3830 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3831}
3832
3833#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3834
3835#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3836
3837template<typename T>
3838static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3839{
3840 ptr->~T();
3841 VmaFree(pAllocationCallbacks, ptr);
3842}
3843
3844template<typename T>
3845static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3846{
3847 if(ptr != VMA_NULL)
3848 {
3849 for(size_t i = count; i--; )
3850 {
3851 ptr[i].~T();
3852 }
3853 VmaFree(pAllocationCallbacks, ptr);
3854 }
3855}
3856
3857// STL-compatible allocator.
3858template<typename T>
3859class VmaStlAllocator
3860{
3861public:
3862 const VkAllocationCallbacks* const m_pCallbacks;
3863 typedef T value_type;
3864
3865 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
3866 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3867
3868 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
3869 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3870
3871 template<typename U>
3872 bool operator==(const VmaStlAllocator<U>& rhs) const
3873 {
3874 return m_pCallbacks == rhs.m_pCallbacks;
3875 }
3876 template<typename U>
3877 bool operator!=(const VmaStlAllocator<U>& rhs) const
3878 {
3879 return m_pCallbacks != rhs.m_pCallbacks;
3880 }
3881
3882 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3883};
3884
3885#if VMA_USE_STL_VECTOR
3886
3887#define VmaVector std::vector
3888
3889template<typename T, typename allocatorT>
3890static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3891{
3892 vec.insert(vec.begin() + index, item);
3893}
3894
3895template<typename T, typename allocatorT>
3896static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3897{
3898 vec.erase(vec.begin() + index);
3899}
3900
3901#else // #if VMA_USE_STL_VECTOR
3902
3903/* Class with interface compatible with subset of std::vector.
3904T must be POD because constructors and destructors are not called and memcpy is
3905used for these objects. */
3906template<typename T, typename AllocatorT>
3907class VmaVector
3908{
3909public:
3910 typedef T value_type;
3911
3912 VmaVector(const AllocatorT& allocator) :
3913 m_Allocator(allocator),
3914 m_pArray(VMA_NULL),
3915 m_Count(0),
3916 m_Capacity(0)
3917 {
3918 }
3919
3920 VmaVector(size_t count, const AllocatorT& allocator) :
3921 m_Allocator(allocator),
3922 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3923 m_Count(count),
3924 m_Capacity(count)
3925 {
3926 }
3927
3928 VmaVector(const VmaVector<T, AllocatorT>& src) :
3929 m_Allocator(src.m_Allocator),
3930 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3931 m_Count(src.m_Count),
3932 m_Capacity(src.m_Count)
3933 {
3934 if(m_Count != 0)
3935 {
3936 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3937 }
3938 }
3939
3940 ~VmaVector()
3941 {
3942 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3943 }
3944
3945 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3946 {
3947 if(&rhs != this)
3948 {
3949 resize(rhs.m_Count);
3950 if(m_Count != 0)
3951 {
3952 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3953 }
3954 }
3955 return *this;
3956 }
3957
3958 bool empty() const { return m_Count == 0; }
3959 size_t size() const { return m_Count; }
3960 T* data() { return m_pArray; }
3961 const T* data() const { return m_pArray; }
3962
3963 T& operator[](size_t index)
3964 {
3965 VMA_HEAVY_ASSERT(index < m_Count);
3966 return m_pArray[index];
3967 }
3968 const T& operator[](size_t index) const
3969 {
3970 VMA_HEAVY_ASSERT(index < m_Count);
3971 return m_pArray[index];
3972 }
3973
3974 T& front()
3975 {
3976 VMA_HEAVY_ASSERT(m_Count > 0);
3977 return m_pArray[0];
3978 }
3979 const T& front() const
3980 {
3981 VMA_HEAVY_ASSERT(m_Count > 0);
3982 return m_pArray[0];
3983 }
3984 T& back()
3985 {
3986 VMA_HEAVY_ASSERT(m_Count > 0);
3987 return m_pArray[m_Count - 1];
3988 }
3989 const T& back() const
3990 {
3991 VMA_HEAVY_ASSERT(m_Count > 0);
3992 return m_pArray[m_Count - 1];
3993 }
3994
3995 void reserve(size_t newCapacity, bool freeMemory = false)
3996 {
3997 newCapacity = VMA_MAX(newCapacity, m_Count);
3998
3999 if((newCapacity < m_Capacity) && !freeMemory)
4000 {
4001 newCapacity = m_Capacity;
4002 }
4003
4004 if(newCapacity != m_Capacity)
4005 {
4006 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4007 if(m_Count != 0)
4008 {
4009 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4010 }
4011 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4012 m_Capacity = newCapacity;
4013 m_pArray = newArray;
4014 }
4015 }
4016
4017 void resize(size_t newCount, bool freeMemory = false)
4018 {
4019 size_t newCapacity = m_Capacity;
4020 if(newCount > m_Capacity)
4021 {
4022 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4023 }
4024 else if(freeMemory)
4025 {
4026 newCapacity = newCount;
4027 }
4028
4029 if(newCapacity != m_Capacity)
4030 {
4031 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4032 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4033 if(elementsToCopy != 0)
4034 {
4035 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4036 }
4037 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4038 m_Capacity = newCapacity;
4039 m_pArray = newArray;
4040 }
4041
4042 m_Count = newCount;
4043 }
4044
4045 void clear(bool freeMemory = false)
4046 {
4047 resize(0, freeMemory);
4048 }
4049
4050 void insert(size_t index, const T& src)
4051 {
4052 VMA_HEAVY_ASSERT(index <= m_Count);
4053 const size_t oldCount = size();
4054 resize(oldCount + 1);
4055 if(index < oldCount)
4056 {
4057 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4058 }
4059 m_pArray[index] = src;
4060 }
4061
4062 void remove(size_t index)
4063 {
4064 VMA_HEAVY_ASSERT(index < m_Count);
4065 const size_t oldCount = size();
4066 if(index < oldCount - 1)
4067 {
4068 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4069 }
4070 resize(oldCount - 1);
4071 }
4072
4073 void push_back(const T& src)
4074 {
4075 const size_t newIndex = size();
4076 resize(newIndex + 1);
4077 m_pArray[newIndex] = src;
4078 }
4079
4080 void pop_back()
4081 {
4082 VMA_HEAVY_ASSERT(m_Count > 0);
4083 resize(size() - 1);
4084 }
4085
4086 void push_front(const T& src)
4087 {
4088 insert(0, src);
4089 }
4090
4091 void pop_front()
4092 {
4093 VMA_HEAVY_ASSERT(m_Count > 0);
4094 remove(0);
4095 }
4096
4097 typedef T* iterator;
4098
4099 iterator begin() { return m_pArray; }
4100 iterator end() { return m_pArray + m_Count; }
4101
4102private:
4103 AllocatorT m_Allocator;
4104 T* m_pArray;
4105 size_t m_Count;
4106 size_t m_Capacity;
4107};
4108
4109template<typename T, typename allocatorT>
4110static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4111{
4112 vec.insert(index, item);
4113}
4114
4115template<typename T, typename allocatorT>
4116static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4117{
4118 vec.remove(index);
4119}
4120
4121#endif // #if VMA_USE_STL_VECTOR
4122
4123template<typename CmpLess, typename VectorT>
4124size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4125{
4126 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4127 vector.data(),
4128 vector.data() + vector.size(),
4129 value,
4130 CmpLess()) - vector.data();
4131 VmaVectorInsert(vector, indexToInsert, value);
4132 return indexToInsert;
4133}
4134
4135template<typename CmpLess, typename VectorT>
4136bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4137{
4138 CmpLess comparator;
4139 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4140 vector.begin(),
4141 vector.end(),
4142 value,
4143 comparator);
4144 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4145 {
4146 size_t indexToRemove = it - vector.begin();
4147 VmaVectorRemove(vector, indexToRemove);
4148 return true;
4149 }
4150 return false;
4151}
4152
4153template<typename CmpLess, typename IterT, typename KeyT>
4154IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4155{
4156 CmpLess comparator;
4157 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4158 beg, end, value, comparator);
4159 if(it == end ||
4160 (!comparator(*it, value) && !comparator(value, *it)))
4161 {
4162 return it;
4163 }
4164 return end;
4165}
4166
4167////////////////////////////////////////////////////////////////////////////////
4168// class VmaPoolAllocator
4169
4170/*
4171Allocator for objects of type T using a list of arrays (pools) to speed up
4172allocation. Number of elements that can be allocated is not bounded because
4173allocator can create multiple blocks.
4174*/
4175template<typename T>
4176class VmaPoolAllocator
4177{
4178 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4179public:
4180 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4181 ~VmaPoolAllocator();
4182 void Clear();
4183 T* Alloc();
4184 void Free(T* ptr);
4185
4186private:
4187 union Item
4188 {
4189 uint32_t NextFreeIndex;
4190 T Value;
4191 };
4192
4193 struct ItemBlock
4194 {
4195 Item* pItems;
4196 uint32_t FirstFreeIndex;
4197 };
4198
4199 const VkAllocationCallbacks* m_pAllocationCallbacks;
4200 size_t m_ItemsPerBlock;
4201 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4202
4203 ItemBlock& CreateNewBlock();
4204};
4205
4206template<typename T>
4207VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4208 m_pAllocationCallbacks(pAllocationCallbacks),
4209 m_ItemsPerBlock(itemsPerBlock),
4210 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4211{
4212 VMA_ASSERT(itemsPerBlock > 0);
4213}
4214
4215template<typename T>
4216VmaPoolAllocator<T>::~VmaPoolAllocator()
4217{
4218 Clear();
4219}
4220
4221template<typename T>
4222void VmaPoolAllocator<T>::Clear()
4223{
4224 for(size_t i = m_ItemBlocks.size(); i--; )
4225 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4226 m_ItemBlocks.clear();
4227}
4228
4229template<typename T>
4230T* VmaPoolAllocator<T>::Alloc()
4231{
4232 for(size_t i = m_ItemBlocks.size(); i--; )
4233 {
4234 ItemBlock& block = m_ItemBlocks[i];
4235 // This block has some free items: Use first one.
4236 if(block.FirstFreeIndex != UINT32_MAX)
4237 {
4238 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4239 block.FirstFreeIndex = pItem->NextFreeIndex;
4240 return &pItem->Value;
4241 }
4242 }
4243
4244 // No block has free item: Create new one and use it.
4245 ItemBlock& newBlock = CreateNewBlock();
4246 Item* const pItem = &newBlock.pItems[0];
4247 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4248 return &pItem->Value;
4249}
4250
4251template<typename T>
4252void VmaPoolAllocator<T>::Free(T* ptr)
4253{
4254 // Search all memory blocks to find ptr.
4255 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4256 {
4257 ItemBlock& block = m_ItemBlocks[i];
4258
4259 // Casting to union.
4260 Item* pItemPtr;
4261 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4262
4263 // Check if pItemPtr is in address range of this block.
4264 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4265 {
4266 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4267 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4268 block.FirstFreeIndex = index;
4269 return;
4270 }
4271 }
4272 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4273}
4274
4275template<typename T>
4276typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4277{
4278 ItemBlock newBlock = {
4279 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4280
4281 m_ItemBlocks.push_back(newBlock);
4282
4283 // Setup singly-linked list of all free items in this block.
4284 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4285 newBlock.pItems[i].NextFreeIndex = i + 1;
4286 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4287 return m_ItemBlocks.back();
4288}
4289
4290////////////////////////////////////////////////////////////////////////////////
4291// class VmaRawList, VmaList
4292
4293#if VMA_USE_STL_LIST
4294
4295#define VmaList std::list
4296
4297#else // #if VMA_USE_STL_LIST
4298
4299template<typename T>
4300struct VmaListItem
4301{
4302 VmaListItem* pPrev;
4303 VmaListItem* pNext;
4304 T Value;
4305};
4306
4307// Doubly linked list.
4308template<typename T>
4309class VmaRawList
4310{
4311 VMA_CLASS_NO_COPY(VmaRawList)
4312public:
4313 typedef VmaListItem<T> ItemType;
4314
4315 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4316 ~VmaRawList();
4317 void Clear();
4318
4319 size_t GetCount() const { return m_Count; }
4320 bool IsEmpty() const { return m_Count == 0; }
4321
4322 ItemType* Front() { return m_pFront; }
4323 const ItemType* Front() const { return m_pFront; }
4324 ItemType* Back() { return m_pBack; }
4325 const ItemType* Back() const { return m_pBack; }
4326
4327 ItemType* PushBack();
4328 ItemType* PushFront();
4329 ItemType* PushBack(const T& value);
4330 ItemType* PushFront(const T& value);
4331 void PopBack();
4332 void PopFront();
4333
4334 // Item can be null - it means PushBack.
4335 ItemType* InsertBefore(ItemType* pItem);
4336 // Item can be null - it means PushFront.
4337 ItemType* InsertAfter(ItemType* pItem);
4338
4339 ItemType* InsertBefore(ItemType* pItem, const T& value);
4340 ItemType* InsertAfter(ItemType* pItem, const T& value);
4341
4342 void Remove(ItemType* pItem);
4343
4344private:
4345 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4346 VmaPoolAllocator<ItemType> m_ItemAllocator;
4347 ItemType* m_pFront;
4348 ItemType* m_pBack;
4349 size_t m_Count;
4350};
4351
4352template<typename T>
4353VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4354 m_pAllocationCallbacks(pAllocationCallbacks),
4355 m_ItemAllocator(pAllocationCallbacks, 128),
4356 m_pFront(VMA_NULL),
4357 m_pBack(VMA_NULL),
4358 m_Count(0)
4359{
4360}
4361
4362template<typename T>
4363VmaRawList<T>::~VmaRawList()
4364{
4365 // Intentionally not calling Clear, because that would be unnecessary
4366 // computations to return all items to m_ItemAllocator as free.
4367}
4368
4369template<typename T>
4370void VmaRawList<T>::Clear()
4371{
4372 if(IsEmpty() == false)
4373 {
4374 ItemType* pItem = m_pBack;
4375 while(pItem != VMA_NULL)
4376 {
4377 ItemType* const pPrevItem = pItem->pPrev;
4378 m_ItemAllocator.Free(pItem);
4379 pItem = pPrevItem;
4380 }
4381 m_pFront = VMA_NULL;
4382 m_pBack = VMA_NULL;
4383 m_Count = 0;
4384 }
4385}
4386
4387template<typename T>
4388VmaListItem<T>* VmaRawList<T>::PushBack()
4389{
4390 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4391 pNewItem->pNext = VMA_NULL;
4392 if(IsEmpty())
4393 {
4394 pNewItem->pPrev = VMA_NULL;
4395 m_pFront = pNewItem;
4396 m_pBack = pNewItem;
4397 m_Count = 1;
4398 }
4399 else
4400 {
4401 pNewItem->pPrev = m_pBack;
4402 m_pBack->pNext = pNewItem;
4403 m_pBack = pNewItem;
4404 ++m_Count;
4405 }
4406 return pNewItem;
4407}
4408
4409template<typename T>
4410VmaListItem<T>* VmaRawList<T>::PushFront()
4411{
4412 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4413 pNewItem->pPrev = VMA_NULL;
4414 if(IsEmpty())
4415 {
4416 pNewItem->pNext = VMA_NULL;
4417 m_pFront = pNewItem;
4418 m_pBack = pNewItem;
4419 m_Count = 1;
4420 }
4421 else
4422 {
4423 pNewItem->pNext = m_pFront;
4424 m_pFront->pPrev = pNewItem;
4425 m_pFront = pNewItem;
4426 ++m_Count;
4427 }
4428 return pNewItem;
4429}
4430
4431template<typename T>
4432VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4433{
4434 ItemType* const pNewItem = PushBack();
4435 pNewItem->Value = value;
4436 return pNewItem;
4437}
4438
4439template<typename T>
4440VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4441{
4442 ItemType* const pNewItem = PushFront();
4443 pNewItem->Value = value;
4444 return pNewItem;
4445}
4446
4447template<typename T>
4448void VmaRawList<T>::PopBack()
4449{
4450 VMA_HEAVY_ASSERT(m_Count > 0);
4451 ItemType* const pBackItem = m_pBack;
4452 ItemType* const pPrevItem = pBackItem->pPrev;
4453 if(pPrevItem != VMA_NULL)
4454 {
4455 pPrevItem->pNext = VMA_NULL;
4456 }
4457 m_pBack = pPrevItem;
4458 m_ItemAllocator.Free(pBackItem);
4459 --m_Count;
4460}
4461
4462template<typename T>
4463void VmaRawList<T>::PopFront()
4464{
4465 VMA_HEAVY_ASSERT(m_Count > 0);
4466 ItemType* const pFrontItem = m_pFront;
4467 ItemType* const pNextItem = pFrontItem->pNext;
4468 if(pNextItem != VMA_NULL)
4469 {
4470 pNextItem->pPrev = VMA_NULL;
4471 }
4472 m_pFront = pNextItem;
4473 m_ItemAllocator.Free(pFrontItem);
4474 --m_Count;
4475}
4476
4477template<typename T>
4478void VmaRawList<T>::Remove(ItemType* pItem)
4479{
4480 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4481 VMA_HEAVY_ASSERT(m_Count > 0);
4482
4483 if(pItem->pPrev != VMA_NULL)
4484 {
4485 pItem->pPrev->pNext = pItem->pNext;
4486 }
4487 else
4488 {
4489 VMA_HEAVY_ASSERT(m_pFront == pItem);
4490 m_pFront = pItem->pNext;
4491 }
4492
4493 if(pItem->pNext != VMA_NULL)
4494 {
4495 pItem->pNext->pPrev = pItem->pPrev;
4496 }
4497 else
4498 {
4499 VMA_HEAVY_ASSERT(m_pBack == pItem);
4500 m_pBack = pItem->pPrev;
4501 }
4502
4503 m_ItemAllocator.Free(pItem);
4504 --m_Count;
4505}
4506
4507template<typename T>
4508VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4509{
4510 if(pItem != VMA_NULL)
4511 {
4512 ItemType* const prevItem = pItem->pPrev;
4513 ItemType* const newItem = m_ItemAllocator.Alloc();
4514 newItem->pPrev = prevItem;
4515 newItem->pNext = pItem;
4516 pItem->pPrev = newItem;
4517 if(prevItem != VMA_NULL)
4518 {
4519 prevItem->pNext = newItem;
4520 }
4521 else
4522 {
4523 VMA_HEAVY_ASSERT(m_pFront == pItem);
4524 m_pFront = newItem;
4525 }
4526 ++m_Count;
4527 return newItem;
4528 }
4529 else
4530 return PushBack();
4531}
4532
4533template<typename T>
4534VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4535{
4536 if(pItem != VMA_NULL)
4537 {
4538 ItemType* const nextItem = pItem->pNext;
4539 ItemType* const newItem = m_ItemAllocator.Alloc();
4540 newItem->pNext = nextItem;
4541 newItem->pPrev = pItem;
4542 pItem->pNext = newItem;
4543 if(nextItem != VMA_NULL)
4544 {
4545 nextItem->pPrev = newItem;
4546 }
4547 else
4548 {
4549 VMA_HEAVY_ASSERT(m_pBack == pItem);
4550 m_pBack = newItem;
4551 }
4552 ++m_Count;
4553 return newItem;
4554 }
4555 else
4556 return PushFront();
4557}
4558
4559template<typename T>
4560VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4561{
4562 ItemType* const newItem = InsertBefore(pItem);
4563 newItem->Value = value;
4564 return newItem;
4565}
4566
4567template<typename T>
4568VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4569{
4570 ItemType* const newItem = InsertAfter(pItem);
4571 newItem->Value = value;
4572 return newItem;
4573}
4574
4575template<typename T, typename AllocatorT>
4576class VmaList
4577{
4578 VMA_CLASS_NO_COPY(VmaList)
4579public:
4580 class iterator
4581 {
4582 public:
4583 iterator() :
4584 m_pList(VMA_NULL),
4585 m_pItem(VMA_NULL)
4586 {
4587 }
4588
4589 T& operator*() const
4590 {
4591 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4592 return m_pItem->Value;
4593 }
4594 T* operator->() const
4595 {
4596 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4597 return &m_pItem->Value;
4598 }
4599
4600 iterator& operator++()
4601 {
4602 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4603 m_pItem = m_pItem->pNext;
4604 return *this;
4605 }
4606 iterator& operator--()
4607 {
4608 if(m_pItem != VMA_NULL)
4609 {
4610 m_pItem = m_pItem->pPrev;
4611 }
4612 else
4613 {
4614 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4615 m_pItem = m_pList->Back();
4616 }
4617 return *this;
4618 }
4619
4620 iterator operator++(int)
4621 {
4622 iterator result = *this;
4623 ++*this;
4624 return result;
4625 }
4626 iterator operator--(int)
4627 {
4628 iterator result = *this;
4629 --*this;
4630 return result;
4631 }
4632
4633 bool operator==(const iterator& rhs) const
4634 {
4635 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4636 return m_pItem == rhs.m_pItem;
4637 }
4638 bool operator!=(const iterator& rhs) const
4639 {
4640 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4641 return m_pItem != rhs.m_pItem;
4642 }
4643
4644 private:
4645 VmaRawList<T>* m_pList;
4646 VmaListItem<T>* m_pItem;
4647
4648 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4649 m_pList(pList),
4650 m_pItem(pItem)
4651 {
4652 }
4653
4654 friend class VmaList<T, AllocatorT>;
4655 };
4656
4657 class const_iterator
4658 {
4659 public:
4660 const_iterator() :
4661 m_pList(VMA_NULL),
4662 m_pItem(VMA_NULL)
4663 {
4664 }
4665
4666 const_iterator(const iterator& src) :
4667 m_pList(src.m_pList),
4668 m_pItem(src.m_pItem)
4669 {
4670 }
4671
4672 const T& operator*() const
4673 {
4674 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4675 return m_pItem->Value;
4676 }
4677 const T* operator->() const
4678 {
4679 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4680 return &m_pItem->Value;
4681 }
4682
4683 const_iterator& operator++()
4684 {
4685 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4686 m_pItem = m_pItem->pNext;
4687 return *this;
4688 }
4689 const_iterator& operator--()
4690 {
4691 if(m_pItem != VMA_NULL)
4692 {
4693 m_pItem = m_pItem->pPrev;
4694 }
4695 else
4696 {
4697 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4698 m_pItem = m_pList->Back();
4699 }
4700 return *this;
4701 }
4702
4703 const_iterator operator++(int)
4704 {
4705 const_iterator result = *this;
4706 ++*this;
4707 return result;
4708 }
4709 const_iterator operator--(int)
4710 {
4711 const_iterator result = *this;
4712 --*this;
4713 return result;
4714 }
4715
4716 bool operator==(const const_iterator& rhs) const
4717 {
4718 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4719 return m_pItem == rhs.m_pItem;
4720 }
4721 bool operator!=(const const_iterator& rhs) const
4722 {
4723 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4724 return m_pItem != rhs.m_pItem;
4725 }
4726
4727 private:
4728 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4729 m_pList(pList),
4730 m_pItem(pItem)
4731 {
4732 }
4733
4734 const VmaRawList<T>* m_pList;
4735 const VmaListItem<T>* m_pItem;
4736
4737 friend class VmaList<T, AllocatorT>;
4738 };
4739
4740 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4741
4742 bool empty() const { return m_RawList.IsEmpty(); }
4743 size_t size() const { return m_RawList.GetCount(); }
4744
4745 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
4746 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4747
4748 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
4749 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4750
4751 void clear() { m_RawList.Clear(); }
4752 void push_back(const T& value) { m_RawList.PushBack(value); }
4753 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4754 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4755
4756private:
4757 VmaRawList<T> m_RawList;
4758};
4759
4760#endif // #if VMA_USE_STL_LIST
4761
4762////////////////////////////////////////////////////////////////////////////////
4763// class VmaMap
4764
4765// Unused in this version.
4766#if 0
4767
4768#if VMA_USE_STL_UNORDERED_MAP
4769
4770#define VmaPair std::pair
4771
4772#define VMA_MAP_TYPE(KeyT, ValueT) \
4773 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4774
4775#else // #if VMA_USE_STL_UNORDERED_MAP
4776
4777template<typename T1, typename T2>
4778struct VmaPair
4779{
4780 T1 first;
4781 T2 second;
4782
4783 VmaPair() : first(), second() { }
4784 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4785};
4786
4787/* Class compatible with subset of interface of std::unordered_map.
4788KeyT, ValueT must be POD because they will be stored in VmaVector.
4789*/
4790template<typename KeyT, typename ValueT>
4791class VmaMap
4792{
4793public:
4794 typedef VmaPair<KeyT, ValueT> PairType;
4795 typedef PairType* iterator;
4796
4797 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4798
4799 iterator begin() { return m_Vector.begin(); }
4800 iterator end() { return m_Vector.end(); }
4801
4802 void insert(const PairType& pair);
4803 iterator find(const KeyT& key);
4804 void erase(iterator it);
4805
4806private:
4807 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4808};
4809
4810#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4811
4812template<typename FirstT, typename SecondT>
4813struct VmaPairFirstLess
4814{
4815 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4816 {
4817 return lhs.first < rhs.first;
4818 }
4819 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4820 {
4821 return lhs.first < rhsFirst;
4822 }
4823};
4824
4825template<typename KeyT, typename ValueT>
4826void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4827{
4828 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4829 m_Vector.data(),
4830 m_Vector.data() + m_Vector.size(),
4831 pair,
4832 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4833 VmaVectorInsert(m_Vector, indexToInsert, pair);
4834}
4835
4836template<typename KeyT, typename ValueT>
4837VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4838{
4839 PairType* it = VmaBinaryFindFirstNotLess(
4840 m_Vector.data(),
4841 m_Vector.data() + m_Vector.size(),
4842 key,
4843 VmaPairFirstLess<KeyT, ValueT>());
4844 if((it != m_Vector.end()) && (it->first == key))
4845 {
4846 return it;
4847 }
4848 else
4849 {
4850 return m_Vector.end();
4851 }
4852}
4853
4854template<typename KeyT, typename ValueT>
4855void VmaMap<KeyT, ValueT>::erase(iterator it)
4856{
4857 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4858}
4859
4860#endif // #if VMA_USE_STL_UNORDERED_MAP
4861
4862#endif // #if 0
4863
4864////////////////////////////////////////////////////////////////////////////////
4865
4866class VmaDeviceMemoryBlock;
4867
4868enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4869
4870struct VmaAllocation_T
4871{
4872 VMA_CLASS_NO_COPY(VmaAllocation_T)
4873private:
4874 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4875
4876 enum FLAGS
4877 {
4878 FLAG_USER_DATA_STRING = 0x01,
4879 };
4880
4881public:
4882 enum ALLOCATION_TYPE
4883 {
4884 ALLOCATION_TYPE_NONE,
4885 ALLOCATION_TYPE_BLOCK,
4886 ALLOCATION_TYPE_DEDICATED,
4887 };
4888
4889 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4890 m_Alignment(1),
4891 m_Size(0),
4892 m_pUserData(VMA_NULL),
4893 m_LastUseFrameIndex(currentFrameIndex),
4894 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4895 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4896 m_MapCount(0),
4897 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4898 {
4899#if VMA_STATS_STRING_ENABLED
4900 m_CreationFrameIndex = currentFrameIndex;
4901 m_BufferImageUsage = 0;
4902#endif
4903 }
4904
4905 ~VmaAllocation_T()
4906 {
4907 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4908
4909 // Check if owned string was freed.
4910 VMA_ASSERT(m_pUserData == VMA_NULL);
4911 }
4912
4913 void InitBlockAllocation(
4914 VmaPool hPool,
4915 VmaDeviceMemoryBlock* block,
4916 VkDeviceSize offset,
4917 VkDeviceSize alignment,
4918 VkDeviceSize size,
4919 VmaSuballocationType suballocationType,
4920 bool mapped,
4921 bool canBecomeLost)
4922 {
4923 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4924 VMA_ASSERT(block != VMA_NULL);
4925 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4926 m_Alignment = alignment;
4927 m_Size = size;
4928 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4929 m_SuballocationType = (uint8_t)suballocationType;
4930 m_BlockAllocation.m_hPool = hPool;
4931 m_BlockAllocation.m_Block = block;
4932 m_BlockAllocation.m_Offset = offset;
4933 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4934 }
4935
4936 void InitLost()
4937 {
4938 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4939 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4940 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4941 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4942 m_BlockAllocation.m_Block = VMA_NULL;
4943 m_BlockAllocation.m_Offset = 0;
4944 m_BlockAllocation.m_CanBecomeLost = true;
4945 }
4946
4947 void ChangeBlockAllocation(
4948 VmaAllocator hAllocator,
4949 VmaDeviceMemoryBlock* block,
4950 VkDeviceSize offset);
4951
4952 void ChangeSize(VkDeviceSize newSize);
4953 void ChangeOffset(VkDeviceSize newOffset);
4954
4955 // pMappedData not null means allocation is created with MAPPED flag.
4956 void InitDedicatedAllocation(
4957 uint32_t memoryTypeIndex,
4958 VkDeviceMemory hMemory,
4959 VmaSuballocationType suballocationType,
4960 void* pMappedData,
4961 VkDeviceSize size)
4962 {
4963 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4964 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4965 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4966 m_Alignment = 0;
4967 m_Size = size;
4968 m_SuballocationType = (uint8_t)suballocationType;
4969 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4970 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
4971 m_DedicatedAllocation.m_hMemory = hMemory;
4972 m_DedicatedAllocation.m_pMappedData = pMappedData;
4973 }
4974
4975 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
4976 VkDeviceSize GetAlignment() const { return m_Alignment; }
4977 VkDeviceSize GetSize() const { return m_Size; }
4978 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
4979 void* GetUserData() const { return m_pUserData; }
4980 void SetUserData(VmaAllocator hAllocator, void* pUserData);
4981 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
4982
4983 VmaDeviceMemoryBlock* GetBlock() const
4984 {
4985 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4986 return m_BlockAllocation.m_Block;
4987 }
4988 VkDeviceSize GetOffset() const;
4989 VkDeviceMemory GetMemory() const;
4990 uint32_t GetMemoryTypeIndex() const;
4991 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
4992 void* GetMappedData() const;
4993 bool CanBecomeLost() const;
4994 VmaPool GetPool() const;
4995
4996 uint32_t GetLastUseFrameIndex() const
4997 {
4998 return m_LastUseFrameIndex.load();
4999 }
5000 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5001 {
5002 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5003 }
5004 /*
5005 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5006 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5007 - Else, returns false.
5008
5009 If hAllocation is already lost, assert - you should not call it then.
5010 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5011 */
5012 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5013
5014 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5015 {
5016 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5017 outInfo.blockCount = 1;
5018 outInfo.allocationCount = 1;
5019 outInfo.unusedRangeCount = 0;
5020 outInfo.usedBytes = m_Size;
5021 outInfo.unusedBytes = 0;
5022 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5023 outInfo.unusedRangeSizeMin = UINT64_MAX;
5024 outInfo.unusedRangeSizeMax = 0;
5025 }
5026
5027 void BlockAllocMap();
5028 void BlockAllocUnmap();
5029 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5030 void DedicatedAllocUnmap(VmaAllocator hAllocator);
5031
5032#if VMA_STATS_STRING_ENABLED
5033 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
5034 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5035
5036 void InitBufferImageUsage(uint32_t bufferImageUsage)
5037 {
5038 VMA_ASSERT(m_BufferImageUsage == 0);
5039 m_BufferImageUsage = bufferImageUsage;
5040 }
5041
5042 void PrintParameters(class VmaJsonWriter& json) const;
5043#endif
5044
5045private:
5046 VkDeviceSize m_Alignment;
5047 VkDeviceSize m_Size;
5048 void* m_pUserData;
5049 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5050 uint8_t m_Type; // ALLOCATION_TYPE
5051 uint8_t m_SuballocationType; // VmaSuballocationType
5052 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5053 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5054 uint8_t m_MapCount;
5055 uint8_t m_Flags; // enum FLAGS
5056
5057 // Allocation out of VmaDeviceMemoryBlock.
5058 struct BlockAllocation
5059 {
5060 VmaPool m_hPool; // Null if belongs to general memory.
5061 VmaDeviceMemoryBlock* m_Block;
5062 VkDeviceSize m_Offset;
5063 bool m_CanBecomeLost;
5064 };
5065
5066 // Allocation for an object that has its own private VkDeviceMemory.
5067 struct DedicatedAllocation
5068 {
5069 uint32_t m_MemoryTypeIndex;
5070 VkDeviceMemory m_hMemory;
5071 void* m_pMappedData; // Not null means memory is mapped.
5072 };
5073
5074 union
5075 {
5076 // Allocation out of VmaDeviceMemoryBlock.
5077 BlockAllocation m_BlockAllocation;
5078 // Allocation for an object that has its own private VkDeviceMemory.
5079 DedicatedAllocation m_DedicatedAllocation;
5080 };
5081
5082#if VMA_STATS_STRING_ENABLED
5083 uint32_t m_CreationFrameIndex;
5084 uint32_t m_BufferImageUsage; // 0 if unknown.
5085#endif
5086
5087 void FreeUserDataString(VmaAllocator hAllocator);
5088};
5089
5090/*
5091Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5092allocated memory block or free.
5093*/
5094struct VmaSuballocation
5095{
5096 VkDeviceSize offset;
5097 VkDeviceSize size;
5098 VmaAllocation hAllocation;
5099 VmaSuballocationType type;
5100};
5101
5102// Comparator for offsets.
5103struct VmaSuballocationOffsetLess
5104{
5105 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5106 {
5107 return lhs.offset < rhs.offset;
5108 }
5109};
5110struct VmaSuballocationOffsetGreater
5111{
5112 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5113 {
5114 return lhs.offset > rhs.offset;
5115 }
5116};
5117
5118typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5119
5120// Cost of one additional allocation lost, as equivalent in bytes.
5121static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5122
5123/*
5124Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5125
5126If canMakeOtherLost was false:
5127- item points to a FREE suballocation.
5128- itemsToMakeLostCount is 0.
5129
5130If canMakeOtherLost was true:
5131- item points to first of sequence of suballocations, which are either FREE,
5132 or point to VmaAllocations that can become lost.
5133- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5134 the requested allocation to succeed.
5135*/
5136struct VmaAllocationRequest
5137{
5138 VkDeviceSize offset;
5139 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5140 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5141 VmaSuballocationList::iterator item;
5142 size_t itemsToMakeLostCount;
5143 void* customData;
5144
5145 VkDeviceSize CalcCost() const
5146 {
5147 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5148 }
5149};
5150
5151/*
5152Data structure used for bookkeeping of allocations and unused ranges of memory
5153in a single VkDeviceMemory block.
5154*/
5155class VmaBlockMetadata
5156{
5157public:
5158 VmaBlockMetadata(VmaAllocator hAllocator);
5159 virtual ~VmaBlockMetadata() { }
5160 virtual void Init(VkDeviceSize size) { m_Size = size; }
5161
5162 // Validates all data structures inside this object. If not valid, returns false.
5163 virtual bool Validate() const = 0;
5164 VkDeviceSize GetSize() const { return m_Size; }
5165 virtual size_t GetAllocationCount() const = 0;
5166 virtual VkDeviceSize GetSumFreeSize() const = 0;
5167 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5168 // Returns true if this block is empty - contains only single free suballocation.
5169 virtual bool IsEmpty() const = 0;
5170
5171 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5172 // Shouldn't modify blockCount.
5173 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5174
5175#if VMA_STATS_STRING_ENABLED
5176 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5177#endif
5178
5179 // Tries to find a place for suballocation with given parameters inside this block.
5180 // If succeeded, fills pAllocationRequest and returns true.
5181 // If failed, returns false.
5182 virtual bool CreateAllocationRequest(
5183 uint32_t currentFrameIndex,
5184 uint32_t frameInUseCount,
5185 VkDeviceSize bufferImageGranularity,
5186 VkDeviceSize allocSize,
5187 VkDeviceSize allocAlignment,
5188 bool upperAddress,
5189 VmaSuballocationType allocType,
5190 bool canMakeOtherLost,
5191 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5192 uint32_t strategy,
5193 VmaAllocationRequest* pAllocationRequest) = 0;
5194
5195 virtual bool MakeRequestedAllocationsLost(
5196 uint32_t currentFrameIndex,
5197 uint32_t frameInUseCount,
5198 VmaAllocationRequest* pAllocationRequest) = 0;
5199
5200 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5201
5202 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5203
5204 // Makes actual allocation based on request. Request must already be checked and valid.
5205 virtual void Alloc(
5206 const VmaAllocationRequest& request,
5207 VmaSuballocationType type,
5208 VkDeviceSize allocSize,
5209 bool upperAddress,
5210 VmaAllocation hAllocation) = 0;
5211
5212 // Frees suballocation assigned to given memory region.
5213 virtual void Free(const VmaAllocation allocation) = 0;
5214 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5215
5216 // Tries to resize (grow or shrink) space for given allocation, in place.
5217 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5218
5219protected:
5220 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5221
5222#if VMA_STATS_STRING_ENABLED
5223 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5224 VkDeviceSize unusedBytes,
5225 size_t allocationCount,
5226 size_t unusedRangeCount) const;
5227 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5228 VkDeviceSize offset,
5229 VmaAllocation hAllocation) const;
5230 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5231 VkDeviceSize offset,
5232 VkDeviceSize size) const;
5233 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5234#endif
5235
5236private:
5237 VkDeviceSize m_Size;
5238 const VkAllocationCallbacks* m_pAllocationCallbacks;
5239};
5240
5241#define VMA_VALIDATE(cond) do { if(!(cond)) { \
5242 VMA_ASSERT(0 && "Validation failed: " #cond); \
5243 return false; \
5244 } } while(false)
5245
5246class VmaBlockMetadata_Generic : public VmaBlockMetadata
5247{
5248 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5249public:
5250 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5251 virtual ~VmaBlockMetadata_Generic();
5252 virtual void Init(VkDeviceSize size);
5253
5254 virtual bool Validate() const;
5255 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
5256 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5257 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5258 virtual bool IsEmpty() const;
5259
5260 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5261 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5262
5263#if VMA_STATS_STRING_ENABLED
5264 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5265#endif
5266
5267 virtual bool CreateAllocationRequest(
5268 uint32_t currentFrameIndex,
5269 uint32_t frameInUseCount,
5270 VkDeviceSize bufferImageGranularity,
5271 VkDeviceSize allocSize,
5272 VkDeviceSize allocAlignment,
5273 bool upperAddress,
5274 VmaSuballocationType allocType,
5275 bool canMakeOtherLost,
5276 uint32_t strategy,
5277 VmaAllocationRequest* pAllocationRequest);
5278
5279 virtual bool MakeRequestedAllocationsLost(
5280 uint32_t currentFrameIndex,
5281 uint32_t frameInUseCount,
5282 VmaAllocationRequest* pAllocationRequest);
5283
5284 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5285
5286 virtual VkResult CheckCorruption(const void* pBlockData);
5287
5288 virtual void Alloc(
5289 const VmaAllocationRequest& request,
5290 VmaSuballocationType type,
5291 VkDeviceSize allocSize,
5292 bool upperAddress,
5293 VmaAllocation hAllocation);
5294
5295 virtual void Free(const VmaAllocation allocation);
5296 virtual void FreeAtOffset(VkDeviceSize offset);
5297
5298 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5299
5300 ////////////////////////////////////////////////////////////////////////////////
5301 // For defragmentation
5302
5303 bool IsBufferImageGranularityConflictPossible(
5304 VkDeviceSize bufferImageGranularity,
5305 VmaSuballocationType& inOutPrevSuballocType) const;
5306
5307private:
5308 friend class VmaDefragmentationAlgorithm_Generic;
5309 friend class VmaDefragmentationAlgorithm_Fast;
5310
5311 uint32_t m_FreeCount;
5312 VkDeviceSize m_SumFreeSize;
5313 VmaSuballocationList m_Suballocations;
5314 // Suballocations that are free and have size greater than certain threshold.
5315 // Sorted by size, ascending.
5316 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5317
5318 bool ValidateFreeSuballocationList() const;
5319
5320 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5321 // If yes, fills pOffset and returns true. If no, returns false.
5322 bool CheckAllocation(
5323 uint32_t currentFrameIndex,
5324 uint32_t frameInUseCount,
5325 VkDeviceSize bufferImageGranularity,
5326 VkDeviceSize allocSize,
5327 VkDeviceSize allocAlignment,
5328 VmaSuballocationType allocType,
5329 VmaSuballocationList::const_iterator suballocItem,
5330 bool canMakeOtherLost,
5331 VkDeviceSize* pOffset,
5332 size_t* itemsToMakeLostCount,
5333 VkDeviceSize* pSumFreeSize,
5334 VkDeviceSize* pSumItemSize) const;
5335 // Given free suballocation, it merges it with following one, which must also be free.
5336 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5337 // Releases given suballocation, making it free.
5338 // Merges it with adjacent free suballocations if applicable.
5339 // Returns iterator to new free suballocation at this place.
5340 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5341 // Given free suballocation, it inserts it into sorted list of
5342 // m_FreeSuballocationsBySize if it's suitable.
5343 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5344 // Given free suballocation, it removes it from sorted list of
5345 // m_FreeSuballocationsBySize if it's suitable.
5346 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5347};
5348
5349/*
5350Allocations and their references in internal data structure look like this:
5351
5352if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5353
5354 0 +-------+
5355 | |
5356 | |
5357 | |
5358 +-------+
5359 | Alloc | 1st[m_1stNullItemsBeginCount]
5360 +-------+
5361 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5362 +-------+
5363 | ... |
5364 +-------+
5365 | Alloc | 1st[1st.size() - 1]
5366 +-------+
5367 | |
5368 | |
5369 | |
5370GetSize() +-------+
5371
5372if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5373
5374 0 +-------+
5375 | Alloc | 2nd[0]
5376 +-------+
5377 | Alloc | 2nd[1]
5378 +-------+
5379 | ... |
5380 +-------+
5381 | Alloc | 2nd[2nd.size() - 1]
5382 +-------+
5383 | |
5384 | |
5385 | |
5386 +-------+
5387 | Alloc | 1st[m_1stNullItemsBeginCount]
5388 +-------+
5389 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5390 +-------+
5391 | ... |
5392 +-------+
5393 | Alloc | 1st[1st.size() - 1]
5394 +-------+
5395 | |
5396GetSize() +-------+
5397
5398if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5399
5400 0 +-------+
5401 | |
5402 | |
5403 | |
5404 +-------+
5405 | Alloc | 1st[m_1stNullItemsBeginCount]
5406 +-------+
5407 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5408 +-------+
5409 | ... |
5410 +-------+
5411 | Alloc | 1st[1st.size() - 1]
5412 +-------+
5413 | |
5414 | |
5415 | |
5416 +-------+
5417 | Alloc | 2nd[2nd.size() - 1]
5418 +-------+
5419 | ... |
5420 +-------+
5421 | Alloc | 2nd[1]
5422 +-------+
5423 | Alloc | 2nd[0]
5424GetSize() +-------+
5425
5426*/
5427class VmaBlockMetadata_Linear : public VmaBlockMetadata
5428{
5429 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5430public:
5431 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5432 virtual ~VmaBlockMetadata_Linear();
5433 virtual void Init(VkDeviceSize size);
5434
5435 virtual bool Validate() const;
5436 virtual size_t GetAllocationCount() const;
5437 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5438 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5439 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5440
5441 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5442 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5443
5444#if VMA_STATS_STRING_ENABLED
5445 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5446#endif
5447
5448 virtual bool CreateAllocationRequest(
5449 uint32_t currentFrameIndex,
5450 uint32_t frameInUseCount,
5451 VkDeviceSize bufferImageGranularity,
5452 VkDeviceSize allocSize,
5453 VkDeviceSize allocAlignment,
5454 bool upperAddress,
5455 VmaSuballocationType allocType,
5456 bool canMakeOtherLost,
5457 uint32_t strategy,
5458 VmaAllocationRequest* pAllocationRequest);
5459
5460 virtual bool MakeRequestedAllocationsLost(
5461 uint32_t currentFrameIndex,
5462 uint32_t frameInUseCount,
5463 VmaAllocationRequest* pAllocationRequest);
5464
5465 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5466
5467 virtual VkResult CheckCorruption(const void* pBlockData);
5468
5469 virtual void Alloc(
5470 const VmaAllocationRequest& request,
5471 VmaSuballocationType type,
5472 VkDeviceSize allocSize,
5473 bool upperAddress,
5474 VmaAllocation hAllocation);
5475
5476 virtual void Free(const VmaAllocation allocation);
5477 virtual void FreeAtOffset(VkDeviceSize offset);
5478
5479private:
5480 /*
5481 There are two suballocation vectors, used in ping-pong way.
5482 The one with index m_1stVectorIndex is called 1st.
5483 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5484 2nd can be non-empty only when 1st is not empty.
5485 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5486 */
5487 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5488
5489 enum SECOND_VECTOR_MODE
5490 {
5491 SECOND_VECTOR_EMPTY,
5492 /*
5493 Suballocations in 2nd vector are created later than the ones in 1st, but they
5494 all have smaller offset.
5495 */
5496 SECOND_VECTOR_RING_BUFFER,
5497 /*
5498 Suballocations in 2nd vector are upper side of double stack.
5499 They all have offsets higher than those in 1st vector.
5500 Top of this stack means smaller offsets, but higher indices in this vector.
5501 */
5502 SECOND_VECTOR_DOUBLE_STACK,
5503 };
5504
5505 VkDeviceSize m_SumFreeSize;
5506 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5507 uint32_t m_1stVectorIndex;
5508 SECOND_VECTOR_MODE m_2ndVectorMode;
5509
5510 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5511 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5512 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5513 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5514
5515 // Number of items in 1st vector with hAllocation = null at the beginning.
5516 size_t m_1stNullItemsBeginCount;
5517 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5518 size_t m_1stNullItemsMiddleCount;
5519 // Number of items in 2nd vector with hAllocation = null.
5520 size_t m_2ndNullItemsCount;
5521
5522 bool ShouldCompact1st() const;
5523 void CleanupAfterFree();
5524};
5525
5526/*
5527- GetSize() is the original size of allocated memory block.
5528- m_UsableSize is this size aligned down to a power of two.
5529 All allocations and calculations happen relative to m_UsableSize.
5530- GetUnusableSize() is the difference between them.
5531 It is repoted as separate, unused range, not available for allocations.
5532
5533Node at level 0 has size = m_UsableSize.
5534Each next level contains nodes with size 2 times smaller than current level.
5535m_LevelCount is the maximum number of levels to use in the current object.
5536*/
5537class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5538{
5539 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5540public:
5541 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5542 virtual ~VmaBlockMetadata_Buddy();
5543 virtual void Init(VkDeviceSize size);
5544
5545 virtual bool Validate() const;
5546 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
5547 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5548 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5549 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5550
5551 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5552 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5553
5554#if VMA_STATS_STRING_ENABLED
5555 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5556#endif
5557
5558 virtual bool CreateAllocationRequest(
5559 uint32_t currentFrameIndex,
5560 uint32_t frameInUseCount,
5561 VkDeviceSize bufferImageGranularity,
5562 VkDeviceSize allocSize,
5563 VkDeviceSize allocAlignment,
5564 bool upperAddress,
5565 VmaSuballocationType allocType,
5566 bool canMakeOtherLost,
5567 uint32_t strategy,
5568 VmaAllocationRequest* pAllocationRequest);
5569
5570 virtual bool MakeRequestedAllocationsLost(
5571 uint32_t currentFrameIndex,
5572 uint32_t frameInUseCount,
5573 VmaAllocationRequest* pAllocationRequest);
5574
5575 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5576
5577 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5578
5579 virtual void Alloc(
5580 const VmaAllocationRequest& request,
5581 VmaSuballocationType type,
5582 VkDeviceSize allocSize,
5583 bool upperAddress,
5584 VmaAllocation hAllocation);
5585
5586 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
5587 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5588
5589private:
5590 static const VkDeviceSize MIN_NODE_SIZE = 32;
5591 static const size_t MAX_LEVELS = 30;
5592
5593 struct ValidationContext
5594 {
5595 size_t calculatedAllocationCount;
5596 size_t calculatedFreeCount;
5597 VkDeviceSize calculatedSumFreeSize;
5598
5599 ValidationContext() :
5600 calculatedAllocationCount(0),
5601 calculatedFreeCount(0),
5602 calculatedSumFreeSize(0) { }
5603 };
5604
5605 struct Node
5606 {
5607 VkDeviceSize offset;
5608 enum TYPE
5609 {
5610 TYPE_FREE,
5611 TYPE_ALLOCATION,
5612 TYPE_SPLIT,
5613 TYPE_COUNT
5614 } type;
5615 Node* parent;
5616 Node* buddy;
5617
5618 union
5619 {
5620 struct
5621 {
5622 Node* prev;
5623 Node* next;
5624 } free;
5625 struct
5626 {
5627 VmaAllocation alloc;
5628 } allocation;
5629 struct
5630 {
5631 Node* leftChild;
5632 } split;
5633 };
5634 };
5635
5636 // Size of the memory block aligned down to a power of two.
5637 VkDeviceSize m_UsableSize;
5638 uint32_t m_LevelCount;
5639
5640 Node* m_Root;
5641 struct {
5642 Node* front;
5643 Node* back;
5644 } m_FreeList[MAX_LEVELS];
5645 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5646 size_t m_AllocationCount;
5647 // Number of nodes in the tree with type == TYPE_FREE.
5648 size_t m_FreeCount;
5649 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5650 VkDeviceSize m_SumFreeSize;
5651
5652 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5653 void DeleteNode(Node* node);
5654 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5655 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
5656 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5657 // Alloc passed just for validation. Can be null.
5658 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5659 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5660 // Adds node to the front of FreeList at given level.
5661 // node->type must be FREE.
5662 // node->free.prev, next can be undefined.
5663 void AddToFreeListFront(uint32_t level, Node* node);
5664 // Removes node from FreeList at given level.
5665 // node->type must be FREE.
5666 // node->free.prev, next stay untouched.
5667 void RemoveFromFreeList(uint32_t level, Node* node);
5668
5669#if VMA_STATS_STRING_ENABLED
5670 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5671#endif
5672};
5673
5674/*
5675Represents a single block of device memory (`VkDeviceMemory`) with all the
5676data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5677
5678Thread-safety: This class must be externally synchronized.
5679*/
5680class VmaDeviceMemoryBlock
5681{
5682 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5683public:
5684 VmaBlockMetadata* m_pMetadata;
5685
5686 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5687
5688 ~VmaDeviceMemoryBlock()
5689 {
5690 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5691 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5692 }
5693
5694 // Always call after construction.
5695 void Init(
5696 VmaAllocator hAllocator,
5697 uint32_t newMemoryTypeIndex,
5698 VkDeviceMemory newMemory,
5699 VkDeviceSize newSize,
5700 uint32_t id,
5701 uint32_t algorithm);
5702 // Always call before destruction.
5703 void Destroy(VmaAllocator allocator);
5704
5705 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
5706 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5707 uint32_t GetId() const { return m_Id; }
5708 void* GetMappedData() const { return m_pMappedData; }
5709
5710 // Validates all data structures inside this object. If not valid, returns false.
5711 bool Validate() const;
5712
5713 VkResult CheckCorruption(VmaAllocator hAllocator);
5714
5715 // ppData can be null.
5716 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5717 void Unmap(VmaAllocator hAllocator, uint32_t count);
5718
5719 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5720 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5721
5722 VkResult BindBufferMemory(
5723 const VmaAllocator hAllocator,
5724 const VmaAllocation hAllocation,
5725 VkBuffer hBuffer);
5726 VkResult BindImageMemory(
5727 const VmaAllocator hAllocator,
5728 const VmaAllocation hAllocation,
5729 VkImage hImage);
5730
5731private:
5732 uint32_t m_MemoryTypeIndex;
5733 uint32_t m_Id;
5734 VkDeviceMemory m_hMemory;
5735
5736 /*
5737 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5738 Also protects m_MapCount, m_pMappedData.
5739 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5740 */
5741 VMA_MUTEX m_Mutex;
5742 uint32_t m_MapCount;
5743 void* m_pMappedData;
5744};
5745
5746struct VmaPointerLess
5747{
5748 bool operator()(const void* lhs, const void* rhs) const
5749 {
5750 return lhs < rhs;
5751 }
5752};
5753
5754struct VmaDefragmentationMove
5755{
5756 size_t srcBlockIndex;
5757 size_t dstBlockIndex;
5758 VkDeviceSize srcOffset;
5759 VkDeviceSize dstOffset;
5760 VkDeviceSize size;
5761};
5762
5763class VmaDefragmentationAlgorithm;
5764
5765/*
5766Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5767Vulkan memory type.
5768
5769Synchronized internally with a mutex.
5770*/
5771struct VmaBlockVector
5772{
5773 VMA_CLASS_NO_COPY(VmaBlockVector)
5774public:
5775 VmaBlockVector(
5776 VmaAllocator hAllocator,
5777 uint32_t memoryTypeIndex,
5778 VkDeviceSize preferredBlockSize,
5779 size_t minBlockCount,
5780 size_t maxBlockCount,
5781 VkDeviceSize bufferImageGranularity,
5782 uint32_t frameInUseCount,
5783 bool isCustomPool,
5784 bool explicitBlockSize,
5785 uint32_t algorithm);
5786 ~VmaBlockVector();
5787
5788 VkResult CreateMinBlocks();
5789
5790 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5791 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
5792 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
5793 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
5794 uint32_t GetAlgorithm() const { return m_Algorithm; }
5795
5796 void GetPoolStats(VmaPoolStats* pStats);
5797
5798 bool IsEmpty() const { return m_Blocks.empty(); }
5799 bool IsCorruptionDetectionEnabled() const;
5800
5801 VkResult Allocate(
5802 VmaPool hCurrentPool,
5803 uint32_t currentFrameIndex,
5804 VkDeviceSize size,
5805 VkDeviceSize alignment,
5806 const VmaAllocationCreateInfo& createInfo,
5807 VmaSuballocationType suballocType,
5808 size_t allocationCount,
5809 VmaAllocation* pAllocations);
5810
5811 void Free(
5812 VmaAllocation hAllocation);
5813
5814 // Adds statistics of this BlockVector to pStats.
5815 void AddStats(VmaStats* pStats);
5816
5817#if VMA_STATS_STRING_ENABLED
5818 void PrintDetailedMap(class VmaJsonWriter& json);
5819#endif
5820
5821 void MakePoolAllocationsLost(
5822 uint32_t currentFrameIndex,
5823 size_t* pLostAllocationCount);
5824 VkResult CheckCorruption();
5825
5826 // Saves results in pCtx->res.
5827 void Defragment(
5828 class VmaBlockVectorDefragmentationContext* pCtx,
5829 VmaDefragmentationStats* pStats,
5830 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5831 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5832 VkCommandBuffer commandBuffer);
5833 void DefragmentationEnd(
5834 class VmaBlockVectorDefragmentationContext* pCtx,
5835 VmaDefragmentationStats* pStats);
5836
5837 ////////////////////////////////////////////////////////////////////////////////
5838 // To be used only while the m_Mutex is locked. Used during defragmentation.
5839
5840 size_t GetBlockCount() const { return m_Blocks.size(); }
5841 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5842 size_t CalcAllocationCount() const;
5843 bool IsBufferImageGranularityConflictPossible() const;
5844
5845private:
5846 friend class VmaDefragmentationAlgorithm_Generic;
5847
5848 const VmaAllocator m_hAllocator;
5849 const uint32_t m_MemoryTypeIndex;
5850 const VkDeviceSize m_PreferredBlockSize;
5851 const size_t m_MinBlockCount;
5852 const size_t m_MaxBlockCount;
5853 const VkDeviceSize m_BufferImageGranularity;
5854 const uint32_t m_FrameInUseCount;
5855 const bool m_IsCustomPool;
5856 const bool m_ExplicitBlockSize;
5857 const uint32_t m_Algorithm;
5858 /* There can be at most one allocation that is completely empty - a
5859 hysteresis to avoid pessimistic case of alternating creation and destruction
5860 of a VkDeviceMemory. */
5861 bool m_HasEmptyBlock;
5862 VMA_RW_MUTEX m_Mutex;
5863 // Incrementally sorted by sumFreeSize, ascending.
5864 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5865 uint32_t m_NextBlockId;
5866
5867 VkDeviceSize CalcMaxBlockSize() const;
5868
5869 // Finds and removes given block from vector.
5870 void Remove(VmaDeviceMemoryBlock* pBlock);
5871
5872 // Performs single step in sorting m_Blocks. They may not be fully sorted
5873 // after this call.
5874 void IncrementallySortBlocks();
5875
5876 VkResult AllocatePage(
5877 VmaPool hCurrentPool,
5878 uint32_t currentFrameIndex,
5879 VkDeviceSize size,
5880 VkDeviceSize alignment,
5881 const VmaAllocationCreateInfo& createInfo,
5882 VmaSuballocationType suballocType,
5883 VmaAllocation* pAllocation);
5884
5885 // To be used only without CAN_MAKE_OTHER_LOST flag.
5886 VkResult AllocateFromBlock(
5887 VmaDeviceMemoryBlock* pBlock,
5888 VmaPool hCurrentPool,
5889 uint32_t currentFrameIndex,
5890 VkDeviceSize size,
5891 VkDeviceSize alignment,
5892 VmaAllocationCreateFlags allocFlags,
5893 void* pUserData,
5894 VmaSuballocationType suballocType,
5895 uint32_t strategy,
5896 VmaAllocation* pAllocation);
5897
5898 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5899
5900 // Saves result to pCtx->res.
5901 void ApplyDefragmentationMovesCpu(
5902 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5903 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5904 // Saves result to pCtx->res.
5905 void ApplyDefragmentationMovesGpu(
5906 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5907 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5908 VkCommandBuffer commandBuffer);
5909
5910 /*
5911 Used during defragmentation. pDefragmentationStats is optional. It's in/out
5912 - updated with new data.
5913 */
5914 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5915};
5916
5917struct VmaPool_T
5918{
5919 VMA_CLASS_NO_COPY(VmaPool_T)
5920public:
5921 VmaBlockVector m_BlockVector;
5922
5923 VmaPool_T(
5924 VmaAllocator hAllocator,
5925 const VmaPoolCreateInfo& createInfo,
5926 VkDeviceSize preferredBlockSize);
5927 ~VmaPool_T();
5928
5929 uint32_t GetId() const { return m_Id; }
5930 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5931
5932#if VMA_STATS_STRING_ENABLED
5933 //void PrintDetailedMap(class VmaStringBuilder& sb);
5934#endif
5935
5936private:
5937 uint32_t m_Id;
5938};
5939
5940/*
5941Performs defragmentation:
5942
5943- Updates `pBlockVector->m_pMetadata`.
5944- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5945- Does not move actual data, only returns requested moves as `moves`.
5946*/
5947class VmaDefragmentationAlgorithm
5948{
5949 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5950public:
5951 VmaDefragmentationAlgorithm(
5952 VmaAllocator hAllocator,
5953 VmaBlockVector* pBlockVector,
5954 uint32_t currentFrameIndex) :
5955 m_hAllocator(hAllocator),
5956 m_pBlockVector(pBlockVector),
5957 m_CurrentFrameIndex(currentFrameIndex)
5958 {
5959 }
5960 virtual ~VmaDefragmentationAlgorithm()
5961 {
5962 }
5963
5964 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5965 virtual void AddAll() = 0;
5966
5967 virtual VkResult Defragment(
5968 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5969 VkDeviceSize maxBytesToMove,
5970 uint32_t maxAllocationsToMove) = 0;
5971
5972 virtual VkDeviceSize GetBytesMoved() const = 0;
5973 virtual uint32_t GetAllocationsMoved() const = 0;
5974
5975protected:
5976 VmaAllocator const m_hAllocator;
5977 VmaBlockVector* const m_pBlockVector;
5978 const uint32_t m_CurrentFrameIndex;
5979
5980 struct AllocationInfo
5981 {
5982 VmaAllocation m_hAllocation;
5983 VkBool32* m_pChanged;
5984
5985 AllocationInfo() :
5986 m_hAllocation(VK_NULL_HANDLE),
5987 m_pChanged(VMA_NULL)
5988 {
5989 }
5990 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
5991 m_hAllocation(hAlloc),
5992 m_pChanged(pChanged)
5993 {
5994 }
5995 };
5996};
5997
5998class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
5999{
6000 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6001public:
6002 VmaDefragmentationAlgorithm_Generic(
6003 VmaAllocator hAllocator,
6004 VmaBlockVector* pBlockVector,
6005 uint32_t currentFrameIndex,
6006 bool overlappingMoveSupported);
6007 virtual ~VmaDefragmentationAlgorithm_Generic();
6008
6009 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6010 virtual void AddAll() { m_AllAllocations = true; }
6011
6012 virtual VkResult Defragment(
6013 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6014 VkDeviceSize maxBytesToMove,
6015 uint32_t maxAllocationsToMove);
6016
6017 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6018 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6019
6020private:
6021 uint32_t m_AllocationCount;
6022 bool m_AllAllocations;
6023
6024 VkDeviceSize m_BytesMoved;
6025 uint32_t m_AllocationsMoved;
6026
6027 struct AllocationInfoSizeGreater
6028 {
6029 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6030 {
6031 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6032 }
6033 };
6034
6035 struct AllocationInfoOffsetGreater
6036 {
6037 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6038 {
6039 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6040 }
6041 };
6042
6043 struct BlockInfo
6044 {
6045 size_t m_OriginalBlockIndex;
6046 VmaDeviceMemoryBlock* m_pBlock;
6047 bool m_HasNonMovableAllocations;
6048 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6049
6050 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6051 m_OriginalBlockIndex(SIZE_MAX),
6052 m_pBlock(VMA_NULL),
6053 m_HasNonMovableAllocations(true),
6054 m_Allocations(pAllocationCallbacks)
6055 {
6056 }
6057
6058 void CalcHasNonMovableAllocations()
6059 {
6060 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6061 const size_t defragmentAllocCount = m_Allocations.size();
6062 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6063 }
6064
6065 void SortAllocationsBySizeDescending()
6066 {
6067 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6068 }
6069
6070 void SortAllocationsByOffsetDescending()
6071 {
6072 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6073 }
6074 };
6075
6076 struct BlockPointerLess
6077 {
6078 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6079 {
6080 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6081 }
6082 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6083 {
6084 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6085 }
6086 };
6087
6088 // 1. Blocks with some non-movable allocations go first.
6089 // 2. Blocks with smaller sumFreeSize go first.
6090 struct BlockInfoCompareMoveDestination
6091 {
6092 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6093 {
6094 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6095 {
6096 return true;
6097 }
6098 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6099 {
6100 return false;
6101 }
6102 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6103 {
6104 return true;
6105 }
6106 return false;
6107 }
6108 };
6109
6110 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6111 BlockInfoVector m_Blocks;
6112
6113 VkResult DefragmentRound(
6114 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6115 VkDeviceSize maxBytesToMove,
6116 uint32_t maxAllocationsToMove);
6117
6118 size_t CalcBlocksWithNonMovableCount() const;
6119
6120 static bool MoveMakesSense(
6121 size_t dstBlockIndex, VkDeviceSize dstOffset,
6122 size_t srcBlockIndex, VkDeviceSize srcOffset);
6123};
6124
6125class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6126{
6127 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6128public:
6129 VmaDefragmentationAlgorithm_Fast(
6130 VmaAllocator hAllocator,
6131 VmaBlockVector* pBlockVector,
6132 uint32_t currentFrameIndex,
6133 bool overlappingMoveSupported);
6134 virtual ~VmaDefragmentationAlgorithm_Fast();
6135
6136 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
6137 virtual void AddAll() { m_AllAllocations = true; }
6138
6139 virtual VkResult Defragment(
6140 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6141 VkDeviceSize maxBytesToMove,
6142 uint32_t maxAllocationsToMove);
6143
6144 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6145 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6146
6147private:
6148 struct BlockInfo
6149 {
6150 size_t origBlockIndex;
6151 };
6152
6153 class FreeSpaceDatabase
6154 {
6155 public:
6156 FreeSpaceDatabase()
6157 {
6158 FreeSpace s = {};
6159 s.blockInfoIndex = SIZE_MAX;
6160 for(size_t i = 0; i < MAX_COUNT; ++i)
6161 {
6162 m_FreeSpaces[i] = s;
6163 }
6164 }
6165
6166 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6167 {
6168 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6169 {
6170 return;
6171 }
6172
6173 // Find first invalid or the smallest structure.
6174 size_t bestIndex = SIZE_MAX;
6175 for(size_t i = 0; i < MAX_COUNT; ++i)
6176 {
6177 // Empty structure.
6178 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6179 {
6180 bestIndex = i;
6181 break;
6182 }
6183 if(m_FreeSpaces[i].size < size &&
6184 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6185 {
6186 bestIndex = i;
6187 }
6188 }
6189
6190 if(bestIndex != SIZE_MAX)
6191 {
6192 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6193 m_FreeSpaces[bestIndex].offset = offset;
6194 m_FreeSpaces[bestIndex].size = size;
6195 }
6196 }
6197
6198 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6199 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6200 {
6201 size_t bestIndex = SIZE_MAX;
6202 VkDeviceSize bestFreeSpaceAfter = 0;
6203 for(size_t i = 0; i < MAX_COUNT; ++i)
6204 {
6205 // Structure is valid.
6206 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6207 {
6208 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6209 // Allocation fits into this structure.
6210 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6211 {
6212 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6213 (dstOffset + size);
6214 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6215 {
6216 bestIndex = i;
6217 bestFreeSpaceAfter = freeSpaceAfter;
6218 }
6219 }
6220 }
6221 }
6222
6223 if(bestIndex != SIZE_MAX)
6224 {
6225 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6226 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6227
6228 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6229 {
6230 // Leave this structure for remaining empty space.
6231 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6232 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6233 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6234 }
6235 else
6236 {
6237 // This structure becomes invalid.
6238 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6239 }
6240
6241 return true;
6242 }
6243
6244 return false;
6245 }
6246
6247 private:
6248 static const size_t MAX_COUNT = 4;
6249
6250 struct FreeSpace
6251 {
6252 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6253 VkDeviceSize offset;
6254 VkDeviceSize size;
6255 } m_FreeSpaces[MAX_COUNT];
6256 };
6257
6258 const bool m_OverlappingMoveSupported;
6259
6260 uint32_t m_AllocationCount;
6261 bool m_AllAllocations;
6262
6263 VkDeviceSize m_BytesMoved;
6264 uint32_t m_AllocationsMoved;
6265
6266 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6267
6268 void PreprocessMetadata();
6269 void PostprocessMetadata();
6270 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6271};
6272
6273struct VmaBlockDefragmentationContext
6274{
6275 enum BLOCK_FLAG
6276 {
6277 BLOCK_FLAG_USED = 0x00000001,
6278 };
6279 uint32_t flags;
6280 VkBuffer hBuffer;
6281
6282 VmaBlockDefragmentationContext() :
6283 flags(0),
6284 hBuffer(VK_NULL_HANDLE)
6285 {
6286 }
6287};
6288
6289class VmaBlockVectorDefragmentationContext
6290{
6291 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6292public:
6293 VkResult res;
6294 bool mutexLocked;
6295 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6296
6297 VmaBlockVectorDefragmentationContext(
6298 VmaAllocator hAllocator,
6299 VmaPool hCustomPool, // Optional.
6300 VmaBlockVector* pBlockVector,
6301 uint32_t currFrameIndex,
6302 uint32_t flags);
6303 ~VmaBlockVectorDefragmentationContext();
6304
6305 VmaPool GetCustomPool() const { return m_hCustomPool; }
6306 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
6307 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6308
6309 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6310 void AddAll() { m_AllAllocations = true; }
6311
6312 void Begin(bool overlappingMoveSupported);
6313
6314private:
6315 const VmaAllocator m_hAllocator;
6316 // Null if not from custom pool.
6317 const VmaPool m_hCustomPool;
6318 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6319 VmaBlockVector* const m_pBlockVector;
6320 const uint32_t m_CurrFrameIndex;
6321 const uint32_t m_AlgorithmFlags;
6322 // Owner of this object.
6323 VmaDefragmentationAlgorithm* m_pAlgorithm;
6324
6325 struct AllocInfo
6326 {
6327 VmaAllocation hAlloc;
6328 VkBool32* pChanged;
6329 };
6330 // Used between constructor and Begin.
6331 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6332 bool m_AllAllocations;
6333};
6334
6335struct VmaDefragmentationContext_T
6336{
6337private:
6338 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6339public:
6340 VmaDefragmentationContext_T(
6341 VmaAllocator hAllocator,
6342 uint32_t currFrameIndex,
6343 uint32_t flags,
6344 VmaDefragmentationStats* pStats);
6345 ~VmaDefragmentationContext_T();
6346
6347 void AddPools(uint32_t poolCount, VmaPool* pPools);
6348 void AddAllocations(
6349 uint32_t allocationCount,
6350 VmaAllocation* pAllocations,
6351 VkBool32* pAllocationsChanged);
6352
6353 /*
6354 Returns:
6355 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6356 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6357 - Negative value if error occured and object can be destroyed immediately.
6358 */
6359 VkResult Defragment(
6360 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6361 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6362 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6363
6364private:
6365 const VmaAllocator m_hAllocator;
6366 const uint32_t m_CurrFrameIndex;
6367 const uint32_t m_Flags;
6368 VmaDefragmentationStats* const m_pStats;
6369 // Owner of these objects.
6370 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6371 // Owner of these objects.
6372 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6373};
6374
6375#if VMA_RECORDING_ENABLED
6376
6377class VmaRecorder
6378{
6379public:
6380 VmaRecorder();
6381 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6382 void WriteConfiguration(
6383 const VkPhysicalDeviceProperties& devProps,
6384 const VkPhysicalDeviceMemoryProperties& memProps,
6385 bool dedicatedAllocationExtensionEnabled);
6386 ~VmaRecorder();
6387
6388 void RecordCreateAllocator(uint32_t frameIndex);
6389 void RecordDestroyAllocator(uint32_t frameIndex);
6390 void RecordCreatePool(uint32_t frameIndex,
6391 const VmaPoolCreateInfo& createInfo,
6392 VmaPool pool);
6393 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6394 void RecordAllocateMemory(uint32_t frameIndex,
6395 const VkMemoryRequirements& vkMemReq,
6396 const VmaAllocationCreateInfo& createInfo,
6397 VmaAllocation allocation);
6398 void RecordAllocateMemoryPages(uint32_t frameIndex,
6399 const VkMemoryRequirements& vkMemReq,
6400 const VmaAllocationCreateInfo& createInfo,
6401 uint64_t allocationCount,
6402 const VmaAllocation* pAllocations);
6403 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6404 const VkMemoryRequirements& vkMemReq,
6405 bool requiresDedicatedAllocation,
6406 bool prefersDedicatedAllocation,
6407 const VmaAllocationCreateInfo& createInfo,
6408 VmaAllocation allocation);
6409 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6410 const VkMemoryRequirements& vkMemReq,
6411 bool requiresDedicatedAllocation,
6412 bool prefersDedicatedAllocation,
6413 const VmaAllocationCreateInfo& createInfo,
6414 VmaAllocation allocation);
6415 void RecordFreeMemory(uint32_t frameIndex,
6416 VmaAllocation allocation);
6417 void RecordFreeMemoryPages(uint32_t frameIndex,
6418 uint64_t allocationCount,
6419 const VmaAllocation* pAllocations);
6420 void RecordResizeAllocation(
6421 uint32_t frameIndex,
6422 VmaAllocation allocation,
6423 VkDeviceSize newSize);
6424 void RecordSetAllocationUserData(uint32_t frameIndex,
6425 VmaAllocation allocation,
6426 const void* pUserData);
6427 void RecordCreateLostAllocation(uint32_t frameIndex,
6428 VmaAllocation allocation);
6429 void RecordMapMemory(uint32_t frameIndex,
6430 VmaAllocation allocation);
6431 void RecordUnmapMemory(uint32_t frameIndex,
6432 VmaAllocation allocation);
6433 void RecordFlushAllocation(uint32_t frameIndex,
6434 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6435 void RecordInvalidateAllocation(uint32_t frameIndex,
6436 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6437 void RecordCreateBuffer(uint32_t frameIndex,
6438 const VkBufferCreateInfo& bufCreateInfo,
6439 const VmaAllocationCreateInfo& allocCreateInfo,
6440 VmaAllocation allocation);
6441 void RecordCreateImage(uint32_t frameIndex,
6442 const VkImageCreateInfo& imageCreateInfo,
6443 const VmaAllocationCreateInfo& allocCreateInfo,
6444 VmaAllocation allocation);
6445 void RecordDestroyBuffer(uint32_t frameIndex,
6446 VmaAllocation allocation);
6447 void RecordDestroyImage(uint32_t frameIndex,
6448 VmaAllocation allocation);
6449 void RecordTouchAllocation(uint32_t frameIndex,
6450 VmaAllocation allocation);
6451 void RecordGetAllocationInfo(uint32_t frameIndex,
6452 VmaAllocation allocation);
6453 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6454 VmaPool pool);
6455 void RecordDefragmentationBegin(uint32_t frameIndex,
6456 const VmaDefragmentationInfo2& info,
6457 VmaDefragmentationContext ctx);
6458 void RecordDefragmentationEnd(uint32_t frameIndex,
6459 VmaDefragmentationContext ctx);
6460
6461private:
6462 struct CallParams
6463 {
6464 uint32_t threadId;
6465 double time;
6466 };
6467
6468 class UserDataString
6469 {
6470 public:
6471 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
6472 const char* GetString() const { return m_Str; }
6473
6474 private:
6475 char m_PtrStr[17];
6476 const char* m_Str;
6477 };
6478
6479 bool m_UseMutex;
6480 VmaRecordFlags m_Flags;
6481 FILE* m_File;
6482 VMA_MUTEX m_FileMutex;
6483 int64_t m_Freq;
6484 int64_t m_StartCounter;
6485
6486 void GetBasicParams(CallParams& outParams);
6487
6488 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6489 template<typename T>
6490 void PrintPointerList(uint64_t count, const T* pItems)
6491 {
6492 if(count)
6493 {
6494 fprintf(m_File, "%p", pItems[0]);
6495 for(uint64_t i = 1; i < count; ++i)
6496 {
6497 fprintf(m_File, " %p", pItems[i]);
6498 }
6499 }
6500 }
6501
6502 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6503 void Flush();
6504};
6505
6506#endif // #if VMA_RECORDING_ENABLED
6507
6508// Main allocator object.
6509struct VmaAllocator_T
6510{
6511 VMA_CLASS_NO_COPY(VmaAllocator_T)
6512public:
6513 bool m_UseMutex;
6514 bool m_UseKhrDedicatedAllocation;
6515 VkDevice m_hDevice;
6516 bool m_AllocationCallbacksSpecified;
6517 VkAllocationCallbacks m_AllocationCallbacks;
6518 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6519
6520 // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6521 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6522 VMA_MUTEX m_HeapSizeLimitMutex;
6523
6524 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6525 VkPhysicalDeviceMemoryProperties m_MemProps;
6526
6527 // Default pools.
6528 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6529
6530 // Each vector is sorted by memory (handle value).
6531 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6532 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6533 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6534
6535 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6536 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6537 ~VmaAllocator_T();
6538
6539 const VkAllocationCallbacks* GetAllocationCallbacks() const
6540 {
6541 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6542 }
6543 const VmaVulkanFunctions& GetVulkanFunctions() const
6544 {
6545 return m_VulkanFunctions;
6546 }
6547
6548 VkDeviceSize GetBufferImageGranularity() const
6549 {
6550 return VMA_MAX(
6551 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6552 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6553 }
6554
6555 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
6556 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6557
6558 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6559 {
6560 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6561 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6562 }
6563 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
6564 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6565 {
6566 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6567 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6568 }
6569 // Minimum alignment for all allocations in specific memory type.
6570 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6571 {
6572 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6573 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6574 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6575 }
6576
6577 bool IsIntegratedGpu() const
6578 {
6579 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6580 }
6581
6582#if VMA_RECORDING_ENABLED
6583 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6584#endif
6585
6586 void GetBufferMemoryRequirements(
6587 VkBuffer hBuffer,
6588 VkMemoryRequirements& memReq,
6589 bool& requiresDedicatedAllocation,
6590 bool& prefersDedicatedAllocation) const;
6591 void GetImageMemoryRequirements(
6592 VkImage hImage,
6593 VkMemoryRequirements& memReq,
6594 bool& requiresDedicatedAllocation,
6595 bool& prefersDedicatedAllocation) const;
6596
6597 // Main allocation function.
6598 VkResult AllocateMemory(
6599 const VkMemoryRequirements& vkMemReq,
6600 bool requiresDedicatedAllocation,
6601 bool prefersDedicatedAllocation,
6602 VkBuffer dedicatedBuffer,
6603 VkImage dedicatedImage,
6604 const VmaAllocationCreateInfo& createInfo,
6605 VmaSuballocationType suballocType,
6606 size_t allocationCount,
6607 VmaAllocation* pAllocations);
6608
6609 // Main deallocation function.
6610 void FreeMemory(
6611 size_t allocationCount,
6612 const VmaAllocation* pAllocations);
6613
6614 VkResult ResizeAllocation(
6615 const VmaAllocation alloc,
6616 VkDeviceSize newSize);
6617
6618 void CalculateStats(VmaStats* pStats);
6619
6620#if VMA_STATS_STRING_ENABLED
6621 void PrintDetailedMap(class VmaJsonWriter& json);
6622#endif
6623
6624 VkResult DefragmentationBegin(
6625 const VmaDefragmentationInfo2& info,
6626 VmaDefragmentationStats* pStats,
6627 VmaDefragmentationContext* pContext);
6628 VkResult DefragmentationEnd(
6629 VmaDefragmentationContext context);
6630
6631 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6632 bool TouchAllocation(VmaAllocation hAllocation);
6633
6634 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6635 void DestroyPool(VmaPool pool);
6636 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6637
6638 void SetCurrentFrameIndex(uint32_t frameIndex);
6639 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6640
6641 void MakePoolAllocationsLost(
6642 VmaPool hPool,
6643 size_t* pLostAllocationCount);
6644 VkResult CheckPoolCorruption(VmaPool hPool);
6645 VkResult CheckCorruption(uint32_t memoryTypeBits);
6646
6647 void CreateLostAllocation(VmaAllocation* pAllocation);
6648
6649 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6650 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6651
6652 VkResult Map(VmaAllocation hAllocation, void** ppData);
6653 void Unmap(VmaAllocation hAllocation);
6654
6655 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6656 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6657
6658 void FlushOrInvalidateAllocation(
6659 VmaAllocation hAllocation,
6660 VkDeviceSize offset, VkDeviceSize size,
6661 VMA_CACHE_OPERATION op);
6662
6663 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6664
6665private:
6666 VkDeviceSize m_PreferredLargeHeapBlockSize;
6667
6668 VkPhysicalDevice m_PhysicalDevice;
6669 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6670
6671 VMA_RW_MUTEX m_PoolsMutex;
6672 // Protected by m_PoolsMutex. Sorted by pointer value.
6673 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6674 uint32_t m_NextPoolId;
6675
6676 VmaVulkanFunctions m_VulkanFunctions;
6677
6678#if VMA_RECORDING_ENABLED
6679 VmaRecorder* m_pRecorder;
6680#endif
6681
6682 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6683
6684 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6685
6686 VkResult AllocateMemoryOfType(
6687 VkDeviceSize size,
6688 VkDeviceSize alignment,
6689 bool dedicatedAllocation,
6690 VkBuffer dedicatedBuffer,
6691 VkImage dedicatedImage,
6692 const VmaAllocationCreateInfo& createInfo,
6693 uint32_t memTypeIndex,
6694 VmaSuballocationType suballocType,
6695 size_t allocationCount,
6696 VmaAllocation* pAllocations);
6697
6698 // Helper function only to be used inside AllocateDedicatedMemory.
6699 VkResult AllocateDedicatedMemoryPage(
6700 VkDeviceSize size,
6701 VmaSuballocationType suballocType,
6702 uint32_t memTypeIndex,
6703 const VkMemoryAllocateInfo& allocInfo,
6704 bool map,
6705 bool isUserDataString,
6706 void* pUserData,
6707 VmaAllocation* pAllocation);
6708
6709 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6710 VkResult AllocateDedicatedMemory(
6711 VkDeviceSize size,
6712 VmaSuballocationType suballocType,
6713 uint32_t memTypeIndex,
6714 bool map,
6715 bool isUserDataString,
6716 void* pUserData,
6717 VkBuffer dedicatedBuffer,
6718 VkImage dedicatedImage,
6719 size_t allocationCount,
6720 VmaAllocation* pAllocations);
6721
6722 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6723 void FreeDedicatedMemory(VmaAllocation allocation);
6724};
6725
6726////////////////////////////////////////////////////////////////////////////////
6727// Memory allocation #2 after VmaAllocator_T definition
6728
6729static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6730{
6731 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6732}
6733
6734static void VmaFree(VmaAllocator hAllocator, void* ptr)
6735{
6736 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6737}
6738
6739template<typename T>
6740static T* VmaAllocate(VmaAllocator hAllocator)
6741{
6742 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6743}
6744
6745template<typename T>
6746static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6747{
6748 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6749}
6750
6751template<typename T>
6752static void vma_delete(VmaAllocator hAllocator, T* ptr)
6753{
6754 if(ptr != VMA_NULL)
6755 {
6756 ptr->~T();
6757 VmaFree(hAllocator, ptr);
6758 }
6759}
6760
6761template<typename T>
6762static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6763{
6764 if(ptr != VMA_NULL)
6765 {
6766 for(size_t i = count; i--; )
6767 ptr[i].~T();
6768 VmaFree(hAllocator, ptr);
6769 }
6770}
6771
6772////////////////////////////////////////////////////////////////////////////////
6773// VmaStringBuilder
6774
6775#if VMA_STATS_STRING_ENABLED
6776
6777class VmaStringBuilder
6778{
6779public:
6780 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
6781 size_t GetLength() const { return m_Data.size(); }
6782 const char* GetData() const { return m_Data.data(); }
6783
6784 void Add(char ch) { m_Data.push_back(ch); }
6785 void Add(const char* pStr);
6786 void AddNewLine() { Add('\n'); }
6787 void AddNumber(uint32_t num);
6788 void AddNumber(uint64_t num);
6789 void AddPointer(const void* ptr);
6790
6791private:
6792 VmaVector< char, VmaStlAllocator<char> > m_Data;
6793};
6794
6795void VmaStringBuilder::Add(const char* pStr)
6796{
6797 const size_t strLen = strlen(pStr);
6798 if(strLen > 0)
6799 {
6800 const size_t oldCount = m_Data.size();
6801 m_Data.resize(oldCount + strLen);
6802 memcpy(m_Data.data() + oldCount, pStr, strLen);
6803 }
6804}
6805
6806void VmaStringBuilder::AddNumber(uint32_t num)
6807{
6808 char buf[11];
6809 VmaUint32ToStr(buf, sizeof(buf), num);
6810 Add(buf);
6811}
6812
6813void VmaStringBuilder::AddNumber(uint64_t num)
6814{
6815 char buf[21];
6816 VmaUint64ToStr(buf, sizeof(buf), num);
6817 Add(buf);
6818}
6819
6820void VmaStringBuilder::AddPointer(const void* ptr)
6821{
6822 char buf[21];
6823 VmaPtrToStr(buf, sizeof(buf), ptr);
6824 Add(buf);
6825}
6826
6827#endif // #if VMA_STATS_STRING_ENABLED
6828
6829////////////////////////////////////////////////////////////////////////////////
6830// VmaJsonWriter
6831
6832#if VMA_STATS_STRING_ENABLED
6833
6834class VmaJsonWriter
6835{
6836 VMA_CLASS_NO_COPY(VmaJsonWriter)
6837public:
6838 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6839 ~VmaJsonWriter();
6840
6841 void BeginObject(bool singleLine = false);
6842 void EndObject();
6843
6844 void BeginArray(bool singleLine = false);
6845 void EndArray();
6846
6847 void WriteString(const char* pStr);
6848 void BeginString(const char* pStr = VMA_NULL);
6849 void ContinueString(const char* pStr);
6850 void ContinueString(uint32_t n);
6851 void ContinueString(uint64_t n);
6852 void ContinueString_Pointer(const void* ptr);
6853 void EndString(const char* pStr = VMA_NULL);
6854
6855 void WriteNumber(uint32_t n);
6856 void WriteNumber(uint64_t n);
6857 void WriteBool(bool b);
6858 void WriteNull();
6859
6860private:
6861 static const char* const INDENT;
6862
6863 enum COLLECTION_TYPE
6864 {
6865 COLLECTION_TYPE_OBJECT,
6866 COLLECTION_TYPE_ARRAY,
6867 };
6868 struct StackItem
6869 {
6870 COLLECTION_TYPE type;
6871 uint32_t valueCount;
6872 bool singleLineMode;
6873 };
6874
6875 VmaStringBuilder& m_SB;
6876 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6877 bool m_InsideString;
6878
6879 void BeginValue(bool isString);
6880 void WriteIndent(bool oneLess = false);
6881};
6882
6883const char* const VmaJsonWriter::INDENT = " ";
6884
6885VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6886 m_SB(sb),
6887 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6888 m_InsideString(false)
6889{
6890}
6891
6892VmaJsonWriter::~VmaJsonWriter()
6893{
6894 VMA_ASSERT(!m_InsideString);
6895 VMA_ASSERT(m_Stack.empty());
6896}
6897
6898void VmaJsonWriter::BeginObject(bool singleLine)
6899{
6900 VMA_ASSERT(!m_InsideString);
6901
6902 BeginValue(false);
6903 m_SB.Add('{');
6904
6905 StackItem item;
6906 item.type = COLLECTION_TYPE_OBJECT;
6907 item.valueCount = 0;
6908 item.singleLineMode = singleLine;
6909 m_Stack.push_back(item);
6910}
6911
6912void VmaJsonWriter::EndObject()
6913{
6914 VMA_ASSERT(!m_InsideString);
6915
6916 WriteIndent(true);
6917 m_SB.Add('}');
6918
6919 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6920 m_Stack.pop_back();
6921}
6922
6923void VmaJsonWriter::BeginArray(bool singleLine)
6924{
6925 VMA_ASSERT(!m_InsideString);
6926
6927 BeginValue(false);
6928 m_SB.Add('[');
6929
6930 StackItem item;
6931 item.type = COLLECTION_TYPE_ARRAY;
6932 item.valueCount = 0;
6933 item.singleLineMode = singleLine;
6934 m_Stack.push_back(item);
6935}
6936
6937void VmaJsonWriter::EndArray()
6938{
6939 VMA_ASSERT(!m_InsideString);
6940
6941 WriteIndent(true);
6942 m_SB.Add(']');
6943
6944 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6945 m_Stack.pop_back();
6946}
6947
6948void VmaJsonWriter::WriteString(const char* pStr)
6949{
6950 BeginString(pStr);
6951 EndString();
6952}
6953
6954void VmaJsonWriter::BeginString(const char* pStr)
6955{
6956 VMA_ASSERT(!m_InsideString);
6957
6958 BeginValue(true);
6959 m_SB.Add('"');
6960 m_InsideString = true;
6961 if(pStr != VMA_NULL && pStr[0] != '\0')
6962 {
6963 ContinueString(pStr);
6964 }
6965}
6966
6967void VmaJsonWriter::ContinueString(const char* pStr)
6968{
6969 VMA_ASSERT(m_InsideString);
6970
6971 const size_t strLen = strlen(pStr);
6972 for(size_t i = 0; i < strLen; ++i)
6973 {
6974 char ch = pStr[i];
6975 if(ch == '\\')
6976 {
6977 m_SB.Add("\\\\");
6978 }
6979 else if(ch == '"')
6980 {
6981 m_SB.Add("\\\"");
6982 }
6983 else if(ch >= 32)
6984 {
6985 m_SB.Add(ch);
6986 }
6987 else switch(ch)
6988 {
6989 case '\b':
6990 m_SB.Add("\\b");
6991 break;
6992 case '\f':
6993 m_SB.Add("\\f");
6994 break;
6995 case '\n':
6996 m_SB.Add("\\n");
6997 break;
6998 case '\r':
6999 m_SB.Add("\\r");
7000 break;
7001 case '\t':
7002 m_SB.Add("\\t");
7003 break;
7004 default:
7005 VMA_ASSERT(0 && "Character not currently supported.");
7006 break;
7007 }
7008 }
7009}
7010
7011void VmaJsonWriter::ContinueString(uint32_t n)
7012{
7013 VMA_ASSERT(m_InsideString);
7014 m_SB.AddNumber(n);
7015}
7016
7017void VmaJsonWriter::ContinueString(uint64_t n)
7018{
7019 VMA_ASSERT(m_InsideString);
7020 m_SB.AddNumber(n);
7021}
7022
7023void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7024{
7025 VMA_ASSERT(m_InsideString);
7026 m_SB.AddPointer(ptr);
7027}
7028
7029void VmaJsonWriter::EndString(const char* pStr)
7030{
7031 VMA_ASSERT(m_InsideString);
7032 if(pStr != VMA_NULL && pStr[0] != '\0')
7033 {
7034 ContinueString(pStr);
7035 }
7036 m_SB.Add('"');
7037 m_InsideString = false;
7038}
7039
7040void VmaJsonWriter::WriteNumber(uint32_t n)
7041{
7042 VMA_ASSERT(!m_InsideString);
7043 BeginValue(false);
7044 m_SB.AddNumber(n);
7045}
7046
7047void VmaJsonWriter::WriteNumber(uint64_t n)
7048{
7049 VMA_ASSERT(!m_InsideString);
7050 BeginValue(false);
7051 m_SB.AddNumber(n);
7052}
7053
7054void VmaJsonWriter::WriteBool(bool b)
7055{
7056 VMA_ASSERT(!m_InsideString);
7057 BeginValue(false);
7058 m_SB.Add(b ? "true" : "false");
7059}
7060
7061void VmaJsonWriter::WriteNull()
7062{
7063 VMA_ASSERT(!m_InsideString);
7064 BeginValue(false);
7065 m_SB.Add("null");
7066}
7067
7068void VmaJsonWriter::BeginValue(bool isString)
7069{
7070 if(!m_Stack.empty())
7071 {
7072 StackItem& currItem = m_Stack.back();
7073 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7074 currItem.valueCount % 2 == 0)
7075 {
7076 VMA_ASSERT(isString);
7077 }
7078
7079 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7080 currItem.valueCount % 2 != 0)
7081 {
7082 m_SB.Add(": ");
7083 }
7084 else if(currItem.valueCount > 0)
7085 {
7086 m_SB.Add(", ");
7087 WriteIndent();
7088 }
7089 else
7090 {
7091 WriteIndent();
7092 }
7093 ++currItem.valueCount;
7094 }
7095}
7096
7097void VmaJsonWriter::WriteIndent(bool oneLess)
7098{
7099 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7100 {
7101 m_SB.AddNewLine();
7102
7103 size_t count = m_Stack.size();
7104 if(count > 0 && oneLess)
7105 {
7106 --count;
7107 }
7108 for(size_t i = 0; i < count; ++i)
7109 {
7110 m_SB.Add(INDENT);
7111 }
7112 }
7113}
7114
7115#endif // #if VMA_STATS_STRING_ENABLED
7116
7117////////////////////////////////////////////////////////////////////////////////
7118
7119void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7120{
7121 if(IsUserDataString())
7122 {
7123 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7124
7125 FreeUserDataString(hAllocator);
7126
7127 if(pUserData != VMA_NULL)
7128 {
7129 const char* const newStrSrc = (char*)pUserData;
7130 const size_t newStrLen = strlen(newStrSrc);
7131 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7132 memcpy(newStrDst, newStrSrc, newStrLen + 1);
7133 m_pUserData = newStrDst;
7134 }
7135 }
7136 else
7137 {
7138 m_pUserData = pUserData;
7139 }
7140}
7141
7142void VmaAllocation_T::ChangeBlockAllocation(
7143 VmaAllocator hAllocator,
7144 VmaDeviceMemoryBlock* block,
7145 VkDeviceSize offset)
7146{
7147 VMA_ASSERT(block != VMA_NULL);
7148 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7149
7150 // Move mapping reference counter from old block to new block.
7151 if(block != m_BlockAllocation.m_Block)
7152 {
7153 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7154 if(IsPersistentMap())
7155 ++mapRefCount;
7156 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7157 block->Map(hAllocator, mapRefCount, VMA_NULL);
7158 }
7159
7160 m_BlockAllocation.m_Block = block;
7161 m_BlockAllocation.m_Offset = offset;
7162}
7163
7164void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7165{
7166 VMA_ASSERT(newSize > 0);
7167 m_Size = newSize;
7168}
7169
7170void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7171{
7172 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7173 m_BlockAllocation.m_Offset = newOffset;
7174}
7175
7176VkDeviceSize VmaAllocation_T::GetOffset() const
7177{
7178 switch(m_Type)
7179 {
7180 case ALLOCATION_TYPE_BLOCK:
7181 return m_BlockAllocation.m_Offset;
7182 case ALLOCATION_TYPE_DEDICATED:
7183 return 0;
7184 default:
7185 VMA_ASSERT(0);
7186 return 0;
7187 }
7188}
7189
7190VkDeviceMemory VmaAllocation_T::GetMemory() const
7191{
7192 switch(m_Type)
7193 {
7194 case ALLOCATION_TYPE_BLOCK:
7195 return m_BlockAllocation.m_Block->GetDeviceMemory();
7196 case ALLOCATION_TYPE_DEDICATED:
7197 return m_DedicatedAllocation.m_hMemory;
7198 default:
7199 VMA_ASSERT(0);
7200 return VK_NULL_HANDLE;
7201 }
7202}
7203
7204uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7205{
7206 switch(m_Type)
7207 {
7208 case ALLOCATION_TYPE_BLOCK:
7209 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7210 case ALLOCATION_TYPE_DEDICATED:
7211 return m_DedicatedAllocation.m_MemoryTypeIndex;
7212 default:
7213 VMA_ASSERT(0);
7214 return UINT32_MAX;
7215 }
7216}
7217
7218void* VmaAllocation_T::GetMappedData() const
7219{
7220 switch(m_Type)
7221 {
7222 case ALLOCATION_TYPE_BLOCK:
7223 if(m_MapCount != 0)
7224 {
7225 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7226 VMA_ASSERT(pBlockData != VMA_NULL);
7227 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7228 }
7229 else
7230 {
7231 return VMA_NULL;
7232 }
7233 break;
7234 case ALLOCATION_TYPE_DEDICATED:
7235 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7236 return m_DedicatedAllocation.m_pMappedData;
7237 default:
7238 VMA_ASSERT(0);
7239 return VMA_NULL;
7240 }
7241}
7242
7243bool VmaAllocation_T::CanBecomeLost() const
7244{
7245 switch(m_Type)
7246 {
7247 case ALLOCATION_TYPE_BLOCK:
7248 return m_BlockAllocation.m_CanBecomeLost;
7249 case ALLOCATION_TYPE_DEDICATED:
7250 return false;
7251 default:
7252 VMA_ASSERT(0);
7253 return false;
7254 }
7255}
7256
7257VmaPool VmaAllocation_T::GetPool() const
7258{
7259 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7260 return m_BlockAllocation.m_hPool;
7261}
7262
7263bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7264{
7265 VMA_ASSERT(CanBecomeLost());
7266
7267 /*
7268 Warning: This is a carefully designed algorithm.
7269 Do not modify unless you really know what you're doing :)
7270 */
7271 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7272 for(;;)
7273 {
7274 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7275 {
7276 VMA_ASSERT(0);
7277 return false;
7278 }
7279 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7280 {
7281 return false;
7282 }
7283 else // Last use time earlier than current time.
7284 {
7285 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7286 {
7287 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7288 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7289 return true;
7290 }
7291 }
7292 }
7293}
7294
7295#if VMA_STATS_STRING_ENABLED
7296
7297// Correspond to values of enum VmaSuballocationType.
7298static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7299 "FREE",
7300 "UNKNOWN",
7301 "BUFFER",
7302 "IMAGE_UNKNOWN",
7303 "IMAGE_LINEAR",
7304 "IMAGE_OPTIMAL",
7305};
7306
7307void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7308{
7309 json.WriteString("Type");
7310 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7311
7312 json.WriteString("Size");
7313 json.WriteNumber(m_Size);
7314
7315 if(m_pUserData != VMA_NULL)
7316 {
7317 json.WriteString("UserData");
7318 if(IsUserDataString())
7319 {
7320 json.WriteString((const char*)m_pUserData);
7321 }
7322 else
7323 {
7324 json.BeginString();
7325 json.ContinueString_Pointer(m_pUserData);
7326 json.EndString();
7327 }
7328 }
7329
7330 json.WriteString("CreationFrameIndex");
7331 json.WriteNumber(m_CreationFrameIndex);
7332
7333 json.WriteString("LastUseFrameIndex");
7334 json.WriteNumber(GetLastUseFrameIndex());
7335
7336 if(m_BufferImageUsage != 0)
7337 {
7338 json.WriteString("Usage");
7339 json.WriteNumber(m_BufferImageUsage);
7340 }
7341}
7342
7343#endif
7344
7345void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7346{
7347 VMA_ASSERT(IsUserDataString());
7348 if(m_pUserData != VMA_NULL)
7349 {
7350 char* const oldStr = (char*)m_pUserData;
7351 const size_t oldStrLen = strlen(oldStr);
7352 vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7353 m_pUserData = VMA_NULL;
7354 }
7355}
7356
7357void VmaAllocation_T::BlockAllocMap()
7358{
7359 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7360
7361 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7362 {
7363 ++m_MapCount;
7364 }
7365 else
7366 {
7367 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7368 }
7369}
7370
7371void VmaAllocation_T::BlockAllocUnmap()
7372{
7373 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7374
7375 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7376 {
7377 --m_MapCount;
7378 }
7379 else
7380 {
7381 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7382 }
7383}
7384
7385VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7386{
7387 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7388
7389 if(m_MapCount != 0)
7390 {
7391 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7392 {
7393 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7394 *ppData = m_DedicatedAllocation.m_pMappedData;
7395 ++m_MapCount;
7396 return VK_SUCCESS;
7397 }
7398 else
7399 {
7400 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7401 return VK_ERROR_MEMORY_MAP_FAILED;
7402 }
7403 }
7404 else
7405 {
7406 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7407 hAllocator->m_hDevice,
7408 m_DedicatedAllocation.m_hMemory,
7409 0, // offset
7410 VK_WHOLE_SIZE,
7411 0, // flags
7412 ppData);
7413 if(result == VK_SUCCESS)
7414 {
7415 m_DedicatedAllocation.m_pMappedData = *ppData;
7416 m_MapCount = 1;
7417 }
7418 return result;
7419 }
7420}
7421
7422void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7423{
7424 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7425
7426 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7427 {
7428 --m_MapCount;
7429 if(m_MapCount == 0)
7430 {
7431 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7432 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7433 hAllocator->m_hDevice,
7434 m_DedicatedAllocation.m_hMemory);
7435 }
7436 }
7437 else
7438 {
7439 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7440 }
7441}
7442
7443#if VMA_STATS_STRING_ENABLED
7444
7445static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7446{
7447 json.BeginObject();
7448
7449 json.WriteString("Blocks");
7450 json.WriteNumber(stat.blockCount);
7451
7452 json.WriteString("Allocations");
7453 json.WriteNumber(stat.allocationCount);
7454
7455 json.WriteString("UnusedRanges");
7456 json.WriteNumber(stat.unusedRangeCount);
7457
7458 json.WriteString("UsedBytes");
7459 json.WriteNumber(stat.usedBytes);
7460
7461 json.WriteString("UnusedBytes");
7462 json.WriteNumber(stat.unusedBytes);
7463
7464 if(stat.allocationCount > 1)
7465 {
7466 json.WriteString("AllocationSize");
7467 json.BeginObject(true);
7468 json.WriteString("Min");
7469 json.WriteNumber(stat.allocationSizeMin);
7470 json.WriteString("Avg");
7471 json.WriteNumber(stat.allocationSizeAvg);
7472 json.WriteString("Max");
7473 json.WriteNumber(stat.allocationSizeMax);
7474 json.EndObject();
7475 }
7476
7477 if(stat.unusedRangeCount > 1)
7478 {
7479 json.WriteString("UnusedRangeSize");
7480 json.BeginObject(true);
7481 json.WriteString("Min");
7482 json.WriteNumber(stat.unusedRangeSizeMin);
7483 json.WriteString("Avg");
7484 json.WriteNumber(stat.unusedRangeSizeAvg);
7485 json.WriteString("Max");
7486 json.WriteNumber(stat.unusedRangeSizeMax);
7487 json.EndObject();
7488 }
7489
7490 json.EndObject();
7491}
7492
7493#endif // #if VMA_STATS_STRING_ENABLED
7494
7495struct VmaSuballocationItemSizeLess
7496{
7497 bool operator()(
7498 const VmaSuballocationList::iterator lhs,
7499 const VmaSuballocationList::iterator rhs) const
7500 {
7501 return lhs->size < rhs->size;
7502 }
7503 bool operator()(
7504 const VmaSuballocationList::iterator lhs,
7505 VkDeviceSize rhsSize) const
7506 {
7507 return lhs->size < rhsSize;
7508 }
7509};
7510
7511
7512////////////////////////////////////////////////////////////////////////////////
7513// class VmaBlockMetadata
7514
7515VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7516 m_Size(0),
7517 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7518{
7519}
7520
7521#if VMA_STATS_STRING_ENABLED
7522
7523void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7524 VkDeviceSize unusedBytes,
7525 size_t allocationCount,
7526 size_t unusedRangeCount) const
7527{
7528 json.BeginObject();
7529
7530 json.WriteString("TotalBytes");
7531 json.WriteNumber(GetSize());
7532
7533 json.WriteString("UnusedBytes");
7534 json.WriteNumber(unusedBytes);
7535
7536 json.WriteString("Allocations");
7537 json.WriteNumber((uint64_t)allocationCount);
7538
7539 json.WriteString("UnusedRanges");
7540 json.WriteNumber((uint64_t)unusedRangeCount);
7541
7542 json.WriteString("Suballocations");
7543 json.BeginArray();
7544}
7545
7546void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7547 VkDeviceSize offset,
7548 VmaAllocation hAllocation) const
7549{
7550 json.BeginObject(true);
7551
7552 json.WriteString("Offset");
7553 json.WriteNumber(offset);
7554
7555 hAllocation->PrintParameters(json);
7556
7557 json.EndObject();
7558}
7559
7560void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7561 VkDeviceSize offset,
7562 VkDeviceSize size) const
7563{
7564 json.BeginObject(true);
7565
7566 json.WriteString("Offset");
7567 json.WriteNumber(offset);
7568
7569 json.WriteString("Type");
7570 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7571
7572 json.WriteString("Size");
7573 json.WriteNumber(size);
7574
7575 json.EndObject();
7576}
7577
7578void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7579{
7580 json.EndArray();
7581 json.EndObject();
7582}
7583
7584#endif // #if VMA_STATS_STRING_ENABLED
7585
7586////////////////////////////////////////////////////////////////////////////////
7587// class VmaBlockMetadata_Generic
7588
7589VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7590 VmaBlockMetadata(hAllocator),
7591 m_FreeCount(0),
7592 m_SumFreeSize(0),
7593 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7594 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7595{
7596}
7597
7598VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7599{
7600}
7601
7602void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7603{
7604 VmaBlockMetadata::Init(size);
7605
7606 m_FreeCount = 1;
7607 m_SumFreeSize = size;
7608
7609 VmaSuballocation suballoc = {};
7610 suballoc.offset = 0;
7611 suballoc.size = size;
7612 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7613 suballoc.hAllocation = VK_NULL_HANDLE;
7614
7615 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7616 m_Suballocations.push_back(suballoc);
7617 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7618 --suballocItem;
7619 m_FreeSuballocationsBySize.push_back(suballocItem);
7620}
7621
7622bool VmaBlockMetadata_Generic::Validate() const
7623{
7624 VMA_VALIDATE(!m_Suballocations.empty());
7625
7626 // Expected offset of new suballocation as calculated from previous ones.
7627 VkDeviceSize calculatedOffset = 0;
7628 // Expected number of free suballocations as calculated from traversing their list.
7629 uint32_t calculatedFreeCount = 0;
7630 // Expected sum size of free suballocations as calculated from traversing their list.
7631 VkDeviceSize calculatedSumFreeSize = 0;
7632 // Expected number of free suballocations that should be registered in
7633 // m_FreeSuballocationsBySize calculated from traversing their list.
7634 size_t freeSuballocationsToRegister = 0;
7635 // True if previous visited suballocation was free.
7636 bool prevFree = false;
7637
7638 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7639 suballocItem != m_Suballocations.cend();
7640 ++suballocItem)
7641 {
7642 const VmaSuballocation& subAlloc = *suballocItem;
7643
7644 // Actual offset of this suballocation doesn't match expected one.
7645 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7646
7647 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7648 // Two adjacent free suballocations are invalid. They should be merged.
7649 VMA_VALIDATE(!prevFree || !currFree);
7650
7651 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7652
7653 if(currFree)
7654 {
7655 calculatedSumFreeSize += subAlloc.size;
7656 ++calculatedFreeCount;
7657 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7658 {
7659 ++freeSuballocationsToRegister;
7660 }
7661
7662 // Margin required between allocations - every free space must be at least that large.
7663 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7664 }
7665 else
7666 {
7667 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7668 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7669
7670 // Margin required between allocations - previous allocation must be free.
7671 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7672 }
7673
7674 calculatedOffset += subAlloc.size;
7675 prevFree = currFree;
7676 }
7677
7678 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7679 // match expected one.
7680 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7681
7682 VkDeviceSize lastSize = 0;
7683 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7684 {
7685 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7686
7687 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7688 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7689 // They must be sorted by size ascending.
7690 VMA_VALIDATE(suballocItem->size >= lastSize);
7691
7692 lastSize = suballocItem->size;
7693 }
7694
7695 // Check if totals match calculacted values.
7696 VMA_VALIDATE(ValidateFreeSuballocationList());
7697 VMA_VALIDATE(calculatedOffset == GetSize());
7698 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7699 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7700
7701 return true;
7702}
7703
7704VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7705{
7706 if(!m_FreeSuballocationsBySize.empty())
7707 {
7708 return m_FreeSuballocationsBySize.back()->size;
7709 }
7710 else
7711 {
7712 return 0;
7713 }
7714}
7715
7716bool VmaBlockMetadata_Generic::IsEmpty() const
7717{
7718 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7719}
7720
7721void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7722{
7723 outInfo.blockCount = 1;
7724
7725 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7726 outInfo.allocationCount = rangeCount - m_FreeCount;
7727 outInfo.unusedRangeCount = m_FreeCount;
7728
7729 outInfo.unusedBytes = m_SumFreeSize;
7730 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7731
7732 outInfo.allocationSizeMin = UINT64_MAX;
7733 outInfo.allocationSizeMax = 0;
7734 outInfo.unusedRangeSizeMin = UINT64_MAX;
7735 outInfo.unusedRangeSizeMax = 0;
7736
7737 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7738 suballocItem != m_Suballocations.cend();
7739 ++suballocItem)
7740 {
7741 const VmaSuballocation& suballoc = *suballocItem;
7742 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7743 {
7744 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7745 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7746 }
7747 else
7748 {
7749 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7750 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7751 }
7752 }
7753}
7754
7755void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7756{
7757 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7758
7759 inoutStats.size += GetSize();
7760 inoutStats.unusedSize += m_SumFreeSize;
7761 inoutStats.allocationCount += rangeCount - m_FreeCount;
7762 inoutStats.unusedRangeCount += m_FreeCount;
7763 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7764}
7765
7766#if VMA_STATS_STRING_ENABLED
7767
7768void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7769{
7770 PrintDetailedMap_Begin(json,
7771 m_SumFreeSize, // unusedBytes
7772 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7773 m_FreeCount); // unusedRangeCount
7774
7775 size_t i = 0;
7776 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7777 suballocItem != m_Suballocations.cend();
7778 ++suballocItem, ++i)
7779 {
7780 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7781 {
7782 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7783 }
7784 else
7785 {
7786 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7787 }
7788 }
7789
7790 PrintDetailedMap_End(json);
7791}
7792
7793#endif // #if VMA_STATS_STRING_ENABLED
7794
7795bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7796 uint32_t currentFrameIndex,
7797 uint32_t frameInUseCount,
7798 VkDeviceSize bufferImageGranularity,
7799 VkDeviceSize allocSize,
7800 VkDeviceSize allocAlignment,
7801 bool upperAddress,
7802 VmaSuballocationType allocType,
7803 bool canMakeOtherLost,
7804 uint32_t strategy,
7805 VmaAllocationRequest* pAllocationRequest)
7806{
7807 VMA_ASSERT(allocSize > 0);
7808 VMA_ASSERT(!upperAddress);
7809 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7810 VMA_ASSERT(pAllocationRequest != VMA_NULL);
7811 VMA_HEAVY_ASSERT(Validate());
7812
7813 // There is not enough total free space in this block to fullfill the request: Early return.
7814 if(canMakeOtherLost == false &&
7815 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7816 {
7817 return false;
7818 }
7819
7820 // New algorithm, efficiently searching freeSuballocationsBySize.
7821 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7822 if(freeSuballocCount > 0)
7823 {
7824 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7825 {
7826 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7827 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7828 m_FreeSuballocationsBySize.data(),
7829 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7830 allocSize + 2 * VMA_DEBUG_MARGIN,
7831 VmaSuballocationItemSizeLess());
7832 size_t index = it - m_FreeSuballocationsBySize.data();
7833 for(; index < freeSuballocCount; ++index)
7834 {
7835 if(CheckAllocation(
7836 currentFrameIndex,
7837 frameInUseCount,
7838 bufferImageGranularity,
7839 allocSize,
7840 allocAlignment,
7841 allocType,
7842 m_FreeSuballocationsBySize[index],
7843 false, // canMakeOtherLost
7844 &pAllocationRequest->offset,
7845 &pAllocationRequest->itemsToMakeLostCount,
7846 &pAllocationRequest->sumFreeSize,
7847 &pAllocationRequest->sumItemSize))
7848 {
7849 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7850 return true;
7851 }
7852 }
7853 }
7854 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7855 {
7856 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7857 it != m_Suballocations.end();
7858 ++it)
7859 {
7860 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7861 currentFrameIndex,
7862 frameInUseCount,
7863 bufferImageGranularity,
7864 allocSize,
7865 allocAlignment,
7866 allocType,
7867 it,
7868 false, // canMakeOtherLost
7869 &pAllocationRequest->offset,
7870 &pAllocationRequest->itemsToMakeLostCount,
7871 &pAllocationRequest->sumFreeSize,
7872 &pAllocationRequest->sumItemSize))
7873 {
7874 pAllocationRequest->item = it;
7875 return true;
7876 }
7877 }
7878 }
7879 else // WORST_FIT, FIRST_FIT
7880 {
7881 // Search staring from biggest suballocations.
7882 for(size_t index = freeSuballocCount; index--; )
7883 {
7884 if(CheckAllocation(
7885 currentFrameIndex,
7886 frameInUseCount,
7887 bufferImageGranularity,
7888 allocSize,
7889 allocAlignment,
7890 allocType,
7891 m_FreeSuballocationsBySize[index],
7892 false, // canMakeOtherLost
7893 &pAllocationRequest->offset,
7894 &pAllocationRequest->itemsToMakeLostCount,
7895 &pAllocationRequest->sumFreeSize,
7896 &pAllocationRequest->sumItemSize))
7897 {
7898 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7899 return true;
7900 }
7901 }
7902 }
7903 }
7904
7905 if(canMakeOtherLost)
7906 {
7907 // Brute-force algorithm. TODO: Come up with something better.
7908
7909 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7910 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7911
7912 VmaAllocationRequest tmpAllocRequest = {};
7913 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7914 suballocIt != m_Suballocations.end();
7915 ++suballocIt)
7916 {
7917 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7918 suballocIt->hAllocation->CanBecomeLost())
7919 {
7920 if(CheckAllocation(
7921 currentFrameIndex,
7922 frameInUseCount,
7923 bufferImageGranularity,
7924 allocSize,
7925 allocAlignment,
7926 allocType,
7927 suballocIt,
7928 canMakeOtherLost,
7929 &tmpAllocRequest.offset,
7930 &tmpAllocRequest.itemsToMakeLostCount,
7931 &tmpAllocRequest.sumFreeSize,
7932 &tmpAllocRequest.sumItemSize))
7933 {
7934 tmpAllocRequest.item = suballocIt;
7935
7936 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7937 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7938 {
7939 *pAllocationRequest = tmpAllocRequest;
7940 }
7941 }
7942 }
7943 }
7944
7945 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7946 {
7947 return true;
7948 }
7949 }
7950
7951 return false;
7952}
7953
7954bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7955 uint32_t currentFrameIndex,
7956 uint32_t frameInUseCount,
7957 VmaAllocationRequest* pAllocationRequest)
7958{
7959 while(pAllocationRequest->itemsToMakeLostCount > 0)
7960 {
7961 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7962 {
7963 ++pAllocationRequest->item;
7964 }
7965 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7966 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
7967 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
7968 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7969 {
7970 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
7971 --pAllocationRequest->itemsToMakeLostCount;
7972 }
7973 else
7974 {
7975 return false;
7976 }
7977 }
7978
7979 VMA_HEAVY_ASSERT(Validate());
7980 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7981 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
7982
7983 return true;
7984}
7985
7986uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7987{
7988 uint32_t lostAllocationCount = 0;
7989 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7990 it != m_Suballocations.end();
7991 ++it)
7992 {
7993 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
7994 it->hAllocation->CanBecomeLost() &&
7995 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7996 {
7997 it = FreeSuballocation(it);
7998 ++lostAllocationCount;
7999 }
8000 }
8001 return lostAllocationCount;
8002}
8003
8004VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8005{
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 {
8012 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8013 {
8014 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8015 return VK_ERROR_VALIDATION_FAILED_EXT;
8016 }
8017 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8018 {
8019 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8020 return VK_ERROR_VALIDATION_FAILED_EXT;
8021 }
8022 }
8023 }
8024
8025 return VK_SUCCESS;
8026}
8027
8028void VmaBlockMetadata_Generic::Alloc(
8029 const VmaAllocationRequest& request,
8030 VmaSuballocationType type,
8031 VkDeviceSize allocSize,
8032 bool upperAddress,
8033 VmaAllocation hAllocation)
8034{
8035 VMA_ASSERT(!upperAddress);
8036 VMA_ASSERT(request.item != m_Suballocations.end());
8037 VmaSuballocation& suballoc = *request.item;
8038 // Given suballocation is a free block.
8039 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8040 // Given offset is inside this suballocation.
8041 VMA_ASSERT(request.offset >= suballoc.offset);
8042 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8043 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8044 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8045
8046 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8047 // it to become used.
8048 UnregisterFreeSuballocation(request.item);
8049
8050 suballoc.offset = request.offset;
8051 suballoc.size = allocSize;
8052 suballoc.type = type;
8053 suballoc.hAllocation = hAllocation;
8054
8055 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8056 if(paddingEnd)
8057 {
8058 VmaSuballocation paddingSuballoc = {};
8059 paddingSuballoc.offset = request.offset + allocSize;
8060 paddingSuballoc.size = paddingEnd;
8061 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8062 VmaSuballocationList::iterator next = request.item;
8063 ++next;
8064 const VmaSuballocationList::iterator paddingEndItem =
8065 m_Suballocations.insert(next, paddingSuballoc);
8066 RegisterFreeSuballocation(paddingEndItem);
8067 }
8068
8069 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8070 if(paddingBegin)
8071 {
8072 VmaSuballocation paddingSuballoc = {};
8073 paddingSuballoc.offset = request.offset - paddingBegin;
8074 paddingSuballoc.size = paddingBegin;
8075 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8076 const VmaSuballocationList::iterator paddingBeginItem =
8077 m_Suballocations.insert(request.item, paddingSuballoc);
8078 RegisterFreeSuballocation(paddingBeginItem);
8079 }
8080
8081 // Update totals.
8082 m_FreeCount = m_FreeCount - 1;
8083 if(paddingBegin > 0)
8084 {
8085 ++m_FreeCount;
8086 }
8087 if(paddingEnd > 0)
8088 {
8089 ++m_FreeCount;
8090 }
8091 m_SumFreeSize -= allocSize;
8092}
8093
8094void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8095{
8096 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8097 suballocItem != m_Suballocations.end();
8098 ++suballocItem)
8099 {
8100 VmaSuballocation& suballoc = *suballocItem;
8101 if(suballoc.hAllocation == allocation)
8102 {
8103 FreeSuballocation(suballocItem);
8104 VMA_HEAVY_ASSERT(Validate());
8105 return;
8106 }
8107 }
8108 VMA_ASSERT(0 && "Not found!");
8109}
8110
8111void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8112{
8113 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8114 suballocItem != m_Suballocations.end();
8115 ++suballocItem)
8116 {
8117 VmaSuballocation& suballoc = *suballocItem;
8118 if(suballoc.offset == offset)
8119 {
8120 FreeSuballocation(suballocItem);
8121 return;
8122 }
8123 }
8124 VMA_ASSERT(0 && "Not found!");
8125}
8126
8127bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8128{
8129 typedef VmaSuballocationList::iterator iter_type;
8130 for(iter_type suballocItem = m_Suballocations.begin();
8131 suballocItem != m_Suballocations.end();
8132 ++suballocItem)
8133 {
8134 VmaSuballocation& suballoc = *suballocItem;
8135 if(suballoc.hAllocation == alloc)
8136 {
8137 iter_type nextItem = suballocItem;
8138 ++nextItem;
8139
8140 // Should have been ensured on higher level.
8141 VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8142
8143 // Shrinking.
8144 if(newSize < alloc->GetSize())
8145 {
8146 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8147
8148 // There is next item.
8149 if(nextItem != m_Suballocations.end())
8150 {
8151 // Next item is free.
8152 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8153 {
8154 // Grow this next item backward.
8155 UnregisterFreeSuballocation(nextItem);
8156 nextItem->offset -= sizeDiff;
8157 nextItem->size += sizeDiff;
8158 RegisterFreeSuballocation(nextItem);
8159 }
8160 // Next item is not free.
8161 else
8162 {
8163 // Create free item after current one.
8164 VmaSuballocation newFreeSuballoc;
8165 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8166 newFreeSuballoc.offset = suballoc.offset + newSize;
8167 newFreeSuballoc.size = sizeDiff;
8168 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8169 iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8170 RegisterFreeSuballocation(newFreeSuballocIt);
8171
8172 ++m_FreeCount;
8173 }
8174 }
8175 // This is the last item.
8176 else
8177 {
8178 // Create free item at the end.
8179 VmaSuballocation newFreeSuballoc;
8180 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8181 newFreeSuballoc.offset = suballoc.offset + newSize;
8182 newFreeSuballoc.size = sizeDiff;
8183 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8184 m_Suballocations.push_back(newFreeSuballoc);
8185
8186 iter_type newFreeSuballocIt = m_Suballocations.end();
8187 RegisterFreeSuballocation(--newFreeSuballocIt);
8188
8189 ++m_FreeCount;
8190 }
8191
8192 suballoc.size = newSize;
8193 m_SumFreeSize += sizeDiff;
8194 }
8195 // Growing.
8196 else
8197 {
8198 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8199
8200 // There is next item.
8201 if(nextItem != m_Suballocations.end())
8202 {
8203 // Next item is free.
8204 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8205 {
8206 // There is not enough free space, including margin.
8207 if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8208 {
8209 return false;
8210 }
8211
8212 // There is more free space than required.
8213 if(nextItem->size > sizeDiff)
8214 {
8215 // Move and shrink this next item.
8216 UnregisterFreeSuballocation(nextItem);
8217 nextItem->offset += sizeDiff;
8218 nextItem->size -= sizeDiff;
8219 RegisterFreeSuballocation(nextItem);
8220 }
8221 // There is exactly the amount of free space required.
8222 else
8223 {
8224 // Remove this next free item.
8225 UnregisterFreeSuballocation(nextItem);
8226 m_Suballocations.erase(nextItem);
8227 --m_FreeCount;
8228 }
8229 }
8230 // Next item is not free - there is no space to grow.
8231 else
8232 {
8233 return false;
8234 }
8235 }
8236 // This is the last item - there is no space to grow.
8237 else
8238 {
8239 return false;
8240 }
8241
8242 suballoc.size = newSize;
8243 m_SumFreeSize -= sizeDiff;
8244 }
8245
8246 // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8247 return true;
8248 }
8249 }
8250 VMA_ASSERT(0 && "Not found!");
8251 return false;
8252}
8253
8254bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8255{
8256 VkDeviceSize lastSize = 0;
8257 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8258 {
8259 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8260
8261 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8262 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8263 VMA_VALIDATE(it->size >= lastSize);
8264 lastSize = it->size;
8265 }
8266 return true;
8267}
8268
8269bool VmaBlockMetadata_Generic::CheckAllocation(
8270 uint32_t currentFrameIndex,
8271 uint32_t frameInUseCount,
8272 VkDeviceSize bufferImageGranularity,
8273 VkDeviceSize allocSize,
8274 VkDeviceSize allocAlignment,
8275 VmaSuballocationType allocType,
8276 VmaSuballocationList::const_iterator suballocItem,
8277 bool canMakeOtherLost,
8278 VkDeviceSize* pOffset,
8279 size_t* itemsToMakeLostCount,
8280 VkDeviceSize* pSumFreeSize,
8281 VkDeviceSize* pSumItemSize) const
8282{
8283 VMA_ASSERT(allocSize > 0);
8284 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8285 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8286 VMA_ASSERT(pOffset != VMA_NULL);
8287
8288 *itemsToMakeLostCount = 0;
8289 *pSumFreeSize = 0;
8290 *pSumItemSize = 0;
8291
8292 if(canMakeOtherLost)
8293 {
8294 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8295 {
8296 *pSumFreeSize = suballocItem->size;
8297 }
8298 else
8299 {
8300 if(suballocItem->hAllocation->CanBecomeLost() &&
8301 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8302 {
8303 ++*itemsToMakeLostCount;
8304 *pSumItemSize = suballocItem->size;
8305 }
8306 else
8307 {
8308 return false;
8309 }
8310 }
8311
8312 // Remaining size is too small for this request: Early return.
8313 if(GetSize() - suballocItem->offset < allocSize)
8314 {
8315 return false;
8316 }
8317
8318 // Start from offset equal to beginning of this suballocation.
8319 *pOffset = suballocItem->offset;
8320
8321 // Apply VMA_DEBUG_MARGIN at the beginning.
8322 if(VMA_DEBUG_MARGIN > 0)
8323 {
8324 *pOffset += VMA_DEBUG_MARGIN;
8325 }
8326
8327 // Apply alignment.
8328 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8329
8330 // Check previous suballocations for BufferImageGranularity conflicts.
8331 // Make bigger alignment if necessary.
8332 if(bufferImageGranularity > 1)
8333 {
8334 bool bufferImageGranularityConflict = false;
8335 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8336 while(prevSuballocItem != m_Suballocations.cbegin())
8337 {
8338 --prevSuballocItem;
8339 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8340 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8341 {
8342 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8343 {
8344 bufferImageGranularityConflict = true;
8345 break;
8346 }
8347 }
8348 else
8349 // Already on previous page.
8350 break;
8351 }
8352 if(bufferImageGranularityConflict)
8353 {
8354 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8355 }
8356 }
8357
8358 // Now that we have final *pOffset, check if we are past suballocItem.
8359 // If yes, return false - this function should be called for another suballocItem as starting point.
8360 if(*pOffset >= suballocItem->offset + suballocItem->size)
8361 {
8362 return false;
8363 }
8364
8365 // Calculate padding at the beginning based on current offset.
8366 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8367
8368 // Calculate required margin at the end.
8369 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8370
8371 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8372 // Another early return check.
8373 if(suballocItem->offset + totalSize > GetSize())
8374 {
8375 return false;
8376 }
8377
8378 // Advance lastSuballocItem until desired size is reached.
8379 // Update itemsToMakeLostCount.
8380 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8381 if(totalSize > suballocItem->size)
8382 {
8383 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8384 while(remainingSize > 0)
8385 {
8386 ++lastSuballocItem;
8387 if(lastSuballocItem == m_Suballocations.cend())
8388 {
8389 return false;
8390 }
8391 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8392 {
8393 *pSumFreeSize += lastSuballocItem->size;
8394 }
8395 else
8396 {
8397 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8398 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8399 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8400 {
8401 ++*itemsToMakeLostCount;
8402 *pSumItemSize += lastSuballocItem->size;
8403 }
8404 else
8405 {
8406 return false;
8407 }
8408 }
8409 remainingSize = (lastSuballocItem->size < remainingSize) ?
8410 remainingSize - lastSuballocItem->size : 0;
8411 }
8412 }
8413
8414 // Check next suballocations for BufferImageGranularity conflicts.
8415 // If conflict exists, we must mark more allocations lost or fail.
8416 if(bufferImageGranularity > 1)
8417 {
8418 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8419 ++nextSuballocItem;
8420 while(nextSuballocItem != m_Suballocations.cend())
8421 {
8422 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8423 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8424 {
8425 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8426 {
8427 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8428 if(nextSuballoc.hAllocation->CanBecomeLost() &&
8429 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8430 {
8431 ++*itemsToMakeLostCount;
8432 }
8433 else
8434 {
8435 return false;
8436 }
8437 }
8438 }
8439 else
8440 {
8441 // Already on next page.
8442 break;
8443 }
8444 ++nextSuballocItem;
8445 }
8446 }
8447 }
8448 else
8449 {
8450 const VmaSuballocation& suballoc = *suballocItem;
8451 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8452
8453 *pSumFreeSize = suballoc.size;
8454
8455 // Size of this suballocation is too small for this request: Early return.
8456 if(suballoc.size < allocSize)
8457 {
8458 return false;
8459 }
8460
8461 // Start from offset equal to beginning of this suballocation.
8462 *pOffset = suballoc.offset;
8463
8464 // Apply VMA_DEBUG_MARGIN at the beginning.
8465 if(VMA_DEBUG_MARGIN > 0)
8466 {
8467 *pOffset += VMA_DEBUG_MARGIN;
8468 }
8469
8470 // Apply alignment.
8471 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8472
8473 // Check previous suballocations for BufferImageGranularity conflicts.
8474 // Make bigger alignment if necessary.
8475 if(bufferImageGranularity > 1)
8476 {
8477 bool bufferImageGranularityConflict = false;
8478 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8479 while(prevSuballocItem != m_Suballocations.cbegin())
8480 {
8481 --prevSuballocItem;
8482 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8483 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8484 {
8485 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8486 {
8487 bufferImageGranularityConflict = true;
8488 break;
8489 }
8490 }
8491 else
8492 // Already on previous page.
8493 break;
8494 }
8495 if(bufferImageGranularityConflict)
8496 {
8497 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8498 }
8499 }
8500
8501 // Calculate padding at the beginning based on current offset.
8502 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8503
8504 // Calculate required margin at the end.
8505 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8506
8507 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8508 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8509 {
8510 return false;
8511 }
8512
8513 // Check next suballocations for BufferImageGranularity conflicts.
8514 // If conflict exists, allocation cannot be made here.
8515 if(bufferImageGranularity > 1)
8516 {
8517 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8518 ++nextSuballocItem;
8519 while(nextSuballocItem != m_Suballocations.cend())
8520 {
8521 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8522 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8523 {
8524 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8525 {
8526 return false;
8527 }
8528 }
8529 else
8530 {
8531 // Already on next page.
8532 break;
8533 }
8534 ++nextSuballocItem;
8535 }
8536 }
8537 }
8538
8539 // All tests passed: Success. pOffset is already filled.
8540 return true;
8541}
8542
8543void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8544{
8545 VMA_ASSERT(item != m_Suballocations.end());
8546 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8547
8548 VmaSuballocationList::iterator nextItem = item;
8549 ++nextItem;
8550 VMA_ASSERT(nextItem != m_Suballocations.end());
8551 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8552
8553 item->size += nextItem->size;
8554 --m_FreeCount;
8555 m_Suballocations.erase(nextItem);
8556}
8557
8558VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8559{
8560 // Change this suballocation to be marked as free.
8561 VmaSuballocation& suballoc = *suballocItem;
8562 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8563 suballoc.hAllocation = VK_NULL_HANDLE;
8564
8565 // Update totals.
8566 ++m_FreeCount;
8567 m_SumFreeSize += suballoc.size;
8568
8569 // Merge with previous and/or next suballocation if it's also free.
8570 bool mergeWithNext = false;
8571 bool mergeWithPrev = false;
8572
8573 VmaSuballocationList::iterator nextItem = suballocItem;
8574 ++nextItem;
8575 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8576 {
8577 mergeWithNext = true;
8578 }
8579
8580 VmaSuballocationList::iterator prevItem = suballocItem;
8581 if(suballocItem != m_Suballocations.begin())
8582 {
8583 --prevItem;
8584 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8585 {
8586 mergeWithPrev = true;
8587 }
8588 }
8589
8590 if(mergeWithNext)
8591 {
8592 UnregisterFreeSuballocation(nextItem);
8593 MergeFreeWithNext(suballocItem);
8594 }
8595
8596 if(mergeWithPrev)
8597 {
8598 UnregisterFreeSuballocation(prevItem);
8599 MergeFreeWithNext(prevItem);
8600 RegisterFreeSuballocation(prevItem);
8601 return prevItem;
8602 }
8603 else
8604 {
8605 RegisterFreeSuballocation(suballocItem);
8606 return suballocItem;
8607 }
8608}
8609
8610void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8611{
8612 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8613 VMA_ASSERT(item->size > 0);
8614
8615 // You may want to enable this validation at the beginning or at the end of
8616 // this function, depending on what do you want to check.
8617 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8618
8619 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8620 {
8621 if(m_FreeSuballocationsBySize.empty())
8622 {
8623 m_FreeSuballocationsBySize.push_back(item);
8624 }
8625 else
8626 {
8627 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8628 }
8629 }
8630
8631 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8632}
8633
8634
8635void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8636{
8637 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8638 VMA_ASSERT(item->size > 0);
8639
8640 // You may want to enable this validation at the beginning or at the end of
8641 // this function, depending on what do you want to check.
8642 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8643
8644 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8645 {
8646 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8647 m_FreeSuballocationsBySize.data(),
8648 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8649 item,
8650 VmaSuballocationItemSizeLess());
8651 for(size_t index = it - m_FreeSuballocationsBySize.data();
8652 index < m_FreeSuballocationsBySize.size();
8653 ++index)
8654 {
8655 if(m_FreeSuballocationsBySize[index] == item)
8656 {
8657 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8658 return;
8659 }
8660 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8661 }
8662 VMA_ASSERT(0 && "Not found.");
8663 }
8664
8665 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8666}
8667
8668bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8669 VkDeviceSize bufferImageGranularity,
8670 VmaSuballocationType& inOutPrevSuballocType) const
8671{
8672 if(bufferImageGranularity == 1 || IsEmpty())
8673 {
8674 return false;
8675 }
8676
8677 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8678 bool typeConflictFound = false;
8679 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8680 it != m_Suballocations.cend();
8681 ++it)
8682 {
8683 const VmaSuballocationType suballocType = it->type;
8684 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8685 {
8686 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8687 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8688 {
8689 typeConflictFound = true;
8690 }
8691 inOutPrevSuballocType = suballocType;
8692 }
8693 }
8694
8695 return typeConflictFound || minAlignment >= bufferImageGranularity;
8696}
8697
8698////////////////////////////////////////////////////////////////////////////////
8699// class VmaBlockMetadata_Linear
8700
8701VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8702 VmaBlockMetadata(hAllocator),
8703 m_SumFreeSize(0),
8704 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8705 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8706 m_1stVectorIndex(0),
8707 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8708 m_1stNullItemsBeginCount(0),
8709 m_1stNullItemsMiddleCount(0),
8710 m_2ndNullItemsCount(0)
8711{
8712}
8713
8714VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8715{
8716}
8717
8718void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8719{
8720 VmaBlockMetadata::Init(size);
8721 m_SumFreeSize = size;
8722}
8723
8724bool VmaBlockMetadata_Linear::Validate() const
8725{
8726 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8727 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8728
8729 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8730 VMA_VALIDATE(!suballocations1st.empty() ||
8731 suballocations2nd.empty() ||
8732 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8733
8734 if(!suballocations1st.empty())
8735 {
8736 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8737 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8738 // Null item at the end should be just pop_back().
8739 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8740 }
8741 if(!suballocations2nd.empty())
8742 {
8743 // Null item at the end should be just pop_back().
8744 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8745 }
8746
8747 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8748 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8749
8750 VkDeviceSize sumUsedSize = 0;
8751 const size_t suballoc1stCount = suballocations1st.size();
8752 VkDeviceSize offset = VMA_DEBUG_MARGIN;
8753
8754 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8755 {
8756 const size_t suballoc2ndCount = suballocations2nd.size();
8757 size_t nullItem2ndCount = 0;
8758 for(size_t i = 0; i < suballoc2ndCount; ++i)
8759 {
8760 const VmaSuballocation& suballoc = suballocations2nd[i];
8761 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8762
8763 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8764 VMA_VALIDATE(suballoc.offset >= offset);
8765
8766 if(!currFree)
8767 {
8768 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8769 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8770 sumUsedSize += suballoc.size;
8771 }
8772 else
8773 {
8774 ++nullItem2ndCount;
8775 }
8776
8777 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8778 }
8779
8780 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8781 }
8782
8783 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8784 {
8785 const VmaSuballocation& suballoc = suballocations1st[i];
8786 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8787 suballoc.hAllocation == VK_NULL_HANDLE);
8788 }
8789
8790 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8791
8792 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8793 {
8794 const VmaSuballocation& suballoc = suballocations1st[i];
8795 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8796
8797 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8798 VMA_VALIDATE(suballoc.offset >= offset);
8799 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8800
8801 if(!currFree)
8802 {
8803 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8804 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8805 sumUsedSize += suballoc.size;
8806 }
8807 else
8808 {
8809 ++nullItem1stCount;
8810 }
8811
8812 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8813 }
8814 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8815
8816 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8817 {
8818 const size_t suballoc2ndCount = suballocations2nd.size();
8819 size_t nullItem2ndCount = 0;
8820 for(size_t i = suballoc2ndCount; i--; )
8821 {
8822 const VmaSuballocation& suballoc = suballocations2nd[i];
8823 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8824
8825 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8826 VMA_VALIDATE(suballoc.offset >= offset);
8827
8828 if(!currFree)
8829 {
8830 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8831 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8832 sumUsedSize += suballoc.size;
8833 }
8834 else
8835 {
8836 ++nullItem2ndCount;
8837 }
8838
8839 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8840 }
8841
8842 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8843 }
8844
8845 VMA_VALIDATE(offset <= GetSize());
8846 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8847
8848 return true;
8849}
8850
8851size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8852{
8853 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8854 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8855}
8856
8857VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8858{
8859 const VkDeviceSize size = GetSize();
8860
8861 /*
8862 We don't consider gaps inside allocation vectors with freed allocations because
8863 they are not suitable for reuse in linear allocator. We consider only space that
8864 is available for new allocations.
8865 */
8866 if(IsEmpty())
8867 {
8868 return size;
8869 }
8870
8871 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8872
8873 switch(m_2ndVectorMode)
8874 {
8875 case SECOND_VECTOR_EMPTY:
8876 /*
8877 Available space is after end of 1st, as well as before beginning of 1st (which
8878 whould make it a ring buffer).
8879 */
8880 {
8881 const size_t suballocations1stCount = suballocations1st.size();
8882 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8883 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8884 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8885 return VMA_MAX(
8886 firstSuballoc.offset,
8887 size - (lastSuballoc.offset + lastSuballoc.size));
8888 }
8889 break;
8890
8891 case SECOND_VECTOR_RING_BUFFER:
8892 /*
8893 Available space is only between end of 2nd and beginning of 1st.
8894 */
8895 {
8896 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8897 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8898 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8899 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8900 }
8901 break;
8902
8903 case SECOND_VECTOR_DOUBLE_STACK:
8904 /*
8905 Available space is only between end of 1st and top of 2nd.
8906 */
8907 {
8908 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8909 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8910 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8911 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8912 }
8913 break;
8914
8915 default:
8916 VMA_ASSERT(0);
8917 return 0;
8918 }
8919}
8920
8921void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8922{
8923 const VkDeviceSize size = GetSize();
8924 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8925 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8926 const size_t suballoc1stCount = suballocations1st.size();
8927 const size_t suballoc2ndCount = suballocations2nd.size();
8928
8929 outInfo.blockCount = 1;
8930 outInfo.allocationCount = (uint32_t)GetAllocationCount();
8931 outInfo.unusedRangeCount = 0;
8932 outInfo.usedBytes = 0;
8933 outInfo.allocationSizeMin = UINT64_MAX;
8934 outInfo.allocationSizeMax = 0;
8935 outInfo.unusedRangeSizeMin = UINT64_MAX;
8936 outInfo.unusedRangeSizeMax = 0;
8937
8938 VkDeviceSize lastOffset = 0;
8939
8940 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8941 {
8942 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8943 size_t nextAlloc2ndIndex = 0;
8944 while(lastOffset < freeSpace2ndTo1stEnd)
8945 {
8946 // Find next non-null allocation or move nextAllocIndex to the end.
8947 while(nextAlloc2ndIndex < suballoc2ndCount &&
8948 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8949 {
8950 ++nextAlloc2ndIndex;
8951 }
8952
8953 // Found non-null allocation.
8954 if(nextAlloc2ndIndex < suballoc2ndCount)
8955 {
8956 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8957
8958 // 1. Process free space before this allocation.
8959 if(lastOffset < suballoc.offset)
8960 {
8961 // There is free space from lastOffset to suballoc.offset.
8962 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8963 ++outInfo.unusedRangeCount;
8964 outInfo.unusedBytes += unusedRangeSize;
8965 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8966 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8967 }
8968
8969 // 2. Process this allocation.
8970 // There is allocation with suballoc.offset, suballoc.size.
8971 outInfo.usedBytes += suballoc.size;
8972 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8973 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
8974
8975 // 3. Prepare for next iteration.
8976 lastOffset = suballoc.offset + suballoc.size;
8977 ++nextAlloc2ndIndex;
8978 }
8979 // We are at the end.
8980 else
8981 {
8982 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8983 if(lastOffset < freeSpace2ndTo1stEnd)
8984 {
8985 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
8986 ++outInfo.unusedRangeCount;
8987 outInfo.unusedBytes += unusedRangeSize;
8988 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8989 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8990 }
8991
8992 // End of loop.
8993 lastOffset = freeSpace2ndTo1stEnd;
8994 }
8995 }
8996 }
8997
8998 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
8999 const VkDeviceSize freeSpace1stTo2ndEnd =
9000 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9001 while(lastOffset < freeSpace1stTo2ndEnd)
9002 {
9003 // Find next non-null allocation or move nextAllocIndex to the end.
9004 while(nextAlloc1stIndex < suballoc1stCount &&
9005 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9006 {
9007 ++nextAlloc1stIndex;
9008 }
9009
9010 // Found non-null allocation.
9011 if(nextAlloc1stIndex < suballoc1stCount)
9012 {
9013 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9014
9015 // 1. Process free space before this allocation.
9016 if(lastOffset < suballoc.offset)
9017 {
9018 // There is free space from lastOffset to suballoc.offset.
9019 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9020 ++outInfo.unusedRangeCount;
9021 outInfo.unusedBytes += unusedRangeSize;
9022 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9023 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9024 }
9025
9026 // 2. Process this allocation.
9027 // There is allocation with suballoc.offset, suballoc.size.
9028 outInfo.usedBytes += suballoc.size;
9029 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9030 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9031
9032 // 3. Prepare for next iteration.
9033 lastOffset = suballoc.offset + suballoc.size;
9034 ++nextAlloc1stIndex;
9035 }
9036 // We are at the end.
9037 else
9038 {
9039 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9040 if(lastOffset < freeSpace1stTo2ndEnd)
9041 {
9042 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9043 ++outInfo.unusedRangeCount;
9044 outInfo.unusedBytes += unusedRangeSize;
9045 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9046 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9047 }
9048
9049 // End of loop.
9050 lastOffset = freeSpace1stTo2ndEnd;
9051 }
9052 }
9053
9054 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9055 {
9056 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9057 while(lastOffset < size)
9058 {
9059 // Find next non-null allocation or move nextAllocIndex to the end.
9060 while(nextAlloc2ndIndex != SIZE_MAX &&
9061 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9062 {
9063 --nextAlloc2ndIndex;
9064 }
9065
9066 // Found non-null allocation.
9067 if(nextAlloc2ndIndex != SIZE_MAX)
9068 {
9069 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9070
9071 // 1. Process free space before this allocation.
9072 if(lastOffset < suballoc.offset)
9073 {
9074 // There is free space from lastOffset to suballoc.offset.
9075 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9076 ++outInfo.unusedRangeCount;
9077 outInfo.unusedBytes += unusedRangeSize;
9078 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9079 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9080 }
9081
9082 // 2. Process this allocation.
9083 // There is allocation with suballoc.offset, suballoc.size.
9084 outInfo.usedBytes += suballoc.size;
9085 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9086 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9087
9088 // 3. Prepare for next iteration.
9089 lastOffset = suballoc.offset + suballoc.size;
9090 --nextAlloc2ndIndex;
9091 }
9092 // We are at the end.
9093 else
9094 {
9095 // There is free space from lastOffset to size.
9096 if(lastOffset < size)
9097 {
9098 const VkDeviceSize unusedRangeSize = size - lastOffset;
9099 ++outInfo.unusedRangeCount;
9100 outInfo.unusedBytes += unusedRangeSize;
9101 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9102 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9103 }
9104
9105 // End of loop.
9106 lastOffset = size;
9107 }
9108 }
9109 }
9110
9111 outInfo.unusedBytes = size - outInfo.usedBytes;
9112}
9113
9114void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9115{
9116 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9117 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9118 const VkDeviceSize size = GetSize();
9119 const size_t suballoc1stCount = suballocations1st.size();
9120 const size_t suballoc2ndCount = suballocations2nd.size();
9121
9122 inoutStats.size += size;
9123
9124 VkDeviceSize lastOffset = 0;
9125
9126 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9127 {
9128 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9129 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9130 while(lastOffset < freeSpace2ndTo1stEnd)
9131 {
9132 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9133 while(nextAlloc2ndIndex < suballoc2ndCount &&
9134 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9135 {
9136 ++nextAlloc2ndIndex;
9137 }
9138
9139 // Found non-null allocation.
9140 if(nextAlloc2ndIndex < suballoc2ndCount)
9141 {
9142 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9143
9144 // 1. Process free space before this allocation.
9145 if(lastOffset < suballoc.offset)
9146 {
9147 // There is free space from lastOffset to suballoc.offset.
9148 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9149 inoutStats.unusedSize += unusedRangeSize;
9150 ++inoutStats.unusedRangeCount;
9151 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9152 }
9153
9154 // 2. Process this allocation.
9155 // There is allocation with suballoc.offset, suballoc.size.
9156 ++inoutStats.allocationCount;
9157
9158 // 3. Prepare for next iteration.
9159 lastOffset = suballoc.offset + suballoc.size;
9160 ++nextAlloc2ndIndex;
9161 }
9162 // We are at the end.
9163 else
9164 {
9165 if(lastOffset < freeSpace2ndTo1stEnd)
9166 {
9167 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9168 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9169 inoutStats.unusedSize += unusedRangeSize;
9170 ++inoutStats.unusedRangeCount;
9171 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9172 }
9173
9174 // End of loop.
9175 lastOffset = freeSpace2ndTo1stEnd;
9176 }
9177 }
9178 }
9179
9180 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9181 const VkDeviceSize freeSpace1stTo2ndEnd =
9182 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9183 while(lastOffset < freeSpace1stTo2ndEnd)
9184 {
9185 // Find next non-null allocation or move nextAllocIndex to the end.
9186 while(nextAlloc1stIndex < suballoc1stCount &&
9187 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9188 {
9189 ++nextAlloc1stIndex;
9190 }
9191
9192 // Found non-null allocation.
9193 if(nextAlloc1stIndex < suballoc1stCount)
9194 {
9195 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9196
9197 // 1. Process free space before this allocation.
9198 if(lastOffset < suballoc.offset)
9199 {
9200 // There is free space from lastOffset to suballoc.offset.
9201 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9202 inoutStats.unusedSize += unusedRangeSize;
9203 ++inoutStats.unusedRangeCount;
9204 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9205 }
9206
9207 // 2. Process this allocation.
9208 // There is allocation with suballoc.offset, suballoc.size.
9209 ++inoutStats.allocationCount;
9210
9211 // 3. Prepare for next iteration.
9212 lastOffset = suballoc.offset + suballoc.size;
9213 ++nextAlloc1stIndex;
9214 }
9215 // We are at the end.
9216 else
9217 {
9218 if(lastOffset < freeSpace1stTo2ndEnd)
9219 {
9220 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9221 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9222 inoutStats.unusedSize += unusedRangeSize;
9223 ++inoutStats.unusedRangeCount;
9224 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9225 }
9226
9227 // End of loop.
9228 lastOffset = freeSpace1stTo2ndEnd;
9229 }
9230 }
9231
9232 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9233 {
9234 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9235 while(lastOffset < size)
9236 {
9237 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9238 while(nextAlloc2ndIndex != SIZE_MAX &&
9239 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9240 {
9241 --nextAlloc2ndIndex;
9242 }
9243
9244 // Found non-null allocation.
9245 if(nextAlloc2ndIndex != SIZE_MAX)
9246 {
9247 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9248
9249 // 1. Process free space before this allocation.
9250 if(lastOffset < suballoc.offset)
9251 {
9252 // There is free space from lastOffset to suballoc.offset.
9253 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9254 inoutStats.unusedSize += unusedRangeSize;
9255 ++inoutStats.unusedRangeCount;
9256 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9257 }
9258
9259 // 2. Process this allocation.
9260 // There is allocation with suballoc.offset, suballoc.size.
9261 ++inoutStats.allocationCount;
9262
9263 // 3. Prepare for next iteration.
9264 lastOffset = suballoc.offset + suballoc.size;
9265 --nextAlloc2ndIndex;
9266 }
9267 // We are at the end.
9268 else
9269 {
9270 if(lastOffset < size)
9271 {
9272 // There is free space from lastOffset to size.
9273 const VkDeviceSize unusedRangeSize = size - lastOffset;
9274 inoutStats.unusedSize += unusedRangeSize;
9275 ++inoutStats.unusedRangeCount;
9276 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9277 }
9278
9279 // End of loop.
9280 lastOffset = size;
9281 }
9282 }
9283 }
9284}
9285
9286#if VMA_STATS_STRING_ENABLED
9287void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9288{
9289 const VkDeviceSize size = GetSize();
9290 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9291 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9292 const size_t suballoc1stCount = suballocations1st.size();
9293 const size_t suballoc2ndCount = suballocations2nd.size();
9294
9295 // FIRST PASS
9296
9297 size_t unusedRangeCount = 0;
9298 VkDeviceSize usedBytes = 0;
9299
9300 VkDeviceSize lastOffset = 0;
9301
9302 size_t alloc2ndCount = 0;
9303 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9304 {
9305 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9306 size_t nextAlloc2ndIndex = 0;
9307 while(lastOffset < freeSpace2ndTo1stEnd)
9308 {
9309 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9310 while(nextAlloc2ndIndex < suballoc2ndCount &&
9311 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9312 {
9313 ++nextAlloc2ndIndex;
9314 }
9315
9316 // Found non-null allocation.
9317 if(nextAlloc2ndIndex < suballoc2ndCount)
9318 {
9319 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9320
9321 // 1. Process free space before this allocation.
9322 if(lastOffset < suballoc.offset)
9323 {
9324 // There is free space from lastOffset to suballoc.offset.
9325 ++unusedRangeCount;
9326 }
9327
9328 // 2. Process this allocation.
9329 // There is allocation with suballoc.offset, suballoc.size.
9330 ++alloc2ndCount;
9331 usedBytes += suballoc.size;
9332
9333 // 3. Prepare for next iteration.
9334 lastOffset = suballoc.offset + suballoc.size;
9335 ++nextAlloc2ndIndex;
9336 }
9337 // We are at the end.
9338 else
9339 {
9340 if(lastOffset < freeSpace2ndTo1stEnd)
9341 {
9342 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9343 ++unusedRangeCount;
9344 }
9345
9346 // End of loop.
9347 lastOffset = freeSpace2ndTo1stEnd;
9348 }
9349 }
9350 }
9351
9352 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9353 size_t alloc1stCount = 0;
9354 const VkDeviceSize freeSpace1stTo2ndEnd =
9355 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9356 while(lastOffset < freeSpace1stTo2ndEnd)
9357 {
9358 // Find next non-null allocation or move nextAllocIndex to the end.
9359 while(nextAlloc1stIndex < suballoc1stCount &&
9360 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9361 {
9362 ++nextAlloc1stIndex;
9363 }
9364
9365 // Found non-null allocation.
9366 if(nextAlloc1stIndex < suballoc1stCount)
9367 {
9368 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9369
9370 // 1. Process free space before this allocation.
9371 if(lastOffset < suballoc.offset)
9372 {
9373 // There is free space from lastOffset to suballoc.offset.
9374 ++unusedRangeCount;
9375 }
9376
9377 // 2. Process this allocation.
9378 // There is allocation with suballoc.offset, suballoc.size.
9379 ++alloc1stCount;
9380 usedBytes += suballoc.size;
9381
9382 // 3. Prepare for next iteration.
9383 lastOffset = suballoc.offset + suballoc.size;
9384 ++nextAlloc1stIndex;
9385 }
9386 // We are at the end.
9387 else
9388 {
9389 if(lastOffset < size)
9390 {
9391 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9392 ++unusedRangeCount;
9393 }
9394
9395 // End of loop.
9396 lastOffset = freeSpace1stTo2ndEnd;
9397 }
9398 }
9399
9400 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9401 {
9402 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9403 while(lastOffset < size)
9404 {
9405 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9406 while(nextAlloc2ndIndex != SIZE_MAX &&
9407 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9408 {
9409 --nextAlloc2ndIndex;
9410 }
9411
9412 // Found non-null allocation.
9413 if(nextAlloc2ndIndex != SIZE_MAX)
9414 {
9415 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9416
9417 // 1. Process free space before this allocation.
9418 if(lastOffset < suballoc.offset)
9419 {
9420 // There is free space from lastOffset to suballoc.offset.
9421 ++unusedRangeCount;
9422 }
9423
9424 // 2. Process this allocation.
9425 // There is allocation with suballoc.offset, suballoc.size.
9426 ++alloc2ndCount;
9427 usedBytes += suballoc.size;
9428
9429 // 3. Prepare for next iteration.
9430 lastOffset = suballoc.offset + suballoc.size;
9431 --nextAlloc2ndIndex;
9432 }
9433 // We are at the end.
9434 else
9435 {
9436 if(lastOffset < size)
9437 {
9438 // There is free space from lastOffset to size.
9439 ++unusedRangeCount;
9440 }
9441
9442 // End of loop.
9443 lastOffset = size;
9444 }
9445 }
9446 }
9447
9448 const VkDeviceSize unusedBytes = size - usedBytes;
9449 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9450
9451 // SECOND PASS
9452 lastOffset = 0;
9453
9454 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9455 {
9456 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9457 size_t nextAlloc2ndIndex = 0;
9458 while(lastOffset < freeSpace2ndTo1stEnd)
9459 {
9460 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9461 while(nextAlloc2ndIndex < suballoc2ndCount &&
9462 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9463 {
9464 ++nextAlloc2ndIndex;
9465 }
9466
9467 // Found non-null allocation.
9468 if(nextAlloc2ndIndex < suballoc2ndCount)
9469 {
9470 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9471
9472 // 1. Process free space before this allocation.
9473 if(lastOffset < suballoc.offset)
9474 {
9475 // There is free space from lastOffset to suballoc.offset.
9476 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9477 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9478 }
9479
9480 // 2. Process this allocation.
9481 // There is allocation with suballoc.offset, suballoc.size.
9482 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9483
9484 // 3. Prepare for next iteration.
9485 lastOffset = suballoc.offset + suballoc.size;
9486 ++nextAlloc2ndIndex;
9487 }
9488 // We are at the end.
9489 else
9490 {
9491 if(lastOffset < freeSpace2ndTo1stEnd)
9492 {
9493 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9494 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9495 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9496 }
9497
9498 // End of loop.
9499 lastOffset = freeSpace2ndTo1stEnd;
9500 }
9501 }
9502 }
9503
9504 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9505 while(lastOffset < freeSpace1stTo2ndEnd)
9506 {
9507 // Find next non-null allocation or move nextAllocIndex to the end.
9508 while(nextAlloc1stIndex < suballoc1stCount &&
9509 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9510 {
9511 ++nextAlloc1stIndex;
9512 }
9513
9514 // Found non-null allocation.
9515 if(nextAlloc1stIndex < suballoc1stCount)
9516 {
9517 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9518
9519 // 1. Process free space before this allocation.
9520 if(lastOffset < suballoc.offset)
9521 {
9522 // There is free space from lastOffset to suballoc.offset.
9523 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9524 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9525 }
9526
9527 // 2. Process this allocation.
9528 // There is allocation with suballoc.offset, suballoc.size.
9529 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9530
9531 // 3. Prepare for next iteration.
9532 lastOffset = suballoc.offset + suballoc.size;
9533 ++nextAlloc1stIndex;
9534 }
9535 // We are at the end.
9536 else
9537 {
9538 if(lastOffset < freeSpace1stTo2ndEnd)
9539 {
9540 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9541 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9542 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9543 }
9544
9545 // End of loop.
9546 lastOffset = freeSpace1stTo2ndEnd;
9547 }
9548 }
9549
9550 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9551 {
9552 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9553 while(lastOffset < size)
9554 {
9555 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9556 while(nextAlloc2ndIndex != SIZE_MAX &&
9557 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9558 {
9559 --nextAlloc2ndIndex;
9560 }
9561
9562 // Found non-null allocation.
9563 if(nextAlloc2ndIndex != SIZE_MAX)
9564 {
9565 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9566
9567 // 1. Process free space before this allocation.
9568 if(lastOffset < suballoc.offset)
9569 {
9570 // There is free space from lastOffset to suballoc.offset.
9571 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9572 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9573 }
9574
9575 // 2. Process this allocation.
9576 // There is allocation with suballoc.offset, suballoc.size.
9577 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9578
9579 // 3. Prepare for next iteration.
9580 lastOffset = suballoc.offset + suballoc.size;
9581 --nextAlloc2ndIndex;
9582 }
9583 // We are at the end.
9584 else
9585 {
9586 if(lastOffset < size)
9587 {
9588 // There is free space from lastOffset to size.
9589 const VkDeviceSize unusedRangeSize = size - lastOffset;
9590 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9591 }
9592
9593 // End of loop.
9594 lastOffset = size;
9595 }
9596 }
9597 }
9598
9599 PrintDetailedMap_End(json);
9600}
9601#endif // #if VMA_STATS_STRING_ENABLED
9602
9603bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9604 uint32_t currentFrameIndex,
9605 uint32_t frameInUseCount,
9606 VkDeviceSize bufferImageGranularity,
9607 VkDeviceSize allocSize,
9608 VkDeviceSize allocAlignment,
9609 bool upperAddress,
9610 VmaSuballocationType allocType,
9611 bool canMakeOtherLost,
9612 uint32_t strategy,
9613 VmaAllocationRequest* pAllocationRequest)
9614{
9615 VMA_ASSERT(allocSize > 0);
9616 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9617 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9618 VMA_HEAVY_ASSERT(Validate());
9619
9620 const VkDeviceSize size = GetSize();
9621 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9622 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9623
9624 if(upperAddress)
9625 {
9626 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9627 {
9628 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9629 return false;
9630 }
9631
9632 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9633 if(allocSize > size)
9634 {
9635 return false;
9636 }
9637 VkDeviceSize resultBaseOffset = size - allocSize;
9638 if(!suballocations2nd.empty())
9639 {
9640 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9641 resultBaseOffset = lastSuballoc.offset - allocSize;
9642 if(allocSize > lastSuballoc.offset)
9643 {
9644 return false;
9645 }
9646 }
9647
9648 // Start from offset equal to end of free space.
9649 VkDeviceSize resultOffset = resultBaseOffset;
9650
9651 // Apply VMA_DEBUG_MARGIN at the end.
9652 if(VMA_DEBUG_MARGIN > 0)
9653 {
9654 if(resultOffset < VMA_DEBUG_MARGIN)
9655 {
9656 return false;
9657 }
9658 resultOffset -= VMA_DEBUG_MARGIN;
9659 }
9660
9661 // Apply alignment.
9662 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9663
9664 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9665 // Make bigger alignment if necessary.
9666 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9667 {
9668 bool bufferImageGranularityConflict = false;
9669 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9670 {
9671 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9672 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9673 {
9674 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9675 {
9676 bufferImageGranularityConflict = true;
9677 break;
9678 }
9679 }
9680 else
9681 // Already on previous page.
9682 break;
9683 }
9684 if(bufferImageGranularityConflict)
9685 {
9686 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9687 }
9688 }
9689
9690 // There is enough free space.
9691 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9692 suballocations1st.back().offset + suballocations1st.back().size :
9693 0;
9694 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9695 {
9696 // Check previous suballocations for BufferImageGranularity conflicts.
9697 // If conflict exists, allocation cannot be made here.
9698 if(bufferImageGranularity > 1)
9699 {
9700 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9701 {
9702 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9703 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9704 {
9705 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9706 {
9707 return false;
9708 }
9709 }
9710 else
9711 {
9712 // Already on next page.
9713 break;
9714 }
9715 }
9716 }
9717
9718 // All tests passed: Success.
9719 pAllocationRequest->offset = resultOffset;
9720 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9721 pAllocationRequest->sumItemSize = 0;
9722 // pAllocationRequest->item unused.
9723 pAllocationRequest->itemsToMakeLostCount = 0;
9724 return true;
9725 }
9726 }
9727 else // !upperAddress
9728 {
9729 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9730 {
9731 // Try to allocate at the end of 1st vector.
9732
9733 VkDeviceSize resultBaseOffset = 0;
9734 if(!suballocations1st.empty())
9735 {
9736 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9737 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9738 }
9739
9740 // Start from offset equal to beginning of free space.
9741 VkDeviceSize resultOffset = resultBaseOffset;
9742
9743 // Apply VMA_DEBUG_MARGIN at the beginning.
9744 if(VMA_DEBUG_MARGIN > 0)
9745 {
9746 resultOffset += VMA_DEBUG_MARGIN;
9747 }
9748
9749 // Apply alignment.
9750 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9751
9752 // Check previous suballocations for BufferImageGranularity conflicts.
9753 // Make bigger alignment if necessary.
9754 if(bufferImageGranularity > 1 && !suballocations1st.empty())
9755 {
9756 bool bufferImageGranularityConflict = false;
9757 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9758 {
9759 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9760 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9761 {
9762 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9763 {
9764 bufferImageGranularityConflict = true;
9765 break;
9766 }
9767 }
9768 else
9769 // Already on previous page.
9770 break;
9771 }
9772 if(bufferImageGranularityConflict)
9773 {
9774 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9775 }
9776 }
9777
9778 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9779 suballocations2nd.back().offset : size;
9780
9781 // There is enough free space at the end after alignment.
9782 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9783 {
9784 // Check next suballocations for BufferImageGranularity conflicts.
9785 // If conflict exists, allocation cannot be made here.
9786 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9787 {
9788 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9789 {
9790 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9791 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9792 {
9793 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9794 {
9795 return false;
9796 }
9797 }
9798 else
9799 {
9800 // Already on previous page.
9801 break;
9802 }
9803 }
9804 }
9805
9806 // All tests passed: Success.
9807 pAllocationRequest->offset = resultOffset;
9808 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9809 pAllocationRequest->sumItemSize = 0;
9810 // pAllocationRequest->item unused.
9811 pAllocationRequest->itemsToMakeLostCount = 0;
9812 return true;
9813 }
9814 }
9815
9816 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9817 // beginning of 1st vector as the end of free space.
9818 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9819 {
9820 VMA_ASSERT(!suballocations1st.empty());
9821
9822 VkDeviceSize resultBaseOffset = 0;
9823 if(!suballocations2nd.empty())
9824 {
9825 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9826 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9827 }
9828
9829 // Start from offset equal to beginning of free space.
9830 VkDeviceSize resultOffset = resultBaseOffset;
9831
9832 // Apply VMA_DEBUG_MARGIN at the beginning.
9833 if(VMA_DEBUG_MARGIN > 0)
9834 {
9835 resultOffset += VMA_DEBUG_MARGIN;
9836 }
9837
9838 // Apply alignment.
9839 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9840
9841 // Check previous suballocations for BufferImageGranularity conflicts.
9842 // Make bigger alignment if necessary.
9843 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9844 {
9845 bool bufferImageGranularityConflict = false;
9846 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9847 {
9848 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9849 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9850 {
9851 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9852 {
9853 bufferImageGranularityConflict = true;
9854 break;
9855 }
9856 }
9857 else
9858 // Already on previous page.
9859 break;
9860 }
9861 if(bufferImageGranularityConflict)
9862 {
9863 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9864 }
9865 }
9866
9867 pAllocationRequest->itemsToMakeLostCount = 0;
9868 pAllocationRequest->sumItemSize = 0;
9869 size_t index1st = m_1stNullItemsBeginCount;
9870
9871 if(canMakeOtherLost)
9872 {
9873 while(index1st < suballocations1st.size() &&
9874 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9875 {
9876 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9877 const VmaSuballocation& suballoc = suballocations1st[index1st];
9878 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9879 {
9880 // No problem.
9881 }
9882 else
9883 {
9884 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9885 if(suballoc.hAllocation->CanBecomeLost() &&
9886 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9887 {
9888 ++pAllocationRequest->itemsToMakeLostCount;
9889 pAllocationRequest->sumItemSize += suballoc.size;
9890 }
9891 else
9892 {
9893 return false;
9894 }
9895 }
9896 ++index1st;
9897 }
9898
9899 // Check next suballocations for BufferImageGranularity conflicts.
9900 // If conflict exists, we must mark more allocations lost or fail.
9901 if(bufferImageGranularity > 1)
9902 {
9903 while(index1st < suballocations1st.size())
9904 {
9905 const VmaSuballocation& suballoc = suballocations1st[index1st];
9906 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9907 {
9908 if(suballoc.hAllocation != VK_NULL_HANDLE)
9909 {
9910 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9911 if(suballoc.hAllocation->CanBecomeLost() &&
9912 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9913 {
9914 ++pAllocationRequest->itemsToMakeLostCount;
9915 pAllocationRequest->sumItemSize += suballoc.size;
9916 }
9917 else
9918 {
9919 return false;
9920 }
9921 }
9922 }
9923 else
9924 {
9925 // Already on next page.
9926 break;
9927 }
9928 ++index1st;
9929 }
9930 }
9931 }
9932
9933 // There is enough free space at the end after alignment.
9934 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9935 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9936 {
9937 // Check next suballocations for BufferImageGranularity conflicts.
9938 // If conflict exists, allocation cannot be made here.
9939 if(bufferImageGranularity > 1)
9940 {
9941 for(size_t nextSuballocIndex = index1st;
9942 nextSuballocIndex < suballocations1st.size();
9943 nextSuballocIndex++)
9944 {
9945 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9946 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9947 {
9948 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9949 {
9950 return false;
9951 }
9952 }
9953 else
9954 {
9955 // Already on next page.
9956 break;
9957 }
9958 }
9959 }
9960
9961 // All tests passed: Success.
9962 pAllocationRequest->offset = resultOffset;
9963 pAllocationRequest->sumFreeSize =
9964 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
9965 - resultBaseOffset
9966 - pAllocationRequest->sumItemSize;
9967 // pAllocationRequest->item unused.
9968 return true;
9969 }
9970 }
9971 }
9972
9973 return false;
9974}
9975
9976bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
9977 uint32_t currentFrameIndex,
9978 uint32_t frameInUseCount,
9979 VmaAllocationRequest* pAllocationRequest)
9980{
9981 if(pAllocationRequest->itemsToMakeLostCount == 0)
9982 {
9983 return true;
9984 }
9985
9986 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
9987
9988 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9989 size_t index1st = m_1stNullItemsBeginCount;
9990 size_t madeLostCount = 0;
9991 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
9992 {
9993 VMA_ASSERT(index1st < suballocations1st.size());
9994 VmaSuballocation& suballoc = suballocations1st[index1st];
9995 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
9996 {
9997 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9998 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
9999 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10000 {
10001 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10002 suballoc.hAllocation = VK_NULL_HANDLE;
10003 m_SumFreeSize += suballoc.size;
10004 ++m_1stNullItemsMiddleCount;
10005 ++madeLostCount;
10006 }
10007 else
10008 {
10009 return false;
10010 }
10011 }
10012 ++index1st;
10013 }
10014
10015 CleanupAfterFree();
10016 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10017
10018 return true;
10019}
10020
10021uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10022{
10023 uint32_t lostAllocationCount = 0;
10024
10025 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10026 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10027 {
10028 VmaSuballocation& suballoc = suballocations1st[i];
10029 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10030 suballoc.hAllocation->CanBecomeLost() &&
10031 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10032 {
10033 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10034 suballoc.hAllocation = VK_NULL_HANDLE;
10035 ++m_1stNullItemsMiddleCount;
10036 m_SumFreeSize += suballoc.size;
10037 ++lostAllocationCount;
10038 }
10039 }
10040
10041 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10042 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10043 {
10044 VmaSuballocation& suballoc = suballocations2nd[i];
10045 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10046 suballoc.hAllocation->CanBecomeLost() &&
10047 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10048 {
10049 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10050 suballoc.hAllocation = VK_NULL_HANDLE;
10051 ++m_2ndNullItemsCount;
10052 ++lostAllocationCount;
10053 }
10054 }
10055
10056 if(lostAllocationCount)
10057 {
10058 CleanupAfterFree();
10059 }
10060
10061 return lostAllocationCount;
10062}
10063
10064VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10065{
10066 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10067 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10068 {
10069 const VmaSuballocation& suballoc = suballocations1st[i];
10070 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10071 {
10072 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10073 {
10074 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10075 return VK_ERROR_VALIDATION_FAILED_EXT;
10076 }
10077 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10078 {
10079 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10080 return VK_ERROR_VALIDATION_FAILED_EXT;
10081 }
10082 }
10083 }
10084
10085 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10086 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10087 {
10088 const VmaSuballocation& suballoc = suballocations2nd[i];
10089 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10090 {
10091 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10092 {
10093 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10094 return VK_ERROR_VALIDATION_FAILED_EXT;
10095 }
10096 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10097 {
10098 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10099 return VK_ERROR_VALIDATION_FAILED_EXT;
10100 }
10101 }
10102 }
10103
10104 return VK_SUCCESS;
10105}
10106
10107void VmaBlockMetadata_Linear::Alloc(
10108 const VmaAllocationRequest& request,
10109 VmaSuballocationType type,
10110 VkDeviceSize allocSize,
10111 bool upperAddress,
10112 VmaAllocation hAllocation)
10113{
10114 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10115
10116 if(upperAddress)
10117 {
10118 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10119 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10120 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10121 suballocations2nd.push_back(newSuballoc);
10122 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10123 }
10124 else
10125 {
10126 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10127
10128 // First allocation.
10129 if(suballocations1st.empty())
10130 {
10131 suballocations1st.push_back(newSuballoc);
10132 }
10133 else
10134 {
10135 // New allocation at the end of 1st vector.
10136 if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10137 {
10138 // Check if it fits before the end of the block.
10139 VMA_ASSERT(request.offset + allocSize <= GetSize());
10140 suballocations1st.push_back(newSuballoc);
10141 }
10142 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10143 else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10144 {
10145 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10146
10147 switch(m_2ndVectorMode)
10148 {
10149 case SECOND_VECTOR_EMPTY:
10150 // First allocation from second part ring buffer.
10151 VMA_ASSERT(suballocations2nd.empty());
10152 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10153 break;
10154 case SECOND_VECTOR_RING_BUFFER:
10155 // 2-part ring buffer is already started.
10156 VMA_ASSERT(!suballocations2nd.empty());
10157 break;
10158 case SECOND_VECTOR_DOUBLE_STACK:
10159 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10160 break;
10161 default:
10162 VMA_ASSERT(0);
10163 }
10164
10165 suballocations2nd.push_back(newSuballoc);
10166 }
10167 else
10168 {
10169 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10170 }
10171 }
10172 }
10173
10174 m_SumFreeSize -= newSuballoc.size;
10175}
10176
10177void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10178{
10179 FreeAtOffset(allocation->GetOffset());
10180}
10181
10182void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10183{
10184 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10185 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10186
10187 if(!suballocations1st.empty())
10188 {
10189 // First allocation: Mark it as next empty at the beginning.
10190 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10191 if(firstSuballoc.offset == offset)
10192 {
10193 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10194 firstSuballoc.hAllocation = VK_NULL_HANDLE;
10195 m_SumFreeSize += firstSuballoc.size;
10196 ++m_1stNullItemsBeginCount;
10197 CleanupAfterFree();
10198 return;
10199 }
10200 }
10201
10202 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10203 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10204 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10205 {
10206 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10207 if(lastSuballoc.offset == offset)
10208 {
10209 m_SumFreeSize += lastSuballoc.size;
10210 suballocations2nd.pop_back();
10211 CleanupAfterFree();
10212 return;
10213 }
10214 }
10215 // Last allocation in 1st vector.
10216 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10217 {
10218 VmaSuballocation& lastSuballoc = suballocations1st.back();
10219 if(lastSuballoc.offset == offset)
10220 {
10221 m_SumFreeSize += lastSuballoc.size;
10222 suballocations1st.pop_back();
10223 CleanupAfterFree();
10224 return;
10225 }
10226 }
10227
10228 // Item from the middle of 1st vector.
10229 {
10230 VmaSuballocation refSuballoc;
10231 refSuballoc.offset = offset;
10232 // Rest of members stays uninitialized intentionally for better performance.
10233 SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10234 suballocations1st.begin() + m_1stNullItemsBeginCount,
10235 suballocations1st.end(),
10236 refSuballoc);
10237 if(it != suballocations1st.end())
10238 {
10239 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10240 it->hAllocation = VK_NULL_HANDLE;
10241 ++m_1stNullItemsMiddleCount;
10242 m_SumFreeSize += it->size;
10243 CleanupAfterFree();
10244 return;
10245 }
10246 }
10247
10248 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10249 {
10250 // Item from the middle of 2nd vector.
10251 VmaSuballocation refSuballoc;
10252 refSuballoc.offset = offset;
10253 // Rest of members stays uninitialized intentionally for better performance.
10254 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10255 VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10256 VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10257 if(it != suballocations2nd.end())
10258 {
10259 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10260 it->hAllocation = VK_NULL_HANDLE;
10261 ++m_2ndNullItemsCount;
10262 m_SumFreeSize += it->size;
10263 CleanupAfterFree();
10264 return;
10265 }
10266 }
10267
10268 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10269}
10270
10271bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10272{
10273 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10274 const size_t suballocCount = AccessSuballocations1st().size();
10275 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10276}
10277
10278void VmaBlockMetadata_Linear::CleanupAfterFree()
10279{
10280 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10281 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10282
10283 if(IsEmpty())
10284 {
10285 suballocations1st.clear();
10286 suballocations2nd.clear();
10287 m_1stNullItemsBeginCount = 0;
10288 m_1stNullItemsMiddleCount = 0;
10289 m_2ndNullItemsCount = 0;
10290 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10291 }
10292 else
10293 {
10294 const size_t suballoc1stCount = suballocations1st.size();
10295 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10296 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10297
10298 // Find more null items at the beginning of 1st vector.
10299 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10300 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10301 {
10302 ++m_1stNullItemsBeginCount;
10303 --m_1stNullItemsMiddleCount;
10304 }
10305
10306 // Find more null items at the end of 1st vector.
10307 while(m_1stNullItemsMiddleCount > 0 &&
10308 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10309 {
10310 --m_1stNullItemsMiddleCount;
10311 suballocations1st.pop_back();
10312 }
10313
10314 // Find more null items at the end of 2nd vector.
10315 while(m_2ndNullItemsCount > 0 &&
10316 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10317 {
10318 --m_2ndNullItemsCount;
10319 suballocations2nd.pop_back();
10320 }
10321
10322 if(ShouldCompact1st())
10323 {
10324 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10325 size_t srcIndex = m_1stNullItemsBeginCount;
10326 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10327 {
10328 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10329 {
10330 ++srcIndex;
10331 }
10332 if(dstIndex != srcIndex)
10333 {
10334 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10335 }
10336 ++srcIndex;
10337 }
10338 suballocations1st.resize(nonNullItemCount);
10339 m_1stNullItemsBeginCount = 0;
10340 m_1stNullItemsMiddleCount = 0;
10341 }
10342
10343 // 2nd vector became empty.
10344 if(suballocations2nd.empty())
10345 {
10346 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10347 }
10348
10349 // 1st vector became empty.
10350 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10351 {
10352 suballocations1st.clear();
10353 m_1stNullItemsBeginCount = 0;
10354
10355 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10356 {
10357 // Swap 1st with 2nd. Now 2nd is empty.
10358 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10359 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10360 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10361 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10362 {
10363 ++m_1stNullItemsBeginCount;
10364 --m_1stNullItemsMiddleCount;
10365 }
10366 m_2ndNullItemsCount = 0;
10367 m_1stVectorIndex ^= 1;
10368 }
10369 }
10370 }
10371
10372 VMA_HEAVY_ASSERT(Validate());
10373}
10374
10375
10376////////////////////////////////////////////////////////////////////////////////
10377// class VmaBlockMetadata_Buddy
10378
10379VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10380 VmaBlockMetadata(hAllocator),
10381 m_Root(VMA_NULL),
10382 m_AllocationCount(0),
10383 m_FreeCount(1),
10384 m_SumFreeSize(0)
10385{
10386 memset(m_FreeList, 0, sizeof(m_FreeList));
10387}
10388
10389VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10390{
10391 DeleteNode(m_Root);
10392}
10393
10394void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10395{
10396 VmaBlockMetadata::Init(size);
10397
10398 m_UsableSize = VmaPrevPow2(size);
10399 m_SumFreeSize = m_UsableSize;
10400
10401 // Calculate m_LevelCount.
10402 m_LevelCount = 1;
10403 while(m_LevelCount < MAX_LEVELS &&
10404 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10405 {
10406 ++m_LevelCount;
10407 }
10408
10409 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10410 rootNode->offset = 0;
10411 rootNode->type = Node::TYPE_FREE;
10412 rootNode->parent = VMA_NULL;
10413 rootNode->buddy = VMA_NULL;
10414
10415 m_Root = rootNode;
10416 AddToFreeListFront(0, rootNode);
10417}
10418
10419bool VmaBlockMetadata_Buddy::Validate() const
10420{
10421 // Validate tree.
10422 ValidationContext ctx;
10423 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10424 {
10425 VMA_VALIDATE(false && "ValidateNode failed.");
10426 }
10427 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10428 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10429
10430 // Validate free node lists.
10431 for(uint32_t level = 0; level < m_LevelCount; ++level)
10432 {
10433 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10434 m_FreeList[level].front->free.prev == VMA_NULL);
10435
10436 for(Node* node = m_FreeList[level].front;
10437 node != VMA_NULL;
10438 node = node->free.next)
10439 {
10440 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10441
10442 if(node->free.next == VMA_NULL)
10443 {
10444 VMA_VALIDATE(m_FreeList[level].back == node);
10445 }
10446 else
10447 {
10448 VMA_VALIDATE(node->free.next->free.prev == node);
10449 }
10450 }
10451 }
10452
10453 // Validate that free lists ar higher levels are empty.
10454 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10455 {
10456 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10457 }
10458
10459 return true;
10460}
10461
10462VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10463{
10464 for(uint32_t level = 0; level < m_LevelCount; ++level)
10465 {
10466 if(m_FreeList[level].front != VMA_NULL)
10467 {
10468 return LevelToNodeSize(level);
10469 }
10470 }
10471 return 0;
10472}
10473
10474void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10475{
10476 const VkDeviceSize unusableSize = GetUnusableSize();
10477
10478 outInfo.blockCount = 1;
10479
10480 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10481 outInfo.usedBytes = outInfo.unusedBytes = 0;
10482
10483 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10484 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10485 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10486
10487 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10488
10489 if(unusableSize > 0)
10490 {
10491 ++outInfo.unusedRangeCount;
10492 outInfo.unusedBytes += unusableSize;
10493 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10494 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10495 }
10496}
10497
10498void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10499{
10500 const VkDeviceSize unusableSize = GetUnusableSize();
10501
10502 inoutStats.size += GetSize();
10503 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10504 inoutStats.allocationCount += m_AllocationCount;
10505 inoutStats.unusedRangeCount += m_FreeCount;
10506 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10507
10508 if(unusableSize > 0)
10509 {
10510 ++inoutStats.unusedRangeCount;
10511 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10512 }
10513}
10514
10515#if VMA_STATS_STRING_ENABLED
10516
10517void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10518{
10519 // TODO optimize
10520 VmaStatInfo stat;
10521 CalcAllocationStatInfo(stat);
10522
10523 PrintDetailedMap_Begin(
10524 json,
10525 stat.unusedBytes,
10526 stat.allocationCount,
10527 stat.unusedRangeCount);
10528
10529 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10530
10531 const VkDeviceSize unusableSize = GetUnusableSize();
10532 if(unusableSize > 0)
10533 {
10534 PrintDetailedMap_UnusedRange(json,
10535 m_UsableSize, // offset
10536 unusableSize); // size
10537 }
10538
10539 PrintDetailedMap_End(json);
10540}
10541
10542#endif // #if VMA_STATS_STRING_ENABLED
10543
10544bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10545 uint32_t currentFrameIndex,
10546 uint32_t frameInUseCount,
10547 VkDeviceSize bufferImageGranularity,
10548 VkDeviceSize allocSize,
10549 VkDeviceSize allocAlignment,
10550 bool upperAddress,
10551 VmaSuballocationType allocType,
10552 bool canMakeOtherLost,
10553 uint32_t strategy,
10554 VmaAllocationRequest* pAllocationRequest)
10555{
10556 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10557
10558 // Simple way to respect bufferImageGranularity. May be optimized some day.
10559 // Whenever it might be an OPTIMAL image...
10560 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10561 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10562 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10563 {
10564 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10565 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10566 }
10567
10568 if(allocSize > m_UsableSize)
10569 {
10570 return false;
10571 }
10572
10573 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10574 for(uint32_t level = targetLevel + 1; level--; )
10575 {
10576 for(Node* freeNode = m_FreeList[level].front;
10577 freeNode != VMA_NULL;
10578 freeNode = freeNode->free.next)
10579 {
10580 if(freeNode->offset % allocAlignment == 0)
10581 {
10582 pAllocationRequest->offset = freeNode->offset;
10583 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10584 pAllocationRequest->sumItemSize = 0;
10585 pAllocationRequest->itemsToMakeLostCount = 0;
10586 pAllocationRequest->customData = (void*)(uintptr_t)level;
10587 return true;
10588 }
10589 }
10590 }
10591
10592 return false;
10593}
10594
10595bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10596 uint32_t currentFrameIndex,
10597 uint32_t frameInUseCount,
10598 VmaAllocationRequest* pAllocationRequest)
10599{
10600 /*
10601 Lost allocations are not supported in buddy allocator at the moment.
10602 Support might be added in the future.
10603 */
10604 return pAllocationRequest->itemsToMakeLostCount == 0;
10605}
10606
10607uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10608{
10609 /*
10610 Lost allocations are not supported in buddy allocator at the moment.
10611 Support might be added in the future.
10612 */
10613 return 0;
10614}
10615
10616void VmaBlockMetadata_Buddy::Alloc(
10617 const VmaAllocationRequest& request,
10618 VmaSuballocationType type,
10619 VkDeviceSize allocSize,
10620 bool upperAddress,
10621 VmaAllocation hAllocation)
10622{
10623 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10624 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10625
10626 Node* currNode = m_FreeList[currLevel].front;
10627 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10628 while(currNode->offset != request.offset)
10629 {
10630 currNode = currNode->free.next;
10631 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10632 }
10633
10634 // Go down, splitting free nodes.
10635 while(currLevel < targetLevel)
10636 {
10637 // currNode is already first free node at currLevel.
10638 // Remove it from list of free nodes at this currLevel.
10639 RemoveFromFreeList(currLevel, currNode);
10640
10641 const uint32_t childrenLevel = currLevel + 1;
10642
10643 // Create two free sub-nodes.
10644 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10645 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10646
10647 leftChild->offset = currNode->offset;
10648 leftChild->type = Node::TYPE_FREE;
10649 leftChild->parent = currNode;
10650 leftChild->buddy = rightChild;
10651
10652 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10653 rightChild->type = Node::TYPE_FREE;
10654 rightChild->parent = currNode;
10655 rightChild->buddy = leftChild;
10656
10657 // Convert current currNode to split type.
10658 currNode->type = Node::TYPE_SPLIT;
10659 currNode->split.leftChild = leftChild;
10660
10661 // Add child nodes to free list. Order is important!
10662 AddToFreeListFront(childrenLevel, rightChild);
10663 AddToFreeListFront(childrenLevel, leftChild);
10664
10665 ++m_FreeCount;
10666 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10667 ++currLevel;
10668 currNode = m_FreeList[currLevel].front;
10669
10670 /*
10671 We can be sure that currNode, as left child of node previously split,
10672 also fullfills the alignment requirement.
10673 */
10674 }
10675
10676 // Remove from free list.
10677 VMA_ASSERT(currLevel == targetLevel &&
10678 currNode != VMA_NULL &&
10679 currNode->type == Node::TYPE_FREE);
10680 RemoveFromFreeList(currLevel, currNode);
10681
10682 // Convert to allocation node.
10683 currNode->type = Node::TYPE_ALLOCATION;
10684 currNode->allocation.alloc = hAllocation;
10685
10686 ++m_AllocationCount;
10687 --m_FreeCount;
10688 m_SumFreeSize -= allocSize;
10689}
10690
10691void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10692{
10693 if(node->type == Node::TYPE_SPLIT)
10694 {
10695 DeleteNode(node->split.leftChild->buddy);
10696 DeleteNode(node->split.leftChild);
10697 }
10698
10699 vma_delete(GetAllocationCallbacks(), node);
10700}
10701
10702bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10703{
10704 VMA_VALIDATE(level < m_LevelCount);
10705 VMA_VALIDATE(curr->parent == parent);
10706 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10707 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10708 switch(curr->type)
10709 {
10710 case Node::TYPE_FREE:
10711 // curr->free.prev, next are validated separately.
10712 ctx.calculatedSumFreeSize += levelNodeSize;
10713 ++ctx.calculatedFreeCount;
10714 break;
10715 case Node::TYPE_ALLOCATION:
10716 ++ctx.calculatedAllocationCount;
10717 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10718 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10719 break;
10720 case Node::TYPE_SPLIT:
10721 {
10722 const uint32_t childrenLevel = level + 1;
10723 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10724 const Node* const leftChild = curr->split.leftChild;
10725 VMA_VALIDATE(leftChild != VMA_NULL);
10726 VMA_VALIDATE(leftChild->offset == curr->offset);
10727 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10728 {
10729 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10730 }
10731 const Node* const rightChild = leftChild->buddy;
10732 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10733 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10734 {
10735 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10736 }
10737 }
10738 break;
10739 default:
10740 return false;
10741 }
10742
10743 return true;
10744}
10745
10746uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10747{
10748 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10749 uint32_t level = 0;
10750 VkDeviceSize currLevelNodeSize = m_UsableSize;
10751 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10752 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10753 {
10754 ++level;
10755 currLevelNodeSize = nextLevelNodeSize;
10756 nextLevelNodeSize = currLevelNodeSize >> 1;
10757 }
10758 return level;
10759}
10760
10761void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10762{
10763 // Find node and level.
10764 Node* node = m_Root;
10765 VkDeviceSize nodeOffset = 0;
10766 uint32_t level = 0;
10767 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10768 while(node->type == Node::TYPE_SPLIT)
10769 {
10770 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10771 if(offset < nodeOffset + nextLevelSize)
10772 {
10773 node = node->split.leftChild;
10774 }
10775 else
10776 {
10777 node = node->split.leftChild->buddy;
10778 nodeOffset += nextLevelSize;
10779 }
10780 ++level;
10781 levelNodeSize = nextLevelSize;
10782 }
10783
10784 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10785 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10786
10787 ++m_FreeCount;
10788 --m_AllocationCount;
10789 m_SumFreeSize += alloc->GetSize();
10790
10791 node->type = Node::TYPE_FREE;
10792
10793 // Join free nodes if possible.
10794 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10795 {
10796 RemoveFromFreeList(level, node->buddy);
10797 Node* const parent = node->parent;
10798
10799 vma_delete(GetAllocationCallbacks(), node->buddy);
10800 vma_delete(GetAllocationCallbacks(), node);
10801 parent->type = Node::TYPE_FREE;
10802
10803 node = parent;
10804 --level;
10805 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10806 --m_FreeCount;
10807 }
10808
10809 AddToFreeListFront(level, node);
10810}
10811
10812void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10813{
10814 switch(node->type)
10815 {
10816 case Node::TYPE_FREE:
10817 ++outInfo.unusedRangeCount;
10818 outInfo.unusedBytes += levelNodeSize;
10819 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10820 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10821 break;
10822 case Node::TYPE_ALLOCATION:
10823 {
10824 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10825 ++outInfo.allocationCount;
10826 outInfo.usedBytes += allocSize;
10827 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10828 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10829
10830 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10831 if(unusedRangeSize > 0)
10832 {
10833 ++outInfo.unusedRangeCount;
10834 outInfo.unusedBytes += unusedRangeSize;
10835 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10836 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10837 }
10838 }
10839 break;
10840 case Node::TYPE_SPLIT:
10841 {
10842 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10843 const Node* const leftChild = node->split.leftChild;
10844 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10845 const Node* const rightChild = leftChild->buddy;
10846 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10847 }
10848 break;
10849 default:
10850 VMA_ASSERT(0);
10851 }
10852}
10853
10854void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10855{
10856 VMA_ASSERT(node->type == Node::TYPE_FREE);
10857
10858 // List is empty.
10859 Node* const frontNode = m_FreeList[level].front;
10860 if(frontNode == VMA_NULL)
10861 {
10862 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10863 node->free.prev = node->free.next = VMA_NULL;
10864 m_FreeList[level].front = m_FreeList[level].back = node;
10865 }
10866 else
10867 {
10868 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10869 node->free.prev = VMA_NULL;
10870 node->free.next = frontNode;
10871 frontNode->free.prev = node;
10872 m_FreeList[level].front = node;
10873 }
10874}
10875
10876void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10877{
10878 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10879
10880 // It is at the front.
10881 if(node->free.prev == VMA_NULL)
10882 {
10883 VMA_ASSERT(m_FreeList[level].front == node);
10884 m_FreeList[level].front = node->free.next;
10885 }
10886 else
10887 {
10888 Node* const prevFreeNode = node->free.prev;
10889 VMA_ASSERT(prevFreeNode->free.next == node);
10890 prevFreeNode->free.next = node->free.next;
10891 }
10892
10893 // It is at the back.
10894 if(node->free.next == VMA_NULL)
10895 {
10896 VMA_ASSERT(m_FreeList[level].back == node);
10897 m_FreeList[level].back = node->free.prev;
10898 }
10899 else
10900 {
10901 Node* const nextFreeNode = node->free.next;
10902 VMA_ASSERT(nextFreeNode->free.prev == node);
10903 nextFreeNode->free.prev = node->free.prev;
10904 }
10905}
10906
10907#if VMA_STATS_STRING_ENABLED
10908void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10909{
10910 switch(node->type)
10911 {
10912 case Node::TYPE_FREE:
10913 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10914 break;
10915 case Node::TYPE_ALLOCATION:
10916 {
10917 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10918 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10919 if(allocSize < levelNodeSize)
10920 {
10921 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10922 }
10923 }
10924 break;
10925 case Node::TYPE_SPLIT:
10926 {
10927 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10928 const Node* const leftChild = node->split.leftChild;
10929 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10930 const Node* const rightChild = leftChild->buddy;
10931 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10932 }
10933 break;
10934 default:
10935 VMA_ASSERT(0);
10936 }
10937}
10938#endif // #if VMA_STATS_STRING_ENABLED
10939
10940
10941////////////////////////////////////////////////////////////////////////////////
10942// class VmaDeviceMemoryBlock
10943
10944VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
10945 m_pMetadata(VMA_NULL),
10946 m_MemoryTypeIndex(UINT32_MAX),
10947 m_Id(0),
10948 m_hMemory(VK_NULL_HANDLE),
10949 m_MapCount(0),
10950 m_pMappedData(VMA_NULL)
10951{
10952}
10953
10954void VmaDeviceMemoryBlock::Init(
10955 VmaAllocator hAllocator,
10956 uint32_t newMemoryTypeIndex,
10957 VkDeviceMemory newMemory,
10958 VkDeviceSize newSize,
10959 uint32_t id,
10960 uint32_t algorithm)
10961{
10962 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
10963
10964 m_MemoryTypeIndex = newMemoryTypeIndex;
10965 m_Id = id;
10966 m_hMemory = newMemory;
10967
10968 switch(algorithm)
10969 {
10970 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
10971 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
10972 break;
10973 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
10974 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
10975 break;
10976 default:
10977 VMA_ASSERT(0);
10978 // Fall-through.
10979 case 0:
10980 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
10981 }
10982 m_pMetadata->Init(newSize);
10983}
10984
10985void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
10986{
10987 // This is the most important assert in the entire library.
10988 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
10989 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
10990
10991 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
10992 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
10993 m_hMemory = VK_NULL_HANDLE;
10994
10995 vma_delete(allocator, m_pMetadata);
10996 m_pMetadata = VMA_NULL;
10997}
10998
10999bool VmaDeviceMemoryBlock::Validate() const
11000{
11001 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11002 (m_pMetadata->GetSize() != 0));
11003
11004 return m_pMetadata->Validate();
11005}
11006
11007VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11008{
11009 void* pData = nullptr;
11010 VkResult res = Map(hAllocator, 1, &pData);
11011 if(res != VK_SUCCESS)
11012 {
11013 return res;
11014 }
11015
11016 res = m_pMetadata->CheckCorruption(pData);
11017
11018 Unmap(hAllocator, 1);
11019
11020 return res;
11021}
11022
11023VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11024{
11025 if(count == 0)
11026 {
11027 return VK_SUCCESS;
11028 }
11029
11030 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11031 if(m_MapCount != 0)
11032 {
11033 m_MapCount += count;
11034 VMA_ASSERT(m_pMappedData != VMA_NULL);
11035 if(ppData != VMA_NULL)
11036 {
11037 *ppData = m_pMappedData;
11038 }
11039 return VK_SUCCESS;
11040 }
11041 else
11042 {
11043 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11044 hAllocator->m_hDevice,
11045 m_hMemory,
11046 0, // offset
11047 VK_WHOLE_SIZE,
11048 0, // flags
11049 &m_pMappedData);
11050 if(result == VK_SUCCESS)
11051 {
11052 if(ppData != VMA_NULL)
11053 {
11054 *ppData = m_pMappedData;
11055 }
11056 m_MapCount = count;
11057 }
11058 return result;
11059 }
11060}
11061
11062void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11063{
11064 if(count == 0)
11065 {
11066 return;
11067 }
11068
11069 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11070 if(m_MapCount >= count)
11071 {
11072 m_MapCount -= count;
11073 if(m_MapCount == 0)
11074 {
11075 m_pMappedData = VMA_NULL;
11076 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11077 }
11078 }
11079 else
11080 {
11081 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11082 }
11083}
11084
11085VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11086{
11087 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11088 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11089
11090 void* pData;
11091 VkResult res = Map(hAllocator, 1, &pData);
11092 if(res != VK_SUCCESS)
11093 {
11094 return res;
11095 }
11096
11097 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11098 VmaWriteMagicValue(pData, allocOffset + allocSize);
11099
11100 Unmap(hAllocator, 1);
11101
11102 return VK_SUCCESS;
11103}
11104
11105VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11106{
11107 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11108 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11109
11110 void* pData;
11111 VkResult res = Map(hAllocator, 1, &pData);
11112 if(res != VK_SUCCESS)
11113 {
11114 return res;
11115 }
11116
11117 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11118 {
11119 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11120 }
11121 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11122 {
11123 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11124 }
11125
11126 Unmap(hAllocator, 1);
11127
11128 return VK_SUCCESS;
11129}
11130
11131VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11132 const VmaAllocator hAllocator,
11133 const VmaAllocation hAllocation,
11134 VkBuffer hBuffer)
11135{
11136 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11137 hAllocation->GetBlock() == this);
11138 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11139 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11140 return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11141 hAllocator->m_hDevice,
11142 hBuffer,
11143 m_hMemory,
11144 hAllocation->GetOffset());
11145}
11146
11147VkResult VmaDeviceMemoryBlock::BindImageMemory(
11148 const VmaAllocator hAllocator,
11149 const VmaAllocation hAllocation,
11150 VkImage hImage)
11151{
11152 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11153 hAllocation->GetBlock() == this);
11154 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11155 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11156 return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11157 hAllocator->m_hDevice,
11158 hImage,
11159 m_hMemory,
11160 hAllocation->GetOffset());
11161}
11162
11163static void InitStatInfo(VmaStatInfo& outInfo)
11164{
11165 memset(&outInfo, 0, sizeof(outInfo));
11166 outInfo.allocationSizeMin = UINT64_MAX;
11167 outInfo.unusedRangeSizeMin = UINT64_MAX;
11168}
11169
11170// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
11171static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11172{
11173 inoutInfo.blockCount += srcInfo.blockCount;
11174 inoutInfo.allocationCount += srcInfo.allocationCount;
11175 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11176 inoutInfo.usedBytes += srcInfo.usedBytes;
11177 inoutInfo.unusedBytes += srcInfo.unusedBytes;
11178 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11179 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11180 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11181 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11182}
11183
11184static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11185{
11186 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11187 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11188 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11189 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11190}
11191
11192VmaPool_T::VmaPool_T(
11193 VmaAllocator hAllocator,
11194 const VmaPoolCreateInfo& createInfo,
11195 VkDeviceSize preferredBlockSize) :
11196 m_BlockVector(
11197 hAllocator,
11198 createInfo.memoryTypeIndex,
11199 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11200 createInfo.minBlockCount,
11201 createInfo.maxBlockCount,
11202 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11203 createInfo.frameInUseCount,
11204 true, // isCustomPool
11205 createInfo.blockSize != 0, // explicitBlockSize
11206 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11207 m_Id(0)
11208{
11209}
11210
11211VmaPool_T::~VmaPool_T()
11212{
11213}
11214
11215#if VMA_STATS_STRING_ENABLED
11216
11217#endif // #if VMA_STATS_STRING_ENABLED
11218
11219VmaBlockVector::VmaBlockVector(
11220 VmaAllocator hAllocator,
11221 uint32_t memoryTypeIndex,
11222 VkDeviceSize preferredBlockSize,
11223 size_t minBlockCount,
11224 size_t maxBlockCount,
11225 VkDeviceSize bufferImageGranularity,
11226 uint32_t frameInUseCount,
11227 bool isCustomPool,
11228 bool explicitBlockSize,
11229 uint32_t algorithm) :
11230 m_hAllocator(hAllocator),
11231 m_MemoryTypeIndex(memoryTypeIndex),
11232 m_PreferredBlockSize(preferredBlockSize),
11233 m_MinBlockCount(minBlockCount),
11234 m_MaxBlockCount(maxBlockCount),
11235 m_BufferImageGranularity(bufferImageGranularity),
11236 m_FrameInUseCount(frameInUseCount),
11237 m_IsCustomPool(isCustomPool),
11238 m_ExplicitBlockSize(explicitBlockSize),
11239 m_Algorithm(algorithm),
11240 m_HasEmptyBlock(false),
11241 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11242 m_NextBlockId(0)
11243{
11244}
11245
11246VmaBlockVector::~VmaBlockVector()
11247{
11248 for(size_t i = m_Blocks.size(); i--; )
11249 {
11250 m_Blocks[i]->Destroy(m_hAllocator);
11251 vma_delete(m_hAllocator, m_Blocks[i]);
11252 }
11253}
11254
11255VkResult VmaBlockVector::CreateMinBlocks()
11256{
11257 for(size_t i = 0; i < m_MinBlockCount; ++i)
11258 {
11259 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11260 if(res != VK_SUCCESS)
11261 {
11262 return res;
11263 }
11264 }
11265 return VK_SUCCESS;
11266}
11267
11268void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11269{
11270 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11271
11272 const size_t blockCount = m_Blocks.size();
11273
11274 pStats->size = 0;
11275 pStats->unusedSize = 0;
11276 pStats->allocationCount = 0;
11277 pStats->unusedRangeCount = 0;
11278 pStats->unusedRangeSizeMax = 0;
11279 pStats->blockCount = blockCount;
11280
11281 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11282 {
11283 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11284 VMA_ASSERT(pBlock);
11285 VMA_HEAVY_ASSERT(pBlock->Validate());
11286 pBlock->m_pMetadata->AddPoolStats(*pStats);
11287 }
11288}
11289
11290bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11291{
11292 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11293 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11294 (VMA_DEBUG_MARGIN > 0) &&
11295 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11296}
11297
11298static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11299
11300VkResult VmaBlockVector::Allocate(
11301 VmaPool hCurrentPool,
11302 uint32_t currentFrameIndex,
11303 VkDeviceSize size,
11304 VkDeviceSize alignment,
11305 const VmaAllocationCreateInfo& createInfo,
11306 VmaSuballocationType suballocType,
11307 size_t allocationCount,
11308 VmaAllocation* pAllocations)
11309{
11310 size_t allocIndex;
11311 VkResult res = VK_SUCCESS;
11312
11313 {
11314 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11315 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11316 {
11317 res = AllocatePage(
11318 hCurrentPool,
11319 currentFrameIndex,
11320 size,
11321 alignment,
11322 createInfo,
11323 suballocType,
11324 pAllocations + allocIndex);
11325 if(res != VK_SUCCESS)
11326 {
11327 break;
11328 }
11329 }
11330 }
11331
11332 if(res != VK_SUCCESS)
11333 {
11334 // Free all already created allocations.
11335 while(allocIndex--)
11336 {
11337 Free(pAllocations[allocIndex]);
11338 }
11339 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11340 }
11341
11342 return res;
11343}
11344
11345VkResult VmaBlockVector::AllocatePage(
11346 VmaPool hCurrentPool,
11347 uint32_t currentFrameIndex,
11348 VkDeviceSize size,
11349 VkDeviceSize alignment,
11350 const VmaAllocationCreateInfo& createInfo,
11351 VmaSuballocationType suballocType,
11352 VmaAllocation* pAllocation)
11353{
11354 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11355 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11356 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11357 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11358 const bool canCreateNewBlock =
11359 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11360 (m_Blocks.size() < m_MaxBlockCount);
11361 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11362
11363 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11364 // Which in turn is available only when maxBlockCount = 1.
11365 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11366 {
11367 canMakeOtherLost = false;
11368 }
11369
11370 // Upper address can only be used with linear allocator and within single memory block.
11371 if(isUpperAddress &&
11372 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11373 {
11374 return VK_ERROR_FEATURE_NOT_PRESENT;
11375 }
11376
11377 // Validate strategy.
11378 switch(strategy)
11379 {
11380 case 0:
11381 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11382 break;
11383 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11384 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11385 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11386 break;
11387 default:
11388 return VK_ERROR_FEATURE_NOT_PRESENT;
11389 }
11390
11391 // Early reject: requested allocation size is larger that maximum block size for this block vector.
11392 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11393 {
11394 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11395 }
11396
11397 /*
11398 Under certain condition, this whole section can be skipped for optimization, so
11399 we move on directly to trying to allocate with canMakeOtherLost. That's the case
11400 e.g. for custom pools with linear algorithm.
11401 */
11402 if(!canMakeOtherLost || canCreateNewBlock)
11403 {
11404 // 1. Search existing allocations. Try to allocate without making other allocations lost.
11405 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11406 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11407
11408 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11409 {
11410 // Use only last block.
11411 if(!m_Blocks.empty())
11412 {
11413 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11414 VMA_ASSERT(pCurrBlock);
11415 VkResult res = AllocateFromBlock(
11416 pCurrBlock,
11417 hCurrentPool,
11418 currentFrameIndex,
11419 size,
11420 alignment,
11421 allocFlagsCopy,
11422 createInfo.pUserData,
11423 suballocType,
11424 strategy,
11425 pAllocation);
11426 if(res == VK_SUCCESS)
11427 {
11428 VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11429 return VK_SUCCESS;
11430 }
11431 }
11432 }
11433 else
11434 {
11435 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11436 {
11437 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11438 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11439 {
11440 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11441 VMA_ASSERT(pCurrBlock);
11442 VkResult res = AllocateFromBlock(
11443 pCurrBlock,
11444 hCurrentPool,
11445 currentFrameIndex,
11446 size,
11447 alignment,
11448 allocFlagsCopy,
11449 createInfo.pUserData,
11450 suballocType,
11451 strategy,
11452 pAllocation);
11453 if(res == VK_SUCCESS)
11454 {
11455 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11456 return VK_SUCCESS;
11457 }
11458 }
11459 }
11460 else // WORST_FIT, FIRST_FIT
11461 {
11462 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11463 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11464 {
11465 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11466 VMA_ASSERT(pCurrBlock);
11467 VkResult res = AllocateFromBlock(
11468 pCurrBlock,
11469 hCurrentPool,
11470 currentFrameIndex,
11471 size,
11472 alignment,
11473 allocFlagsCopy,
11474 createInfo.pUserData,
11475 suballocType,
11476 strategy,
11477 pAllocation);
11478 if(res == VK_SUCCESS)
11479 {
11480 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11481 return VK_SUCCESS;
11482 }
11483 }
11484 }
11485 }
11486
11487 // 2. Try to create new block.
11488 if(canCreateNewBlock)
11489 {
11490 // Calculate optimal size for new block.
11491 VkDeviceSize newBlockSize = m_PreferredBlockSize;
11492 uint32_t newBlockSizeShift = 0;
11493 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11494
11495 if(!m_ExplicitBlockSize)
11496 {
11497 // Allocate 1/8, 1/4, 1/2 as first blocks.
11498 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11499 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11500 {
11501 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11502 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11503 {
11504 newBlockSize = smallerNewBlockSize;
11505 ++newBlockSizeShift;
11506 }
11507 else
11508 {
11509 break;
11510 }
11511 }
11512 }
11513
11514 size_t newBlockIndex = 0;
11515 VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11516 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11517 if(!m_ExplicitBlockSize)
11518 {
11519 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11520 {
11521 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11522 if(smallerNewBlockSize >= size)
11523 {
11524 newBlockSize = smallerNewBlockSize;
11525 ++newBlockSizeShift;
11526 res = CreateBlock(newBlockSize, &newBlockIndex);
11527 }
11528 else
11529 {
11530 break;
11531 }
11532 }
11533 }
11534
11535 if(res == VK_SUCCESS)
11536 {
11537 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11538 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11539
11540 res = AllocateFromBlock(
11541 pBlock,
11542 hCurrentPool,
11543 currentFrameIndex,
11544 size,
11545 alignment,
11546 allocFlagsCopy,
11547 createInfo.pUserData,
11548 suballocType,
11549 strategy,
11550 pAllocation);
11551 if(res == VK_SUCCESS)
11552 {
11553 VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
11554 return VK_SUCCESS;
11555 }
11556 else
11557 {
11558 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11559 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11560 }
11561 }
11562 }
11563 }
11564
11565 // 3. Try to allocate from existing blocks with making other allocations lost.
11566 if(canMakeOtherLost)
11567 {
11568 uint32_t tryIndex = 0;
11569 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11570 {
11571 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11572 VmaAllocationRequest bestRequest = {};
11573 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11574
11575 // 1. Search existing allocations.
11576 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11577 {
11578 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11579 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11580 {
11581 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11582 VMA_ASSERT(pCurrBlock);
11583 VmaAllocationRequest currRequest = {};
11584 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11585 currentFrameIndex,
11586 m_FrameInUseCount,
11587 m_BufferImageGranularity,
11588 size,
11589 alignment,
11590 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11591 suballocType,
11592 canMakeOtherLost,
11593 strategy,
11594 &currRequest))
11595 {
11596 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11597 if(pBestRequestBlock == VMA_NULL ||
11598 currRequestCost < bestRequestCost)
11599 {
11600 pBestRequestBlock = pCurrBlock;
11601 bestRequest = currRequest;
11602 bestRequestCost = currRequestCost;
11603
11604 if(bestRequestCost == 0)
11605 {
11606 break;
11607 }
11608 }
11609 }
11610 }
11611 }
11612 else // WORST_FIT, FIRST_FIT
11613 {
11614 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11615 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11616 {
11617 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11618 VMA_ASSERT(pCurrBlock);
11619 VmaAllocationRequest currRequest = {};
11620 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11621 currentFrameIndex,
11622 m_FrameInUseCount,
11623 m_BufferImageGranularity,
11624 size,
11625 alignment,
11626 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11627 suballocType,
11628 canMakeOtherLost,
11629 strategy,
11630 &currRequest))
11631 {
11632 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11633 if(pBestRequestBlock == VMA_NULL ||
11634 currRequestCost < bestRequestCost ||
11635 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11636 {
11637 pBestRequestBlock = pCurrBlock;
11638 bestRequest = currRequest;
11639 bestRequestCost = currRequestCost;
11640
11641 if(bestRequestCost == 0 ||
11642 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11643 {
11644 break;
11645 }
11646 }
11647 }
11648 }
11649 }
11650
11651 if(pBestRequestBlock != VMA_NULL)
11652 {
11653 if(mapped)
11654 {
11655 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11656 if(res != VK_SUCCESS)
11657 {
11658 return res;
11659 }
11660 }
11661
11662 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11663 currentFrameIndex,
11664 m_FrameInUseCount,
11665 &bestRequest))
11666 {
11667 // We no longer have an empty Allocation.
11668 if(pBestRequestBlock->m_pMetadata->IsEmpty())
11669 {
11670 m_HasEmptyBlock = false;
11671 }
11672 // Allocate from this pBlock.
11673 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11674 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11675 (*pAllocation)->InitBlockAllocation(
11676 hCurrentPool,
11677 pBestRequestBlock,
11678 bestRequest.offset,
11679 alignment,
11680 size,
11681 suballocType,
11682 mapped,
11683 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11684 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11685 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
11686 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11687 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11688 {
11689 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11690 }
11691 if(IsCorruptionDetectionEnabled())
11692 {
11693 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11694 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11695 }
11696 return VK_SUCCESS;
11697 }
11698 // else: Some allocations must have been touched while we are here. Next try.
11699 }
11700 else
11701 {
11702 // Could not find place in any of the blocks - break outer loop.
11703 break;
11704 }
11705 }
11706 /* Maximum number of tries exceeded - a very unlike event when many other
11707 threads are simultaneously touching allocations making it impossible to make
11708 lost at the same time as we try to allocate. */
11709 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11710 {
11711 return VK_ERROR_TOO_MANY_OBJECTS;
11712 }
11713 }
11714
11715 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11716}
11717
11718void VmaBlockVector::Free(
11719 VmaAllocation hAllocation)
11720{
11721 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11722
11723 // Scope for lock.
11724 {
11725 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11726
11727 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11728
11729 if(IsCorruptionDetectionEnabled())
11730 {
11731 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11732 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11733 }
11734
11735 if(hAllocation->IsPersistentMap())
11736 {
11737 pBlock->Unmap(m_hAllocator, 1);
11738 }
11739
11740 pBlock->m_pMetadata->Free(hAllocation);
11741 VMA_HEAVY_ASSERT(pBlock->Validate());
11742
11743 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11744
11745 // pBlock became empty after this deallocation.
11746 if(pBlock->m_pMetadata->IsEmpty())
11747 {
11748 // Already has empty Allocation. We don't want to have two, so delete this one.
11749 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11750 {
11751 pBlockToDelete = pBlock;
11752 Remove(pBlock);
11753 }
11754 // We now have first empty block.
11755 else
11756 {
11757 m_HasEmptyBlock = true;
11758 }
11759 }
11760 // pBlock didn't become empty, but we have another empty block - find and free that one.
11761 // (This is optional, heuristics.)
11762 else if(m_HasEmptyBlock)
11763 {
11764 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11765 if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11766 {
11767 pBlockToDelete = pLastBlock;
11768 m_Blocks.pop_back();
11769 m_HasEmptyBlock = false;
11770 }
11771 }
11772
11773 IncrementallySortBlocks();
11774 }
11775
11776 // Destruction of a free Allocation. Deferred until this point, outside of mutex
11777 // lock, for performance reason.
11778 if(pBlockToDelete != VMA_NULL)
11779 {
11780 VMA_DEBUG_LOG(" Deleted empty allocation");
11781 pBlockToDelete->Destroy(m_hAllocator);
11782 vma_delete(m_hAllocator, pBlockToDelete);
11783 }
11784}
11785
11786VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11787{
11788 VkDeviceSize result = 0;
11789 for(size_t i = m_Blocks.size(); i--; )
11790 {
11791 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11792 if(result >= m_PreferredBlockSize)
11793 {
11794 break;
11795 }
11796 }
11797 return result;
11798}
11799
11800void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11801{
11802 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11803 {
11804 if(m_Blocks[blockIndex] == pBlock)
11805 {
11806 VmaVectorRemove(m_Blocks, blockIndex);
11807 return;
11808 }
11809 }
11810 VMA_ASSERT(0);
11811}
11812
11813void VmaBlockVector::IncrementallySortBlocks()
11814{
11815 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11816 {
11817 // Bubble sort only until first swap.
11818 for(size_t i = 1; i < m_Blocks.size(); ++i)
11819 {
11820 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11821 {
11822 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11823 return;
11824 }
11825 }
11826 }
11827}
11828
11829VkResult VmaBlockVector::AllocateFromBlock(
11830 VmaDeviceMemoryBlock* pBlock,
11831 VmaPool hCurrentPool,
11832 uint32_t currentFrameIndex,
11833 VkDeviceSize size,
11834 VkDeviceSize alignment,
11835 VmaAllocationCreateFlags allocFlags,
11836 void* pUserData,
11837 VmaSuballocationType suballocType,
11838 uint32_t strategy,
11839 VmaAllocation* pAllocation)
11840{
11841 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11842 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11843 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11844 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11845
11846 VmaAllocationRequest currRequest = {};
11847 if(pBlock->m_pMetadata->CreateAllocationRequest(
11848 currentFrameIndex,
11849 m_FrameInUseCount,
11850 m_BufferImageGranularity,
11851 size,
11852 alignment,
11853 isUpperAddress,
11854 suballocType,
11855 false, // canMakeOtherLost
11856 strategy,
11857 &currRequest))
11858 {
11859 // Allocate from pCurrBlock.
11860 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11861
11862 if(mapped)
11863 {
11864 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11865 if(res != VK_SUCCESS)
11866 {
11867 return res;
11868 }
11869 }
11870
11871 // We no longer have an empty Allocation.
11872 if(pBlock->m_pMetadata->IsEmpty())
11873 {
11874 m_HasEmptyBlock = false;
11875 }
11876
11877 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11878 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11879 (*pAllocation)->InitBlockAllocation(
11880 hCurrentPool,
11881 pBlock,
11882 currRequest.offset,
11883 alignment,
11884 size,
11885 suballocType,
11886 mapped,
11887 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11888 VMA_HEAVY_ASSERT(pBlock->Validate());
11889 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11890 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11891 {
11892 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11893 }
11894 if(IsCorruptionDetectionEnabled())
11895 {
11896 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11897 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11898 }
11899 return VK_SUCCESS;
11900 }
11901 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11902}
11903
11904VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11905{
11906 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11907 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11908 allocInfo.allocationSize = blockSize;
11909 VkDeviceMemory mem = VK_NULL_HANDLE;
11910 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11911 if(res < 0)
11912 {
11913 return res;
11914 }
11915
11916 // New VkDeviceMemory successfully created.
11917
11918 // Create new Allocation for it.
11919 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11920 pBlock->Init(
11921 m_hAllocator,
11922 m_MemoryTypeIndex,
11923 mem,
11924 allocInfo.allocationSize,
11925 m_NextBlockId++,
11926 m_Algorithm);
11927
11928 m_Blocks.push_back(pBlock);
11929 if(pNewBlockIndex != VMA_NULL)
11930 {
11931 *pNewBlockIndex = m_Blocks.size() - 1;
11932 }
11933
11934 return VK_SUCCESS;
11935}
11936
11937void VmaBlockVector::ApplyDefragmentationMovesCpu(
11938 class VmaBlockVectorDefragmentationContext* pDefragCtx,
11939 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11940{
11941 const size_t blockCount = m_Blocks.size();
11942 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11943
11944 enum BLOCK_FLAG
11945 {
11946 BLOCK_FLAG_USED = 0x00000001,
11947 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11948 };
11949
11950 struct BlockInfo
11951 {
11952 uint32_t flags;
11953 void* pMappedData;
11954 };
11955 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11956 blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11957 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
11958
11959 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11960 const size_t moveCount = moves.size();
11961 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11962 {
11963 const VmaDefragmentationMove& move = moves[moveIndex];
11964 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
11965 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
11966 }
11967
11968 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
11969
11970 // Go over all blocks. Get mapped pointer or map if necessary.
11971 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
11972 {
11973 BlockInfo& currBlockInfo = blockInfo[blockIndex];
11974 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
11975 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
11976 {
11977 currBlockInfo.pMappedData = pBlock->GetMappedData();
11978 // It is not originally mapped - map it.
11979 if(currBlockInfo.pMappedData == VMA_NULL)
11980 {
11981 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
11982 if(pDefragCtx->res == VK_SUCCESS)
11983 {
11984 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
11985 }
11986 }
11987 }
11988 }
11989
11990 // Go over all moves. Do actual data transfer.
11991 if(pDefragCtx->res == VK_SUCCESS)
11992 {
11993 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
11994 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
11995
11996 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11997 {
11998 const VmaDefragmentationMove& move = moves[moveIndex];
11999
12000 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12001 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12002
12003 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12004
12005 // Invalidate source.
12006 if(isNonCoherent)
12007 {
12008 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12009 memRange.memory = pSrcBlock->GetDeviceMemory();
12010 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12011 memRange.size = VMA_MIN(
12012 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12013 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12014 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12015 }
12016
12017 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12018 memmove(
12019 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12020 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12021 static_cast<size_t>(move.size));
12022
12023 if(IsCorruptionDetectionEnabled())
12024 {
12025 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12026 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12027 }
12028
12029 // Flush destination.
12030 if(isNonCoherent)
12031 {
12032 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12033 memRange.memory = pDstBlock->GetDeviceMemory();
12034 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12035 memRange.size = VMA_MIN(
12036 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12037 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12038 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12039 }
12040 }
12041 }
12042
12043 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12044 // Regardless of pCtx->res == VK_SUCCESS.
12045 for(size_t blockIndex = blockCount; blockIndex--; )
12046 {
12047 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12048 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12049 {
12050 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12051 pBlock->Unmap(m_hAllocator, 1);
12052 }
12053 }
12054}
12055
12056void VmaBlockVector::ApplyDefragmentationMovesGpu(
12057 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12058 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12059 VkCommandBuffer commandBuffer)
12060{
12061 const size_t blockCount = m_Blocks.size();
12062
12063 pDefragCtx->blockContexts.resize(blockCount);
12064 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12065
12066 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12067 const size_t moveCount = moves.size();
12068 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12069 {
12070 const VmaDefragmentationMove& move = moves[moveIndex];
12071 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12072 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12073 }
12074
12075 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12076
12077 // Go over all blocks. Create and bind buffer for whole block if necessary.
12078 {
12079 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12080 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12081 VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12082
12083 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12084 {
12085 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12086 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12087 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12088 {
12089 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12090 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12091 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12092 if(pDefragCtx->res == VK_SUCCESS)
12093 {
12094 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12095 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12096 }
12097 }
12098 }
12099 }
12100
12101 // Go over all moves. Post data transfer commands to command buffer.
12102 if(pDefragCtx->res == VK_SUCCESS)
12103 {
12104 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12105 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12106
12107 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12108 {
12109 const VmaDefragmentationMove& move = moves[moveIndex];
12110
12111 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12112 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12113
12114 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12115
12116 VkBufferCopy region = {
12117 move.srcOffset,
12118 move.dstOffset,
12119 move.size };
12120 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12121 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12122 }
12123 }
12124
12125 // Save buffers to defrag context for later destruction.
12126 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12127 {
12128 pDefragCtx->res = VK_NOT_READY;
12129 }
12130}
12131
12132void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12133{
12134 m_HasEmptyBlock = false;
12135 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12136 {
12137 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12138 if(pBlock->m_pMetadata->IsEmpty())
12139 {
12140 if(m_Blocks.size() > m_MinBlockCount)
12141 {
12142 if(pDefragmentationStats != VMA_NULL)
12143 {
12144 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12145 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12146 }
12147
12148 VmaVectorRemove(m_Blocks, blockIndex);
12149 pBlock->Destroy(m_hAllocator);
12150 vma_delete(m_hAllocator, pBlock);
12151 }
12152 else
12153 {
12154 m_HasEmptyBlock = true;
12155 }
12156 }
12157 }
12158}
12159
12160#if VMA_STATS_STRING_ENABLED
12161
12162void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12163{
12164 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12165
12166 json.BeginObject();
12167
12168 if(m_IsCustomPool)
12169 {
12170 json.WriteString("MemoryTypeIndex");
12171 json.WriteNumber(m_MemoryTypeIndex);
12172
12173 json.WriteString("BlockSize");
12174 json.WriteNumber(m_PreferredBlockSize);
12175
12176 json.WriteString("BlockCount");
12177 json.BeginObject(true);
12178 if(m_MinBlockCount > 0)
12179 {
12180 json.WriteString("Min");
12181 json.WriteNumber((uint64_t)m_MinBlockCount);
12182 }
12183 if(m_MaxBlockCount < SIZE_MAX)
12184 {
12185 json.WriteString("Max");
12186 json.WriteNumber((uint64_t)m_MaxBlockCount);
12187 }
12188 json.WriteString("Cur");
12189 json.WriteNumber((uint64_t)m_Blocks.size());
12190 json.EndObject();
12191
12192 if(m_FrameInUseCount > 0)
12193 {
12194 json.WriteString("FrameInUseCount");
12195 json.WriteNumber(m_FrameInUseCount);
12196 }
12197
12198 if(m_Algorithm != 0)
12199 {
12200 json.WriteString("Algorithm");
12201 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12202 }
12203 }
12204 else
12205 {
12206 json.WriteString("PreferredBlockSize");
12207 json.WriteNumber(m_PreferredBlockSize);
12208 }
12209
12210 json.WriteString("Blocks");
12211 json.BeginObject();
12212 for(size_t i = 0; i < m_Blocks.size(); ++i)
12213 {
12214 json.BeginString();
12215 json.ContinueString(m_Blocks[i]->GetId());
12216 json.EndString();
12217
12218 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12219 }
12220 json.EndObject();
12221
12222 json.EndObject();
12223}
12224
12225#endif // #if VMA_STATS_STRING_ENABLED
12226
12227void VmaBlockVector::Defragment(
12228 class VmaBlockVectorDefragmentationContext* pCtx,
12229 VmaDefragmentationStats* pStats,
12230 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12231 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12232 VkCommandBuffer commandBuffer)
12233{
12234 pCtx->res = VK_SUCCESS;
12235
12236 const VkMemoryPropertyFlags memPropFlags =
12237 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12238 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12239 const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12240
12241 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12242 isHostVisible;
12243 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12244 (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12245
12246 // There are options to defragment this memory type.
12247 if(canDefragmentOnCpu || canDefragmentOnGpu)
12248 {
12249 bool defragmentOnGpu;
12250 // There is only one option to defragment this memory type.
12251 if(canDefragmentOnGpu != canDefragmentOnCpu)
12252 {
12253 defragmentOnGpu = canDefragmentOnGpu;
12254 }
12255 // Both options are available: Heuristics to choose the best one.
12256 else
12257 {
12258 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12259 m_hAllocator->IsIntegratedGpu();
12260 }
12261
12262 bool overlappingMoveSupported = !defragmentOnGpu;
12263
12264 if(m_hAllocator->m_UseMutex)
12265 {
12266 m_Mutex.LockWrite();
12267 pCtx->mutexLocked = true;
12268 }
12269
12270 pCtx->Begin(overlappingMoveSupported);
12271
12272 // Defragment.
12273
12274 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12275 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12276 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12277 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12278 pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12279
12280 // Accumulate statistics.
12281 if(pStats != VMA_NULL)
12282 {
12283 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12284 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12285 pStats->bytesMoved += bytesMoved;
12286 pStats->allocationsMoved += allocationsMoved;
12287 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12288 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12289 if(defragmentOnGpu)
12290 {
12291 maxGpuBytesToMove -= bytesMoved;
12292 maxGpuAllocationsToMove -= allocationsMoved;
12293 }
12294 else
12295 {
12296 maxCpuBytesToMove -= bytesMoved;
12297 maxCpuAllocationsToMove -= allocationsMoved;
12298 }
12299 }
12300
12301 if(pCtx->res >= VK_SUCCESS)
12302 {
12303 if(defragmentOnGpu)
12304 {
12305 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12306 }
12307 else
12308 {
12309 ApplyDefragmentationMovesCpu(pCtx, moves);
12310 }
12311 }
12312 }
12313}
12314
12315void VmaBlockVector::DefragmentationEnd(
12316 class VmaBlockVectorDefragmentationContext* pCtx,
12317 VmaDefragmentationStats* pStats)
12318{
12319 // Destroy buffers.
12320 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12321 {
12322 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12323 if(blockCtx.hBuffer)
12324 {
12325 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12326 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12327 }
12328 }
12329
12330 if(pCtx->res >= VK_SUCCESS)
12331 {
12332 FreeEmptyBlocks(pStats);
12333 }
12334
12335 if(pCtx->mutexLocked)
12336 {
12337 VMA_ASSERT(m_hAllocator->m_UseMutex);
12338 m_Mutex.UnlockWrite();
12339 }
12340}
12341
12342size_t VmaBlockVector::CalcAllocationCount() const
12343{
12344 size_t result = 0;
12345 for(size_t i = 0; i < m_Blocks.size(); ++i)
12346 {
12347 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12348 }
12349 return result;
12350}
12351
12352bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12353{
12354 if(m_BufferImageGranularity == 1)
12355 {
12356 return false;
12357 }
12358 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12359 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12360 {
12361 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12362 VMA_ASSERT(m_Algorithm == 0);
12363 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12364 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12365 {
12366 return true;
12367 }
12368 }
12369 return false;
12370}
12371
12372void VmaBlockVector::MakePoolAllocationsLost(
12373 uint32_t currentFrameIndex,
12374 size_t* pLostAllocationCount)
12375{
12376 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12377 size_t lostAllocationCount = 0;
12378 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12379 {
12380 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12381 VMA_ASSERT(pBlock);
12382 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12383 }
12384 if(pLostAllocationCount != VMA_NULL)
12385 {
12386 *pLostAllocationCount = lostAllocationCount;
12387 }
12388}
12389
12390VkResult VmaBlockVector::CheckCorruption()
12391{
12392 if(!IsCorruptionDetectionEnabled())
12393 {
12394 return VK_ERROR_FEATURE_NOT_PRESENT;
12395 }
12396
12397 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12398 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12399 {
12400 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12401 VMA_ASSERT(pBlock);
12402 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12403 if(res != VK_SUCCESS)
12404 {
12405 return res;
12406 }
12407 }
12408 return VK_SUCCESS;
12409}
12410
12411void VmaBlockVector::AddStats(VmaStats* pStats)
12412{
12413 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12414 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12415
12416 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12417
12418 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12419 {
12420 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12421 VMA_ASSERT(pBlock);
12422 VMA_HEAVY_ASSERT(pBlock->Validate());
12423 VmaStatInfo allocationStatInfo;
12424 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12425 VmaAddStatInfo(pStats->total, allocationStatInfo);
12426 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12427 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12428 }
12429}
12430
12431////////////////////////////////////////////////////////////////////////////////
12432// VmaDefragmentationAlgorithm_Generic members definition
12433
12434VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12435 VmaAllocator hAllocator,
12436 VmaBlockVector* pBlockVector,
12437 uint32_t currentFrameIndex,
12438 bool overlappingMoveSupported) :
12439 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12440 m_AllAllocations(false),
12441 m_AllocationCount(0),
12442 m_BytesMoved(0),
12443 m_AllocationsMoved(0),
12444 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12445{
12446 // Create block info for each block.
12447 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12448 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12449 {
12450 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12451 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12452 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12453 m_Blocks.push_back(pBlockInfo);
12454 }
12455
12456 // Sort them by m_pBlock pointer value.
12457 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12458}
12459
12460VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12461{
12462 for(size_t i = m_Blocks.size(); i--; )
12463 {
12464 vma_delete(m_hAllocator, m_Blocks[i]);
12465 }
12466}
12467
12468void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12469{
12470 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12471 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12472 {
12473 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12474 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12475 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12476 {
12477 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12478 (*it)->m_Allocations.push_back(allocInfo);
12479 }
12480 else
12481 {
12482 VMA_ASSERT(0);
12483 }
12484
12485 ++m_AllocationCount;
12486 }
12487}
12488
12489VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12490 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12491 VkDeviceSize maxBytesToMove,
12492 uint32_t maxAllocationsToMove)
12493{
12494 if(m_Blocks.empty())
12495 {
12496 return VK_SUCCESS;
12497 }
12498
12499 // This is a choice based on research.
12500 // Option 1:
12501 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12502 // Option 2:
12503 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12504 // Option 3:
12505 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12506
12507 size_t srcBlockMinIndex = 0;
12508 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12509 /*
12510 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12511 {
12512 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12513 if(blocksWithNonMovableCount > 0)
12514 {
12515 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12516 }
12517 }
12518 */
12519
12520 size_t srcBlockIndex = m_Blocks.size() - 1;
12521 size_t srcAllocIndex = SIZE_MAX;
12522 for(;;)
12523 {
12524 // 1. Find next allocation to move.
12525 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12526 // 1.2. Then start from last to first m_Allocations.
12527 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12528 {
12529 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12530 {
12531 // Finished: no more allocations to process.
12532 if(srcBlockIndex == srcBlockMinIndex)
12533 {
12534 return VK_SUCCESS;
12535 }
12536 else
12537 {
12538 --srcBlockIndex;
12539 srcAllocIndex = SIZE_MAX;
12540 }
12541 }
12542 else
12543 {
12544 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12545 }
12546 }
12547
12548 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12549 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12550
12551 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12552 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12553 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12554 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12555
12556 // 2. Try to find new place for this allocation in preceding or current block.
12557 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12558 {
12559 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12560 VmaAllocationRequest dstAllocRequest;
12561 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12562 m_CurrentFrameIndex,
12563 m_pBlockVector->GetFrameInUseCount(),
12564 m_pBlockVector->GetBufferImageGranularity(),
12565 size,
12566 alignment,
12567 false, // upperAddress
12568 suballocType,
12569 false, // canMakeOtherLost
12570 strategy,
12571 &dstAllocRequest) &&
12572 MoveMakesSense(
12573 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12574 {
12575 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12576
12577 // Reached limit on number of allocations or bytes to move.
12578 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12579 (m_BytesMoved + size > maxBytesToMove))
12580 {
12581 return VK_SUCCESS;
12582 }
12583
12584 VmaDefragmentationMove move;
12585 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12586 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12587 move.srcOffset = srcOffset;
12588 move.dstOffset = dstAllocRequest.offset;
12589 move.size = size;
12590 moves.push_back(move);
12591
12592 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12593 dstAllocRequest,
12594 suballocType,
12595 size,
12596 false, // upperAddress
12597 allocInfo.m_hAllocation);
12598 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12599
12600 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12601
12602 if(allocInfo.m_pChanged != VMA_NULL)
12603 {
12604 *allocInfo.m_pChanged = VK_TRUE;
12605 }
12606
12607 ++m_AllocationsMoved;
12608 m_BytesMoved += size;
12609
12610 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12611
12612 break;
12613 }
12614 }
12615
12616 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12617
12618 if(srcAllocIndex > 0)
12619 {
12620 --srcAllocIndex;
12621 }
12622 else
12623 {
12624 if(srcBlockIndex > 0)
12625 {
12626 --srcBlockIndex;
12627 srcAllocIndex = SIZE_MAX;
12628 }
12629 else
12630 {
12631 return VK_SUCCESS;
12632 }
12633 }
12634 }
12635}
12636
12637size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12638{
12639 size_t result = 0;
12640 for(size_t i = 0; i < m_Blocks.size(); ++i)
12641 {
12642 if(m_Blocks[i]->m_HasNonMovableAllocations)
12643 {
12644 ++result;
12645 }
12646 }
12647 return result;
12648}
12649
12650VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12651 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12652 VkDeviceSize maxBytesToMove,
12653 uint32_t maxAllocationsToMove)
12654{
12655 if(!m_AllAllocations && m_AllocationCount == 0)
12656 {
12657 return VK_SUCCESS;
12658 }
12659
12660 const size_t blockCount = m_Blocks.size();
12661 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12662 {
12663 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12664
12665 if(m_AllAllocations)
12666 {
12667 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12668 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12669 it != pMetadata->m_Suballocations.end();
12670 ++it)
12671 {
12672 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12673 {
12674 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12675 pBlockInfo->m_Allocations.push_back(allocInfo);
12676 }
12677 }
12678 }
12679
12680 pBlockInfo->CalcHasNonMovableAllocations();
12681
12682 // This is a choice based on research.
12683 // Option 1:
12684 pBlockInfo->SortAllocationsByOffsetDescending();
12685 // Option 2:
12686 //pBlockInfo->SortAllocationsBySizeDescending();
12687 }
12688
12689 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12690 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12691
12692 // This is a choice based on research.
12693 const uint32_t roundCount = 2;
12694
12695 // Execute defragmentation rounds (the main part).
12696 VkResult result = VK_SUCCESS;
12697 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12698 {
12699 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12700 }
12701
12702 return result;
12703}
12704
12705bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12706 size_t dstBlockIndex, VkDeviceSize dstOffset,
12707 size_t srcBlockIndex, VkDeviceSize srcOffset)
12708{
12709 if(dstBlockIndex < srcBlockIndex)
12710 {
12711 return true;
12712 }
12713 if(dstBlockIndex > srcBlockIndex)
12714 {
12715 return false;
12716 }
12717 if(dstOffset < srcOffset)
12718 {
12719 return true;
12720 }
12721 return false;
12722}
12723
12724////////////////////////////////////////////////////////////////////////////////
12725// VmaDefragmentationAlgorithm_Fast
12726
12727VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12728 VmaAllocator hAllocator,
12729 VmaBlockVector* pBlockVector,
12730 uint32_t currentFrameIndex,
12731 bool overlappingMoveSupported) :
12732 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12733 m_OverlappingMoveSupported(overlappingMoveSupported),
12734 m_AllocationCount(0),
12735 m_AllAllocations(false),
12736 m_BytesMoved(0),
12737 m_AllocationsMoved(0),
12738 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12739{
12740 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12741
12742}
12743
12744VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12745{
12746}
12747
12748VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12749 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12750 VkDeviceSize maxBytesToMove,
12751 uint32_t maxAllocationsToMove)
12752{
12753 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12754
12755 const size_t blockCount = m_pBlockVector->GetBlockCount();
12756 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12757 {
12758 return VK_SUCCESS;
12759 }
12760
12761 PreprocessMetadata();
12762
12763 // Sort blocks in order from most destination.
12764
12765 m_BlockInfos.resize(blockCount);
12766 for(size_t i = 0; i < blockCount; ++i)
12767 {
12768 m_BlockInfos[i].origBlockIndex = i;
12769 }
12770
12771 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12772 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12773 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12774 });
12775
12776 // THE MAIN ALGORITHM
12777
12778 FreeSpaceDatabase freeSpaceDb;
12779
12780 size_t dstBlockInfoIndex = 0;
12781 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12782 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12783 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12784 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12785 VkDeviceSize dstOffset = 0;
12786
12787 bool end = false;
12788 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12789 {
12790 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12791 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12792 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12793 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12794 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12795 {
12796 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12797 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12798 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12799 if(m_AllocationsMoved == maxAllocationsToMove ||
12800 m_BytesMoved + srcAllocSize > maxBytesToMove)
12801 {
12802 end = true;
12803 break;
12804 }
12805 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12806
12807 // Try to place it in one of free spaces from the database.
12808 size_t freeSpaceInfoIndex;
12809 VkDeviceSize dstAllocOffset;
12810 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12811 freeSpaceInfoIndex, dstAllocOffset))
12812 {
12813 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12814 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12815 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12816 VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12817
12818 // Same block
12819 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12820 {
12821 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12822
12823 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12824
12825 VmaSuballocation suballoc = *srcSuballocIt;
12826 suballoc.offset = dstAllocOffset;
12827 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12828 m_BytesMoved += srcAllocSize;
12829 ++m_AllocationsMoved;
12830
12831 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12832 ++nextSuballocIt;
12833 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12834 srcSuballocIt = nextSuballocIt;
12835
12836 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12837
12838 VmaDefragmentationMove move = {
12839 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12840 srcAllocOffset, dstAllocOffset,
12841 srcAllocSize };
12842 moves.push_back(move);
12843 }
12844 // Different block
12845 else
12846 {
12847 // MOVE OPTION 2: Move the allocation to a different block.
12848
12849 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12850
12851 VmaSuballocation suballoc = *srcSuballocIt;
12852 suballoc.offset = dstAllocOffset;
12853 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12854 m_BytesMoved += srcAllocSize;
12855 ++m_AllocationsMoved;
12856
12857 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12858 ++nextSuballocIt;
12859 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12860 srcSuballocIt = nextSuballocIt;
12861
12862 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12863
12864 VmaDefragmentationMove move = {
12865 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12866 srcAllocOffset, dstAllocOffset,
12867 srcAllocSize };
12868 moves.push_back(move);
12869 }
12870 }
12871 else
12872 {
12873 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12874
12875 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12876 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12877 dstAllocOffset + srcAllocSize > dstBlockSize)
12878 {
12879 // But before that, register remaining free space at the end of dst block.
12880 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12881
12882 ++dstBlockInfoIndex;
12883 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12884 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12885 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12886 dstBlockSize = pDstMetadata->GetSize();
12887 dstOffset = 0;
12888 dstAllocOffset = 0;
12889 }
12890
12891 // Same block
12892 if(dstBlockInfoIndex == srcBlockInfoIndex)
12893 {
12894 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12895
12896 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12897
12898 bool skipOver = overlap;
12899 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12900 {
12901 // If destination and source place overlap, skip if it would move it
12902 // by only < 1/64 of its size.
12903 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12904 }
12905
12906 if(skipOver)
12907 {
12908 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12909
12910 dstOffset = srcAllocOffset + srcAllocSize;
12911 ++srcSuballocIt;
12912 }
12913 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12914 else
12915 {
12916 srcSuballocIt->offset = dstAllocOffset;
12917 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12918 dstOffset = dstAllocOffset + srcAllocSize;
12919 m_BytesMoved += srcAllocSize;
12920 ++m_AllocationsMoved;
12921 ++srcSuballocIt;
12922 VmaDefragmentationMove move = {
12923 srcOrigBlockIndex, dstOrigBlockIndex,
12924 srcAllocOffset, dstAllocOffset,
12925 srcAllocSize };
12926 moves.push_back(move);
12927 }
12928 }
12929 // Different block
12930 else
12931 {
12932 // MOVE OPTION 2: Move the allocation to a different block.
12933
12934 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12935 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12936
12937 VmaSuballocation suballoc = *srcSuballocIt;
12938 suballoc.offset = dstAllocOffset;
12939 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12940 dstOffset = dstAllocOffset + srcAllocSize;
12941 m_BytesMoved += srcAllocSize;
12942 ++m_AllocationsMoved;
12943
12944 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12945 ++nextSuballocIt;
12946 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12947 srcSuballocIt = nextSuballocIt;
12948
12949 pDstMetadata->m_Suballocations.push_back(suballoc);
12950
12951 VmaDefragmentationMove move = {
12952 srcOrigBlockIndex, dstOrigBlockIndex,
12953 srcAllocOffset, dstAllocOffset,
12954 srcAllocSize };
12955 moves.push_back(move);
12956 }
12957 }
12958 }
12959 }
12960
12961 m_BlockInfos.clear();
12962
12963 PostprocessMetadata();
12964
12965 return VK_SUCCESS;
12966}
12967
12968void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
12969{
12970 const size_t blockCount = m_pBlockVector->GetBlockCount();
12971 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12972 {
12973 VmaBlockMetadata_Generic* const pMetadata =
12974 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
12975 pMetadata->m_FreeCount = 0;
12976 pMetadata->m_SumFreeSize = pMetadata->GetSize();
12977 pMetadata->m_FreeSuballocationsBySize.clear();
12978 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
12979 it != pMetadata->m_Suballocations.end(); )
12980 {
12981 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
12982 {
12983 VmaSuballocationList::iterator nextIt = it;
12984 ++nextIt;
12985 pMetadata->m_Suballocations.erase(it);
12986 it = nextIt;
12987 }
12988 else
12989 {
12990 ++it;
12991 }
12992 }
12993 }
12994}
12995
12996void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
12997{
12998 const size_t blockCount = m_pBlockVector->GetBlockCount();
12999 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13000 {
13001 VmaBlockMetadata_Generic* const pMetadata =
13002 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13003 const VkDeviceSize blockSize = pMetadata->GetSize();
13004
13005 // No allocations in this block - entire area is free.
13006 if(pMetadata->m_Suballocations.empty())
13007 {
13008 pMetadata->m_FreeCount = 1;
13009 //pMetadata->m_SumFreeSize is already set to blockSize.
13010 VmaSuballocation suballoc = {
13011 0, // offset
13012 blockSize, // size
13013 VMA_NULL, // hAllocation
13014 VMA_SUBALLOCATION_TYPE_FREE };
13015 pMetadata->m_Suballocations.push_back(suballoc);
13016 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13017 }
13018 // There are some allocations in this block.
13019 else
13020 {
13021 VkDeviceSize offset = 0;
13022 VmaSuballocationList::iterator it;
13023 for(it = pMetadata->m_Suballocations.begin();
13024 it != pMetadata->m_Suballocations.end();
13025 ++it)
13026 {
13027 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13028 VMA_ASSERT(it->offset >= offset);
13029
13030 // Need to insert preceding free space.
13031 if(it->offset > offset)
13032 {
13033 ++pMetadata->m_FreeCount;
13034 const VkDeviceSize freeSize = it->offset - offset;
13035 VmaSuballocation suballoc = {
13036 offset, // offset
13037 freeSize, // size
13038 VMA_NULL, // hAllocation
13039 VMA_SUBALLOCATION_TYPE_FREE };
13040 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13041 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13042 {
13043 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13044 }
13045 }
13046
13047 pMetadata->m_SumFreeSize -= it->size;
13048 offset = it->offset + it->size;
13049 }
13050
13051 // Need to insert trailing free space.
13052 if(offset < blockSize)
13053 {
13054 ++pMetadata->m_FreeCount;
13055 const VkDeviceSize freeSize = blockSize - offset;
13056 VmaSuballocation suballoc = {
13057 offset, // offset
13058 freeSize, // size
13059 VMA_NULL, // hAllocation
13060 VMA_SUBALLOCATION_TYPE_FREE };
13061 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13062 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13063 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13064 {
13065 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13066 }
13067 }
13068
13069 VMA_SORT(
13070 pMetadata->m_FreeSuballocationsBySize.begin(),
13071 pMetadata->m_FreeSuballocationsBySize.end(),
13072 VmaSuballocationItemSizeLess());
13073 }
13074
13075 VMA_HEAVY_ASSERT(pMetadata->Validate());
13076 }
13077}
13078
13079void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13080{
13081 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13082 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13083 while(it != pMetadata->m_Suballocations.end())
13084 {
13085 if(it->offset < suballoc.offset)
13086 {
13087 ++it;
13088 }
13089 }
13090 pMetadata->m_Suballocations.insert(it, suballoc);
13091}
13092
13093////////////////////////////////////////////////////////////////////////////////
13094// VmaBlockVectorDefragmentationContext
13095
13096VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13097 VmaAllocator hAllocator,
13098 VmaPool hCustomPool,
13099 VmaBlockVector* pBlockVector,
13100 uint32_t currFrameIndex,
13101 uint32_t algorithmFlags) :
13102 res(VK_SUCCESS),
13103 mutexLocked(false),
13104 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13105 m_hAllocator(hAllocator),
13106 m_hCustomPool(hCustomPool),
13107 m_pBlockVector(pBlockVector),
13108 m_CurrFrameIndex(currFrameIndex),
13109 m_AlgorithmFlags(algorithmFlags),
13110 m_pAlgorithm(VMA_NULL),
13111 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13112 m_AllAllocations(false)
13113{
13114}
13115
13116VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13117{
13118 vma_delete(m_hAllocator, m_pAlgorithm);
13119}
13120
13121void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13122{
13123 AllocInfo info = { hAlloc, pChanged };
13124 m_Allocations.push_back(info);
13125}
13126
13127void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13128{
13129 const bool allAllocations = m_AllAllocations ||
13130 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13131
13132 /********************************
13133 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13134 ********************************/
13135
13136 /*
13137 Fast algorithm is supported only when certain criteria are met:
13138 - VMA_DEBUG_MARGIN is 0.
13139 - All allocations in this block vector are moveable.
13140 - There is no possibility of image/buffer granularity conflict.
13141 */
13142 if(VMA_DEBUG_MARGIN == 0 &&
13143 allAllocations &&
13144 !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13145 {
13146 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13147 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13148 }
13149 else
13150 {
13151 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13152 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13153 }
13154
13155 if(allAllocations)
13156 {
13157 m_pAlgorithm->AddAll();
13158 }
13159 else
13160 {
13161 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13162 {
13163 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13164 }
13165 }
13166}
13167
13168////////////////////////////////////////////////////////////////////////////////
13169// VmaDefragmentationContext
13170
13171VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13172 VmaAllocator hAllocator,
13173 uint32_t currFrameIndex,
13174 uint32_t flags,
13175 VmaDefragmentationStats* pStats) :
13176 m_hAllocator(hAllocator),
13177 m_CurrFrameIndex(currFrameIndex),
13178 m_Flags(flags),
13179 m_pStats(pStats),
13180 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13181{
13182 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13183}
13184
13185VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13186{
13187 for(size_t i = m_CustomPoolContexts.size(); i--; )
13188 {
13189 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13190 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13191 vma_delete(m_hAllocator, pBlockVectorCtx);
13192 }
13193 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13194 {
13195 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13196 if(pBlockVectorCtx)
13197 {
13198 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13199 vma_delete(m_hAllocator, pBlockVectorCtx);
13200 }
13201 }
13202}
13203
13204void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13205{
13206 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13207 {
13208 VmaPool pool = pPools[poolIndex];
13209 VMA_ASSERT(pool);
13210 // Pools with algorithm other than default are not defragmented.
13211 if(pool->m_BlockVector.GetAlgorithm() == 0)
13212 {
13213 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13214
13215 for(size_t i = m_CustomPoolContexts.size(); i--; )
13216 {
13217 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13218 {
13219 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13220 break;
13221 }
13222 }
13223
13224 if(!pBlockVectorDefragCtx)
13225 {
13226 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13227 m_hAllocator,
13228 pool,
13229 &pool->m_BlockVector,
13230 m_CurrFrameIndex,
13231 m_Flags);
13232 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13233 }
13234
13235 pBlockVectorDefragCtx->AddAll();
13236 }
13237 }
13238}
13239
13240void VmaDefragmentationContext_T::AddAllocations(
13241 uint32_t allocationCount,
13242 VmaAllocation* pAllocations,
13243 VkBool32* pAllocationsChanged)
13244{
13245 // Dispatch pAllocations among defragmentators. Create them when necessary.
13246 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13247 {
13248 const VmaAllocation hAlloc = pAllocations[allocIndex];
13249 VMA_ASSERT(hAlloc);
13250 // DedicatedAlloc cannot be defragmented.
13251 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13252 // Lost allocation cannot be defragmented.
13253 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13254 {
13255 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13256
13257 const VmaPool hAllocPool = hAlloc->GetPool();
13258 // This allocation belongs to custom pool.
13259 if(hAllocPool != VK_NULL_HANDLE)
13260 {
13261 // Pools with algorithm other than default are not defragmented.
13262 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13263 {
13264 for(size_t i = m_CustomPoolContexts.size(); i--; )
13265 {
13266 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13267 {
13268 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13269 break;
13270 }
13271 }
13272 if(!pBlockVectorDefragCtx)
13273 {
13274 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13275 m_hAllocator,
13276 hAllocPool,
13277 &hAllocPool->m_BlockVector,
13278 m_CurrFrameIndex,
13279 m_Flags);
13280 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13281 }
13282 }
13283 }
13284 // This allocation belongs to default pool.
13285 else
13286 {
13287 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13288 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13289 if(!pBlockVectorDefragCtx)
13290 {
13291 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13292 m_hAllocator,
13293 VMA_NULL, // hCustomPool
13294 m_hAllocator->m_pBlockVectors[memTypeIndex],
13295 m_CurrFrameIndex,
13296 m_Flags);
13297 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13298 }
13299 }
13300
13301 if(pBlockVectorDefragCtx)
13302 {
13303 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13304 &pAllocationsChanged[allocIndex] : VMA_NULL;
13305 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13306 }
13307 }
13308 }
13309}
13310
13311VkResult VmaDefragmentationContext_T::Defragment(
13312 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13313 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13314 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13315{
13316 if(pStats)
13317 {
13318 memset(pStats, 0, sizeof(VmaDefragmentationStats));
13319 }
13320
13321 if(commandBuffer == VK_NULL_HANDLE)
13322 {
13323 maxGpuBytesToMove = 0;
13324 maxGpuAllocationsToMove = 0;
13325 }
13326
13327 VkResult res = VK_SUCCESS;
13328
13329 // Process default pools.
13330 for(uint32_t memTypeIndex = 0;
13331 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13332 ++memTypeIndex)
13333 {
13334 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13335 if(pBlockVectorCtx)
13336 {
13337 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13338 pBlockVectorCtx->GetBlockVector()->Defragment(
13339 pBlockVectorCtx,
13340 pStats,
13341 maxCpuBytesToMove, maxCpuAllocationsToMove,
13342 maxGpuBytesToMove, maxGpuAllocationsToMove,
13343 commandBuffer);
13344 if(pBlockVectorCtx->res != VK_SUCCESS)
13345 {
13346 res = pBlockVectorCtx->res;
13347 }
13348 }
13349 }
13350
13351 // Process custom pools.
13352 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13353 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13354 ++customCtxIndex)
13355 {
13356 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13357 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13358 pBlockVectorCtx->GetBlockVector()->Defragment(
13359 pBlockVectorCtx,
13360 pStats,
13361 maxCpuBytesToMove, maxCpuAllocationsToMove,
13362 maxGpuBytesToMove, maxGpuAllocationsToMove,
13363 commandBuffer);
13364 if(pBlockVectorCtx->res != VK_SUCCESS)
13365 {
13366 res = pBlockVectorCtx->res;
13367 }
13368 }
13369
13370 return res;
13371}
13372
13373////////////////////////////////////////////////////////////////////////////////
13374// VmaRecorder
13375
13376#if VMA_RECORDING_ENABLED
13377
13378VmaRecorder::VmaRecorder() :
13379 m_UseMutex(true),
13380 m_Flags(0),
13381 m_File(VMA_NULL),
13382 m_Freq(INT64_MAX),
13383 m_StartCounter(INT64_MAX)
13384{
13385}
13386
13387VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13388{
13389 m_UseMutex = useMutex;
13390 m_Flags = settings.flags;
13391
13392 QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13393 QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13394
13395 // Open file for writing.
13396 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13397 if(err != 0)
13398 {
13399 return VK_ERROR_INITIALIZATION_FAILED;
13400 }
13401
13402 // Write header.
13403 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13404 fprintf(m_File, "%s\n", "1,5");
13405
13406 return VK_SUCCESS;
13407}
13408
13409VmaRecorder::~VmaRecorder()
13410{
13411 if(m_File != VMA_NULL)
13412 {
13413 fclose(m_File);
13414 }
13415}
13416
13417void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13418{
13419 CallParams callParams;
13420 GetBasicParams(callParams);
13421
13422 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13423 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13424 Flush();
13425}
13426
13427void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13428{
13429 CallParams callParams;
13430 GetBasicParams(callParams);
13431
13432 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13433 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13434 Flush();
13435}
13436
13437void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13438{
13439 CallParams callParams;
13440 GetBasicParams(callParams);
13441
13442 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13443 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13444 createInfo.memoryTypeIndex,
13445 createInfo.flags,
13446 createInfo.blockSize,
13447 (uint64_t)createInfo.minBlockCount,
13448 (uint64_t)createInfo.maxBlockCount,
13449 createInfo.frameInUseCount,
13450 pool);
13451 Flush();
13452}
13453
13454void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13455{
13456 CallParams callParams;
13457 GetBasicParams(callParams);
13458
13459 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13460 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13461 pool);
13462 Flush();
13463}
13464
13465void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13466 const VkMemoryRequirements& vkMemReq,
13467 const VmaAllocationCreateInfo& createInfo,
13468 VmaAllocation allocation)
13469{
13470 CallParams callParams;
13471 GetBasicParams(callParams);
13472
13473 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13474 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13475 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13476 vkMemReq.size,
13477 vkMemReq.alignment,
13478 vkMemReq.memoryTypeBits,
13479 createInfo.flags,
13480 createInfo.usage,
13481 createInfo.requiredFlags,
13482 createInfo.preferredFlags,
13483 createInfo.memoryTypeBits,
13484 createInfo.pool,
13485 allocation,
13486 userDataStr.GetString());
13487 Flush();
13488}
13489
13490void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13491 const VkMemoryRequirements& vkMemReq,
13492 const VmaAllocationCreateInfo& createInfo,
13493 uint64_t allocationCount,
13494 const VmaAllocation* pAllocations)
13495{
13496 CallParams callParams;
13497 GetBasicParams(callParams);
13498
13499 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13500 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13501 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13502 vkMemReq.size,
13503 vkMemReq.alignment,
13504 vkMemReq.memoryTypeBits,
13505 createInfo.flags,
13506 createInfo.usage,
13507 createInfo.requiredFlags,
13508 createInfo.preferredFlags,
13509 createInfo.memoryTypeBits,
13510 createInfo.pool);
13511 PrintPointerList(allocationCount, pAllocations);
13512 fprintf(m_File, ",%s\n", userDataStr.GetString());
13513 Flush();
13514}
13515
13516void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13517 const VkMemoryRequirements& vkMemReq,
13518 bool requiresDedicatedAllocation,
13519 bool prefersDedicatedAllocation,
13520 const VmaAllocationCreateInfo& createInfo,
13521 VmaAllocation allocation)
13522{
13523 CallParams callParams;
13524 GetBasicParams(callParams);
13525
13526 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13527 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13528 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,
13529 vkMemReq.size,
13530 vkMemReq.alignment,
13531 vkMemReq.memoryTypeBits,
13532 requiresDedicatedAllocation ? 1 : 0,
13533 prefersDedicatedAllocation ? 1 : 0,
13534 createInfo.flags,
13535 createInfo.usage,
13536 createInfo.requiredFlags,
13537 createInfo.preferredFlags,
13538 createInfo.memoryTypeBits,
13539 createInfo.pool,
13540 allocation,
13541 userDataStr.GetString());
13542 Flush();
13543}
13544
13545void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13546 const VkMemoryRequirements& vkMemReq,
13547 bool requiresDedicatedAllocation,
13548 bool prefersDedicatedAllocation,
13549 const VmaAllocationCreateInfo& createInfo,
13550 VmaAllocation allocation)
13551{
13552 CallParams callParams;
13553 GetBasicParams(callParams);
13554
13555 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13556 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13557 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,
13558 vkMemReq.size,
13559 vkMemReq.alignment,
13560 vkMemReq.memoryTypeBits,
13561 requiresDedicatedAllocation ? 1 : 0,
13562 prefersDedicatedAllocation ? 1 : 0,
13563 createInfo.flags,
13564 createInfo.usage,
13565 createInfo.requiredFlags,
13566 createInfo.preferredFlags,
13567 createInfo.memoryTypeBits,
13568 createInfo.pool,
13569 allocation,
13570 userDataStr.GetString());
13571 Flush();
13572}
13573
13574void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13575 VmaAllocation allocation)
13576{
13577 CallParams callParams;
13578 GetBasicParams(callParams);
13579
13580 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13581 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13582 allocation);
13583 Flush();
13584}
13585
13586void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13587 uint64_t allocationCount,
13588 const VmaAllocation* pAllocations)
13589{
13590 CallParams callParams;
13591 GetBasicParams(callParams);
13592
13593 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13594 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13595 PrintPointerList(allocationCount, pAllocations);
13596 fprintf(m_File, "\n");
13597 Flush();
13598}
13599
13600void VmaRecorder::RecordResizeAllocation(
13601 uint32_t frameIndex,
13602 VmaAllocation allocation,
13603 VkDeviceSize newSize)
13604{
13605 CallParams callParams;
13606 GetBasicParams(callParams);
13607
13608 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13609 fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13610 allocation, newSize);
13611 Flush();
13612}
13613
13614void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13615 VmaAllocation allocation,
13616 const void* pUserData)
13617{
13618 CallParams callParams;
13619 GetBasicParams(callParams);
13620
13621 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13622 UserDataString userDataStr(
13623 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13624 pUserData);
13625 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13626 allocation,
13627 userDataStr.GetString());
13628 Flush();
13629}
13630
13631void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13632 VmaAllocation allocation)
13633{
13634 CallParams callParams;
13635 GetBasicParams(callParams);
13636
13637 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13638 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13639 allocation);
13640 Flush();
13641}
13642
13643void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13644 VmaAllocation allocation)
13645{
13646 CallParams callParams;
13647 GetBasicParams(callParams);
13648
13649 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13650 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13651 allocation);
13652 Flush();
13653}
13654
13655void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13656 VmaAllocation allocation)
13657{
13658 CallParams callParams;
13659 GetBasicParams(callParams);
13660
13661 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13662 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13663 allocation);
13664 Flush();
13665}
13666
13667void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13668 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13669{
13670 CallParams callParams;
13671 GetBasicParams(callParams);
13672
13673 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13674 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13675 allocation,
13676 offset,
13677 size);
13678 Flush();
13679}
13680
13681void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13682 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13683{
13684 CallParams callParams;
13685 GetBasicParams(callParams);
13686
13687 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13688 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13689 allocation,
13690 offset,
13691 size);
13692 Flush();
13693}
13694
13695void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13696 const VkBufferCreateInfo& bufCreateInfo,
13697 const VmaAllocationCreateInfo& allocCreateInfo,
13698 VmaAllocation allocation)
13699{
13700 CallParams callParams;
13701 GetBasicParams(callParams);
13702
13703 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13704 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13705 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,
13706 bufCreateInfo.flags,
13707 bufCreateInfo.size,
13708 bufCreateInfo.usage,
13709 bufCreateInfo.sharingMode,
13710 allocCreateInfo.flags,
13711 allocCreateInfo.usage,
13712 allocCreateInfo.requiredFlags,
13713 allocCreateInfo.preferredFlags,
13714 allocCreateInfo.memoryTypeBits,
13715 allocCreateInfo.pool,
13716 allocation,
13717 userDataStr.GetString());
13718 Flush();
13719}
13720
13721void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13722 const VkImageCreateInfo& imageCreateInfo,
13723 const VmaAllocationCreateInfo& allocCreateInfo,
13724 VmaAllocation allocation)
13725{
13726 CallParams callParams;
13727 GetBasicParams(callParams);
13728
13729 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13730 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13731 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,
13732 imageCreateInfo.flags,
13733 imageCreateInfo.imageType,
13734 imageCreateInfo.format,
13735 imageCreateInfo.extent.width,
13736 imageCreateInfo.extent.height,
13737 imageCreateInfo.extent.depth,
13738 imageCreateInfo.mipLevels,
13739 imageCreateInfo.arrayLayers,
13740 imageCreateInfo.samples,
13741 imageCreateInfo.tiling,
13742 imageCreateInfo.usage,
13743 imageCreateInfo.sharingMode,
13744 imageCreateInfo.initialLayout,
13745 allocCreateInfo.flags,
13746 allocCreateInfo.usage,
13747 allocCreateInfo.requiredFlags,
13748 allocCreateInfo.preferredFlags,
13749 allocCreateInfo.memoryTypeBits,
13750 allocCreateInfo.pool,
13751 allocation,
13752 userDataStr.GetString());
13753 Flush();
13754}
13755
13756void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13757 VmaAllocation allocation)
13758{
13759 CallParams callParams;
13760 GetBasicParams(callParams);
13761
13762 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13763 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13764 allocation);
13765 Flush();
13766}
13767
13768void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13769 VmaAllocation allocation)
13770{
13771 CallParams callParams;
13772 GetBasicParams(callParams);
13773
13774 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13775 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13776 allocation);
13777 Flush();
13778}
13779
13780void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13781 VmaAllocation allocation)
13782{
13783 CallParams callParams;
13784 GetBasicParams(callParams);
13785
13786 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13787 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13788 allocation);
13789 Flush();
13790}
13791
13792void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13793 VmaAllocation allocation)
13794{
13795 CallParams callParams;
13796 GetBasicParams(callParams);
13797
13798 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13799 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13800 allocation);
13801 Flush();
13802}
13803
13804void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13805 VmaPool pool)
13806{
13807 CallParams callParams;
13808 GetBasicParams(callParams);
13809
13810 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13811 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13812 pool);
13813 Flush();
13814}
13815
13816void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13817 const VmaDefragmentationInfo2& info,
13818 VmaDefragmentationContext ctx)
13819{
13820 CallParams callParams;
13821 GetBasicParams(callParams);
13822
13823 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13824 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13825 info.flags);
13826 PrintPointerList(info.allocationCount, info.pAllocations);
13827 fprintf(m_File, ",");
13828 PrintPointerList(info.poolCount, info.pPools);
13829 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13830 info.maxCpuBytesToMove,
13831 info.maxCpuAllocationsToMove,
13832 info.maxGpuBytesToMove,
13833 info.maxGpuAllocationsToMove,
13834 info.commandBuffer,
13835 ctx);
13836 Flush();
13837}
13838
13839void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13840 VmaDefragmentationContext ctx)
13841{
13842 CallParams callParams;
13843 GetBasicParams(callParams);
13844
13845 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13846 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13847 ctx);
13848 Flush();
13849}
13850
13851VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13852{
13853 if(pUserData != VMA_NULL)
13854 {
13855 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13856 {
13857 m_Str = (const char*)pUserData;
13858 }
13859 else
13860 {
13861 sprintf_s(m_PtrStr, "%p", pUserData);
13862 m_Str = m_PtrStr;
13863 }
13864 }
13865 else
13866 {
13867 m_Str = "";
13868 }
13869}
13870
13871void VmaRecorder::WriteConfiguration(
13872 const VkPhysicalDeviceProperties& devProps,
13873 const VkPhysicalDeviceMemoryProperties& memProps,
13874 bool dedicatedAllocationExtensionEnabled)
13875{
13876 fprintf(m_File, "Config,Begin\n");
13877
13878 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13879 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13880 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13881 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13882 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13883 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13884
13885 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13886 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13887 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13888
13889 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13890 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13891 {
13892 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13893 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13894 }
13895 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13896 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13897 {
13898 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13899 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13900 }
13901
13902 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13903
13904 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13905 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13906 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13907 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13908 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13909 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13910 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13911 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13912 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13913
13914 fprintf(m_File, "Config,End\n");
13915}
13916
13917void VmaRecorder::GetBasicParams(CallParams& outParams)
13918{
13919 outParams.threadId = GetCurrentThreadId();
13920
13921 LARGE_INTEGER counter;
13922 QueryPerformanceCounter(&counter);
13923 outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13924}
13925
13926void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13927{
13928 if(count)
13929 {
13930 fprintf(m_File, "%p", pItems[0]);
13931 for(uint64_t i = 1; i < count; ++i)
13932 {
13933 fprintf(m_File, " %p", pItems[i]);
13934 }
13935 }
13936}
13937
13938void VmaRecorder::Flush()
13939{
13940 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13941 {
13942 fflush(m_File);
13943 }
13944}
13945
13946#endif // #if VMA_RECORDING_ENABLED
13947
13948////////////////////////////////////////////////////////////////////////////////
13949// VmaAllocator_T
13950
13951VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13952 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13953 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13954 m_hDevice(pCreateInfo->device),
13955 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13956 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13957 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13958 m_PreferredLargeHeapBlockSize(0),
13959 m_PhysicalDevice(pCreateInfo->physicalDevice),
13960 m_CurrentFrameIndex(0),
13961 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13962 m_NextPoolId(0)
13963#if VMA_RECORDING_ENABLED
13964 ,m_pRecorder(VMA_NULL)
13965#endif
13966{
13967 if(VMA_DEBUG_DETECT_CORRUPTION)
13968 {
13969 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
13970 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
13971 }
13972
13973 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
13974
13975#if !(VMA_DEDICATED_ALLOCATION)
13976 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
13977 {
13978 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
13979 }
13980#endif
13981
13982 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
13983 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
13984 memset(&m_MemProps, 0, sizeof(m_MemProps));
13985
13986 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
13987 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
13988
13989 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
13990 {
13991 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
13992 }
13993
13994 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
13995 {
13996 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
13997 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
13998 }
13999
14000 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14001
14002 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14003 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14004
14005 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14006 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14007 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14008 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14009
14010 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14011 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14012
14013 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14014 {
14015 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14016 {
14017 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14018 if(limit != VK_WHOLE_SIZE)
14019 {
14020 m_HeapSizeLimit[heapIndex] = limit;
14021 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14022 {
14023 m_MemProps.memoryHeaps[heapIndex].size = limit;
14024 }
14025 }
14026 }
14027 }
14028
14029 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14030 {
14031 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14032
14033 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14034 this,
14035 memTypeIndex,
14036 preferredBlockSize,
14037 0,
14038 SIZE_MAX,
14039 GetBufferImageGranularity(),
14040 pCreateInfo->frameInUseCount,
14041 false, // isCustomPool
14042 false, // explicitBlockSize
14043 false); // linearAlgorithm
14044 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14045 // becase minBlockCount is 0.
14046 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14047
14048 }
14049}
14050
14051VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14052{
14053 VkResult res = VK_SUCCESS;
14054
14055 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14056 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14057 {
14058#if VMA_RECORDING_ENABLED
14059 m_pRecorder = vma_new(this, VmaRecorder)();
14060 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14061 if(res != VK_SUCCESS)
14062 {
14063 return res;
14064 }
14065 m_pRecorder->WriteConfiguration(
14066 m_PhysicalDeviceProperties,
14067 m_MemProps,
14068 m_UseKhrDedicatedAllocation);
14069 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14070#else
14071 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14072 return VK_ERROR_FEATURE_NOT_PRESENT;
14073#endif
14074 }
14075
14076 return res;
14077}
14078
14079VmaAllocator_T::~VmaAllocator_T()
14080{
14081#if VMA_RECORDING_ENABLED
14082 if(m_pRecorder != VMA_NULL)
14083 {
14084 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14085 vma_delete(this, m_pRecorder);
14086 }
14087#endif
14088
14089 VMA_ASSERT(m_Pools.empty());
14090
14091 for(size_t i = GetMemoryTypeCount(); i--; )
14092 {
14093 vma_delete(this, m_pDedicatedAllocations[i]);
14094 vma_delete(this, m_pBlockVectors[i]);
14095 }
14096}
14097
14098void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14099{
14100#if VMA_STATIC_VULKAN_FUNCTIONS == 1
14101 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14102 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14103 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14104 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14105 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14106 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14107 m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14108 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14109 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14110 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14111 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14112 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14113 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14114 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14115 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14116 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14117 m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14118#if VMA_DEDICATED_ALLOCATION
14119 if(m_UseKhrDedicatedAllocation)
14120 {
14121 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14122 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14123 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14124 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14125 }
14126#endif // #if VMA_DEDICATED_ALLOCATION
14127#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14128
14129#define VMA_COPY_IF_NOT_NULL(funcName) \
14130 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14131
14132 if(pVulkanFunctions != VMA_NULL)
14133 {
14134 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14135 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14136 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14137 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14138 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14139 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14140 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14141 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14142 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14143 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14144 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14145 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14146 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14147 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14148 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14149 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14150 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14151#if VMA_DEDICATED_ALLOCATION
14152 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14153 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14154#endif
14155 }
14156
14157#undef VMA_COPY_IF_NOT_NULL
14158
14159 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14160 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14161 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14162 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14163 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14164 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14165 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14166 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14167 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14168 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14169 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14170 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14171 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14172 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14173 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14174 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14175 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14176 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14177 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14178#if VMA_DEDICATED_ALLOCATION
14179 if(m_UseKhrDedicatedAllocation)
14180 {
14181 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14182 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14183 }
14184#endif
14185}
14186
14187VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14188{
14189 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14190 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14191 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14192 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14193}
14194
14195VkResult VmaAllocator_T::AllocateMemoryOfType(
14196 VkDeviceSize size,
14197 VkDeviceSize alignment,
14198 bool dedicatedAllocation,
14199 VkBuffer dedicatedBuffer,
14200 VkImage dedicatedImage,
14201 const VmaAllocationCreateInfo& createInfo,
14202 uint32_t memTypeIndex,
14203 VmaSuballocationType suballocType,
14204 size_t allocationCount,
14205 VmaAllocation* pAllocations)
14206{
14207 VMA_ASSERT(pAllocations != VMA_NULL);
14208 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14209
14210 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14211
14212 // If memory type is not HOST_VISIBLE, disable MAPPED.
14213 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14214 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14215 {
14216 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14217 }
14218
14219 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14220 VMA_ASSERT(blockVector);
14221
14222 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14223 bool preferDedicatedMemory =
14224 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14225 dedicatedAllocation ||
14226 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14227 size > preferredBlockSize / 2;
14228
14229 if(preferDedicatedMemory &&
14230 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14231 finalCreateInfo.pool == VK_NULL_HANDLE)
14232 {
14233 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14234 }
14235
14236 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14237 {
14238 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14239 {
14240 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14241 }
14242 else
14243 {
14244 return AllocateDedicatedMemory(
14245 size,
14246 suballocType,
14247 memTypeIndex,
14248 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14249 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14250 finalCreateInfo.pUserData,
14251 dedicatedBuffer,
14252 dedicatedImage,
14253 allocationCount,
14254 pAllocations);
14255 }
14256 }
14257 else
14258 {
14259 VkResult res = blockVector->Allocate(
14260 VK_NULL_HANDLE, // hCurrentPool
14261 m_CurrentFrameIndex.load(),
14262 size,
14263 alignment,
14264 finalCreateInfo,
14265 suballocType,
14266 allocationCount,
14267 pAllocations);
14268 if(res == VK_SUCCESS)
14269 {
14270 return res;
14271 }
14272
14273 // 5. Try dedicated memory.
14274 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14275 {
14276 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14277 }
14278 else
14279 {
14280 res = AllocateDedicatedMemory(
14281 size,
14282 suballocType,
14283 memTypeIndex,
14284 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14285 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14286 finalCreateInfo.pUserData,
14287 dedicatedBuffer,
14288 dedicatedImage,
14289 allocationCount,
14290 pAllocations);
14291 if(res == VK_SUCCESS)
14292 {
14293 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14294 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14295 return VK_SUCCESS;
14296 }
14297 else
14298 {
14299 // Everything failed: Return error code.
14300 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14301 return res;
14302 }
14303 }
14304 }
14305}
14306
14307VkResult VmaAllocator_T::AllocateDedicatedMemory(
14308 VkDeviceSize size,
14309 VmaSuballocationType suballocType,
14310 uint32_t memTypeIndex,
14311 bool map,
14312 bool isUserDataString,
14313 void* pUserData,
14314 VkBuffer dedicatedBuffer,
14315 VkImage dedicatedImage,
14316 size_t allocationCount,
14317 VmaAllocation* pAllocations)
14318{
14319 VMA_ASSERT(allocationCount > 0 && pAllocations);
14320
14321 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14322 allocInfo.memoryTypeIndex = memTypeIndex;
14323 allocInfo.allocationSize = size;
14324
14325#if VMA_DEDICATED_ALLOCATION
14326 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14327 if(m_UseKhrDedicatedAllocation)
14328 {
14329 if(dedicatedBuffer != VK_NULL_HANDLE)
14330 {
14331 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14332 dedicatedAllocInfo.buffer = dedicatedBuffer;
14333 allocInfo.pNext = &dedicatedAllocInfo;
14334 }
14335 else if(dedicatedImage != VK_NULL_HANDLE)
14336 {
14337 dedicatedAllocInfo.image = dedicatedImage;
14338 allocInfo.pNext = &dedicatedAllocInfo;
14339 }
14340 }
14341#endif // #if VMA_DEDICATED_ALLOCATION
14342
14343 size_t allocIndex;
14344 VkResult res;
14345 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14346 {
14347 res = AllocateDedicatedMemoryPage(
14348 size,
14349 suballocType,
14350 memTypeIndex,
14351 allocInfo,
14352 map,
14353 isUserDataString,
14354 pUserData,
14355 pAllocations + allocIndex);
14356 if(res != VK_SUCCESS)
14357 {
14358 break;
14359 }
14360 }
14361
14362 if(res == VK_SUCCESS)
14363 {
14364 // Register them in m_pDedicatedAllocations.
14365 {
14366 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14367 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14368 VMA_ASSERT(pDedicatedAllocations);
14369 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14370 {
14371 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14372 }
14373 }
14374
14375 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14376 }
14377 else
14378 {
14379 // Free all already created allocations.
14380 while(allocIndex--)
14381 {
14382 VmaAllocation currAlloc = pAllocations[allocIndex];
14383 VkDeviceMemory hMemory = currAlloc->GetMemory();
14384
14385 /*
14386 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14387 before vkFreeMemory.
14388
14389 if(currAlloc->GetMappedData() != VMA_NULL)
14390 {
14391 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14392 }
14393 */
14394
14395 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14396
14397 currAlloc->SetUserData(this, VMA_NULL);
14398 vma_delete(this, currAlloc);
14399 }
14400
14401 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14402 }
14403
14404 return res;
14405}
14406
14407VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14408 VkDeviceSize size,
14409 VmaSuballocationType suballocType,
14410 uint32_t memTypeIndex,
14411 const VkMemoryAllocateInfo& allocInfo,
14412 bool map,
14413 bool isUserDataString,
14414 void* pUserData,
14415 VmaAllocation* pAllocation)
14416{
14417 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14418 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14419 if(res < 0)
14420 {
14421 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14422 return res;
14423 }
14424
14425 void* pMappedData = VMA_NULL;
14426 if(map)
14427 {
14428 res = (*m_VulkanFunctions.vkMapMemory)(
14429 m_hDevice,
14430 hMemory,
14431 0,
14432 VK_WHOLE_SIZE,
14433 0,
14434 &pMappedData);
14435 if(res < 0)
14436 {
14437 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14438 FreeVulkanMemory(memTypeIndex, size, hMemory);
14439 return res;
14440 }
14441 }
14442
14443 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14444 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14445 (*pAllocation)->SetUserData(this, pUserData);
14446 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14447 {
14448 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14449 }
14450
14451 return VK_SUCCESS;
14452}
14453
14454void VmaAllocator_T::GetBufferMemoryRequirements(
14455 VkBuffer hBuffer,
14456 VkMemoryRequirements& memReq,
14457 bool& requiresDedicatedAllocation,
14458 bool& prefersDedicatedAllocation) const
14459{
14460#if VMA_DEDICATED_ALLOCATION
14461 if(m_UseKhrDedicatedAllocation)
14462 {
14463 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14464 memReqInfo.buffer = hBuffer;
14465
14466 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14467
14468 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14469 memReq2.pNext = &memDedicatedReq;
14470
14471 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14472
14473 memReq = memReq2.memoryRequirements;
14474 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14475 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14476 }
14477 else
14478#endif // #if VMA_DEDICATED_ALLOCATION
14479 {
14480 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14481 requiresDedicatedAllocation = false;
14482 prefersDedicatedAllocation = false;
14483 }
14484}
14485
14486void VmaAllocator_T::GetImageMemoryRequirements(
14487 VkImage hImage,
14488 VkMemoryRequirements& memReq,
14489 bool& requiresDedicatedAllocation,
14490 bool& prefersDedicatedAllocation) const
14491{
14492#if VMA_DEDICATED_ALLOCATION
14493 if(m_UseKhrDedicatedAllocation)
14494 {
14495 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14496 memReqInfo.image = hImage;
14497
14498 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14499
14500 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14501 memReq2.pNext = &memDedicatedReq;
14502
14503 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14504
14505 memReq = memReq2.memoryRequirements;
14506 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14507 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14508 }
14509 else
14510#endif // #if VMA_DEDICATED_ALLOCATION
14511 {
14512 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14513 requiresDedicatedAllocation = false;
14514 prefersDedicatedAllocation = false;
14515 }
14516}
14517
14518VkResult VmaAllocator_T::AllocateMemory(
14519 const VkMemoryRequirements& vkMemReq,
14520 bool requiresDedicatedAllocation,
14521 bool prefersDedicatedAllocation,
14522 VkBuffer dedicatedBuffer,
14523 VkImage dedicatedImage,
14524 const VmaAllocationCreateInfo& createInfo,
14525 VmaSuballocationType suballocType,
14526 size_t allocationCount,
14527 VmaAllocation* pAllocations)
14528{
14529 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14530
14531 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14532
14533 if(vkMemReq.size == 0)
14534 {
14535 return VK_ERROR_VALIDATION_FAILED_EXT;
14536 }
14537 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14538 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14539 {
14540 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14541 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14542 }
14543 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14544 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14545 {
14546 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14547 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14548 }
14549 if(requiresDedicatedAllocation)
14550 {
14551 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14552 {
14553 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14554 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14555 }
14556 if(createInfo.pool != VK_NULL_HANDLE)
14557 {
14558 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14559 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14560 }
14561 }
14562 if((createInfo.pool != VK_NULL_HANDLE) &&
14563 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14564 {
14565 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14566 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14567 }
14568
14569 if(createInfo.pool != VK_NULL_HANDLE)
14570 {
14571 const VkDeviceSize alignmentForPool = VMA_MAX(
14572 vkMemReq.alignment,
14573 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14574 return createInfo.pool->m_BlockVector.Allocate(
14575 createInfo.pool,
14576 m_CurrentFrameIndex.load(),
14577 vkMemReq.size,
14578 alignmentForPool,
14579 createInfo,
14580 suballocType,
14581 allocationCount,
14582 pAllocations);
14583 }
14584 else
14585 {
14586 // Bit mask of memory Vulkan types acceptable for this allocation.
14587 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14588 uint32_t memTypeIndex = UINT32_MAX;
14589 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14590 if(res == VK_SUCCESS)
14591 {
14592 VkDeviceSize alignmentForMemType = VMA_MAX(
14593 vkMemReq.alignment,
14594 GetMemoryTypeMinAlignment(memTypeIndex));
14595
14596 res = AllocateMemoryOfType(
14597 vkMemReq.size,
14598 alignmentForMemType,
14599 requiresDedicatedAllocation || prefersDedicatedAllocation,
14600 dedicatedBuffer,
14601 dedicatedImage,
14602 createInfo,
14603 memTypeIndex,
14604 suballocType,
14605 allocationCount,
14606 pAllocations);
14607 // Succeeded on first try.
14608 if(res == VK_SUCCESS)
14609 {
14610 return res;
14611 }
14612 // Allocation from this memory type failed. Try other compatible memory types.
14613 else
14614 {
14615 for(;;)
14616 {
14617 // Remove old memTypeIndex from list of possibilities.
14618 memoryTypeBits &= ~(1u << memTypeIndex);
14619 // Find alternative memTypeIndex.
14620 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14621 if(res == VK_SUCCESS)
14622 {
14623 alignmentForMemType = VMA_MAX(
14624 vkMemReq.alignment,
14625 GetMemoryTypeMinAlignment(memTypeIndex));
14626
14627 res = AllocateMemoryOfType(
14628 vkMemReq.size,
14629 alignmentForMemType,
14630 requiresDedicatedAllocation || prefersDedicatedAllocation,
14631 dedicatedBuffer,
14632 dedicatedImage,
14633 createInfo,
14634 memTypeIndex,
14635 suballocType,
14636 allocationCount,
14637 pAllocations);
14638 // Allocation from this alternative memory type succeeded.
14639 if(res == VK_SUCCESS)
14640 {
14641 return res;
14642 }
14643 // else: Allocation from this memory type failed. Try next one - next loop iteration.
14644 }
14645 // No other matching memory type index could be found.
14646 else
14647 {
14648 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14649 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14650 }
14651 }
14652 }
14653 }
14654 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14655 else
14656 return res;
14657 }
14658}
14659
14660void VmaAllocator_T::FreeMemory(
14661 size_t allocationCount,
14662 const VmaAllocation* pAllocations)
14663{
14664 VMA_ASSERT(pAllocations);
14665
14666 for(size_t allocIndex = allocationCount; allocIndex--; )
14667 {
14668 VmaAllocation allocation = pAllocations[allocIndex];
14669
14670 if(allocation != VK_NULL_HANDLE)
14671 {
14672 if(TouchAllocation(allocation))
14673 {
14674 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14675 {
14676 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14677 }
14678
14679 switch(allocation->GetType())
14680 {
14681 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14682 {
14683 VmaBlockVector* pBlockVector = VMA_NULL;
14684 VmaPool hPool = allocation->GetPool();
14685 if(hPool != VK_NULL_HANDLE)
14686 {
14687 pBlockVector = &hPool->m_BlockVector;
14688 }
14689 else
14690 {
14691 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14692 pBlockVector = m_pBlockVectors[memTypeIndex];
14693 }
14694 pBlockVector->Free(allocation);
14695 }
14696 break;
14697 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14698 FreeDedicatedMemory(allocation);
14699 break;
14700 default:
14701 VMA_ASSERT(0);
14702 }
14703 }
14704
14705 allocation->SetUserData(this, VMA_NULL);
14706 vma_delete(this, allocation);
14707 }
14708 }
14709}
14710
14711VkResult VmaAllocator_T::ResizeAllocation(
14712 const VmaAllocation alloc,
14713 VkDeviceSize newSize)
14714{
14715 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14716 {
14717 return VK_ERROR_VALIDATION_FAILED_EXT;
14718 }
14719 if(newSize == alloc->GetSize())
14720 {
14721 return VK_SUCCESS;
14722 }
14723
14724 switch(alloc->GetType())
14725 {
14726 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14727 return VK_ERROR_FEATURE_NOT_PRESENT;
14728 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14729 if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14730 {
14731 alloc->ChangeSize(newSize);
14732 VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14733 return VK_SUCCESS;
14734 }
14735 else
14736 {
14737 return VK_ERROR_OUT_OF_POOL_MEMORY;
14738 }
14739 default:
14740 VMA_ASSERT(0);
14741 return VK_ERROR_VALIDATION_FAILED_EXT;
14742 }
14743}
14744
14745void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14746{
14747 // Initialize.
14748 InitStatInfo(pStats->total);
14749 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14750 InitStatInfo(pStats->memoryType[i]);
14751 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14752 InitStatInfo(pStats->memoryHeap[i]);
14753
14754 // Process default pools.
14755 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14756 {
14757 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14758 VMA_ASSERT(pBlockVector);
14759 pBlockVector->AddStats(pStats);
14760 }
14761
14762 // Process custom pools.
14763 {
14764 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14765 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14766 {
14767 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14768 }
14769 }
14770
14771 // Process dedicated allocations.
14772 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14773 {
14774 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14775 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14776 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14777 VMA_ASSERT(pDedicatedAllocVector);
14778 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14779 {
14780 VmaStatInfo allocationStatInfo;
14781 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14782 VmaAddStatInfo(pStats->total, allocationStatInfo);
14783 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14784 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14785 }
14786 }
14787
14788 // Postprocess.
14789 VmaPostprocessCalcStatInfo(pStats->total);
14790 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14791 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14792 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14793 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14794}
14795
14796static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14797
14798VkResult VmaAllocator_T::DefragmentationBegin(
14799 const VmaDefragmentationInfo2& info,
14800 VmaDefragmentationStats* pStats,
14801 VmaDefragmentationContext* pContext)
14802{
14803 if(info.pAllocationsChanged != VMA_NULL)
14804 {
14805 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14806 }
14807
14808 *pContext = vma_new(this, VmaDefragmentationContext_T)(
14809 this, m_CurrentFrameIndex.load(), info.flags, pStats);
14810
14811 (*pContext)->AddPools(info.poolCount, info.pPools);
14812 (*pContext)->AddAllocations(
14813 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14814
14815 VkResult res = (*pContext)->Defragment(
14816 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14817 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14818 info.commandBuffer, pStats);
14819
14820 if(res != VK_NOT_READY)
14821 {
14822 vma_delete(this, *pContext);
14823 *pContext = VMA_NULL;
14824 }
14825
14826 return res;
14827}
14828
14829VkResult VmaAllocator_T::DefragmentationEnd(
14830 VmaDefragmentationContext context)
14831{
14832 vma_delete(this, context);
14833 return VK_SUCCESS;
14834}
14835
14836void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14837{
14838 if(hAllocation->CanBecomeLost())
14839 {
14840 /*
14841 Warning: This is a carefully designed algorithm.
14842 Do not modify unless you really know what you're doing :)
14843 */
14844 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14845 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14846 for(;;)
14847 {
14848 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14849 {
14850 pAllocationInfo->memoryType = UINT32_MAX;
14851 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14852 pAllocationInfo->offset = 0;
14853 pAllocationInfo->size = hAllocation->GetSize();
14854 pAllocationInfo->pMappedData = VMA_NULL;
14855 pAllocationInfo->pUserData = hAllocation->GetUserData();
14856 return;
14857 }
14858 else if(localLastUseFrameIndex == localCurrFrameIndex)
14859 {
14860 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14861 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14862 pAllocationInfo->offset = hAllocation->GetOffset();
14863 pAllocationInfo->size = hAllocation->GetSize();
14864 pAllocationInfo->pMappedData = VMA_NULL;
14865 pAllocationInfo->pUserData = hAllocation->GetUserData();
14866 return;
14867 }
14868 else // Last use time earlier than current time.
14869 {
14870 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14871 {
14872 localLastUseFrameIndex = localCurrFrameIndex;
14873 }
14874 }
14875 }
14876 }
14877 else
14878 {
14879#if VMA_STATS_STRING_ENABLED
14880 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14881 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14882 for(;;)
14883 {
14884 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14885 if(localLastUseFrameIndex == localCurrFrameIndex)
14886 {
14887 break;
14888 }
14889 else // Last use time earlier than current time.
14890 {
14891 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14892 {
14893 localLastUseFrameIndex = localCurrFrameIndex;
14894 }
14895 }
14896 }
14897#endif
14898
14899 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14900 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14901 pAllocationInfo->offset = hAllocation->GetOffset();
14902 pAllocationInfo->size = hAllocation->GetSize();
14903 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14904 pAllocationInfo->pUserData = hAllocation->GetUserData();
14905 }
14906}
14907
14908bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14909{
14910 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14911 if(hAllocation->CanBecomeLost())
14912 {
14913 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14914 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14915 for(;;)
14916 {
14917 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14918 {
14919 return false;
14920 }
14921 else if(localLastUseFrameIndex == localCurrFrameIndex)
14922 {
14923 return true;
14924 }
14925 else // Last use time earlier than current time.
14926 {
14927 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14928 {
14929 localLastUseFrameIndex = localCurrFrameIndex;
14930 }
14931 }
14932 }
14933 }
14934 else
14935 {
14936#if VMA_STATS_STRING_ENABLED
14937 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14938 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14939 for(;;)
14940 {
14941 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14942 if(localLastUseFrameIndex == localCurrFrameIndex)
14943 {
14944 break;
14945 }
14946 else // Last use time earlier than current time.
14947 {
14948 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14949 {
14950 localLastUseFrameIndex = localCurrFrameIndex;
14951 }
14952 }
14953 }
14954#endif
14955
14956 return true;
14957 }
14958}
14959
14960VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
14961{
14962 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
14963
14964 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
14965
14966 if(newCreateInfo.maxBlockCount == 0)
14967 {
14968 newCreateInfo.maxBlockCount = SIZE_MAX;
14969 }
14970 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
14971 {
14972 return VK_ERROR_INITIALIZATION_FAILED;
14973 }
14974
14975 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
14976
14977 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
14978
14979 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
14980 if(res != VK_SUCCESS)
14981 {
14982 vma_delete(this, *pPool);
14983 *pPool = VMA_NULL;
14984 return res;
14985 }
14986
14987 // Add to m_Pools.
14988 {
14989 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
14990 (*pPool)->SetId(m_NextPoolId++);
14991 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
14992 }
14993
14994 return VK_SUCCESS;
14995}
14996
14997void VmaAllocator_T::DestroyPool(VmaPool pool)
14998{
14999 // Remove from m_Pools.
15000 {
15001 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15002 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15003 VMA_ASSERT(success && "Pool not found in Allocator.");
15004 }
15005
15006 vma_delete(this, pool);
15007}
15008
15009void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15010{
15011 pool->m_BlockVector.GetPoolStats(pPoolStats);
15012}
15013
15014void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15015{
15016 m_CurrentFrameIndex.store(frameIndex);
15017}
15018
15019void VmaAllocator_T::MakePoolAllocationsLost(
15020 VmaPool hPool,
15021 size_t* pLostAllocationCount)
15022{
15023 hPool->m_BlockVector.MakePoolAllocationsLost(
15024 m_CurrentFrameIndex.load(),
15025 pLostAllocationCount);
15026}
15027
15028VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15029{
15030 return hPool->m_BlockVector.CheckCorruption();
15031}
15032
15033VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15034{
15035 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15036
15037 // Process default pools.
15038 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15039 {
15040 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15041 {
15042 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15043 VMA_ASSERT(pBlockVector);
15044 VkResult localRes = pBlockVector->CheckCorruption();
15045 switch(localRes)
15046 {
15047 case VK_ERROR_FEATURE_NOT_PRESENT:
15048 break;
15049 case VK_SUCCESS:
15050 finalRes = VK_SUCCESS;
15051 break;
15052 default:
15053 return localRes;
15054 }
15055 }
15056 }
15057
15058 // Process custom pools.
15059 {
15060 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15061 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15062 {
15063 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15064 {
15065 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15066 switch(localRes)
15067 {
15068 case VK_ERROR_FEATURE_NOT_PRESENT:
15069 break;
15070 case VK_SUCCESS:
15071 finalRes = VK_SUCCESS;
15072 break;
15073 default:
15074 return localRes;
15075 }
15076 }
15077 }
15078 }
15079
15080 return finalRes;
15081}
15082
15083void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15084{
15085 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15086 (*pAllocation)->InitLost();
15087}
15088
15089VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15090{
15091 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15092
15093 VkResult res;
15094 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15095 {
15096 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15097 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15098 {
15099 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15100 if(res == VK_SUCCESS)
15101 {
15102 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15103 }
15104 }
15105 else
15106 {
15107 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15108 }
15109 }
15110 else
15111 {
15112 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15113 }
15114
15115 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15116 {
15117 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15118 }
15119
15120 return res;
15121}
15122
15123void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15124{
15125 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15126 {
15127 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15128 }
15129
15130 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15131
15132 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15133 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15134 {
15135 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15136 m_HeapSizeLimit[heapIndex] += size;
15137 }
15138}
15139
15140VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15141{
15142 if(hAllocation->CanBecomeLost())
15143 {
15144 return VK_ERROR_MEMORY_MAP_FAILED;
15145 }
15146
15147 switch(hAllocation->GetType())
15148 {
15149 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15150 {
15151 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15152 char *pBytes = VMA_NULL;
15153 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15154 if(res == VK_SUCCESS)
15155 {
15156 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15157 hAllocation->BlockAllocMap();
15158 }
15159 return res;
15160 }
15161 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15162 return hAllocation->DedicatedAllocMap(this, ppData);
15163 default:
15164 VMA_ASSERT(0);
15165 return VK_ERROR_MEMORY_MAP_FAILED;
15166 }
15167}
15168
15169void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15170{
15171 switch(hAllocation->GetType())
15172 {
15173 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15174 {
15175 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15176 hAllocation->BlockAllocUnmap();
15177 pBlock->Unmap(this, 1);
15178 }
15179 break;
15180 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15181 hAllocation->DedicatedAllocUnmap(this);
15182 break;
15183 default:
15184 VMA_ASSERT(0);
15185 }
15186}
15187
15188VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15189{
15190 VkResult res = VK_SUCCESS;
15191 switch(hAllocation->GetType())
15192 {
15193 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15194 res = GetVulkanFunctions().vkBindBufferMemory(
15195 m_hDevice,
15196 hBuffer,
15197 hAllocation->GetMemory(),
15198 0); //memoryOffset
15199 break;
15200 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15201 {
15202 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15203 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15204 res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15205 break;
15206 }
15207 default:
15208 VMA_ASSERT(0);
15209 }
15210 return res;
15211}
15212
15213VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15214{
15215 VkResult res = VK_SUCCESS;
15216 switch(hAllocation->GetType())
15217 {
15218 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15219 res = GetVulkanFunctions().vkBindImageMemory(
15220 m_hDevice,
15221 hImage,
15222 hAllocation->GetMemory(),
15223 0); //memoryOffset
15224 break;
15225 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15226 {
15227 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15228 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15229 res = pBlock->BindImageMemory(this, hAllocation, hImage);
15230 break;
15231 }
15232 default:
15233 VMA_ASSERT(0);
15234 }
15235 return res;
15236}
15237
15238void VmaAllocator_T::FlushOrInvalidateAllocation(
15239 VmaAllocation hAllocation,
15240 VkDeviceSize offset, VkDeviceSize size,
15241 VMA_CACHE_OPERATION op)
15242{
15243 const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15244 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15245 {
15246 const VkDeviceSize allocationSize = hAllocation->GetSize();
15247 VMA_ASSERT(offset <= allocationSize);
15248
15249 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15250
15251 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15252 memRange.memory = hAllocation->GetMemory();
15253
15254 switch(hAllocation->GetType())
15255 {
15256 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15257 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15258 if(size == VK_WHOLE_SIZE)
15259 {
15260 memRange.size = allocationSize - memRange.offset;
15261 }
15262 else
15263 {
15264 VMA_ASSERT(offset + size <= allocationSize);
15265 memRange.size = VMA_MIN(
15266 VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15267 allocationSize - memRange.offset);
15268 }
15269 break;
15270
15271 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15272 {
15273 // 1. Still within this allocation.
15274 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15275 if(size == VK_WHOLE_SIZE)
15276 {
15277 size = allocationSize - offset;
15278 }
15279 else
15280 {
15281 VMA_ASSERT(offset + size <= allocationSize);
15282 }
15283 memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15284
15285 // 2. Adjust to whole block.
15286 const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15287 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15288 const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15289 memRange.offset += allocationOffset;
15290 memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15291
15292 break;
15293 }
15294
15295 default:
15296 VMA_ASSERT(0);
15297 }
15298
15299 switch(op)
15300 {
15301 case VMA_CACHE_FLUSH:
15302 (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15303 break;
15304 case VMA_CACHE_INVALIDATE:
15305 (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15306 break;
15307 default:
15308 VMA_ASSERT(0);
15309 }
15310 }
15311 // else: Just ignore this call.
15312}
15313
15314void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15315{
15316 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15317
15318 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15319 {
15320 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15321 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15322 VMA_ASSERT(pDedicatedAllocations);
15323 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15324 VMA_ASSERT(success);
15325 }
15326
15327 VkDeviceMemory hMemory = allocation->GetMemory();
15328
15329 /*
15330 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15331 before vkFreeMemory.
15332
15333 if(allocation->GetMappedData() != VMA_NULL)
15334 {
15335 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15336 }
15337 */
15338
15339 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15340
15341 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15342}
15343
15344void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15345{
15346 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15347 !hAllocation->CanBecomeLost() &&
15348 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15349 {
15350 void* pData = VMA_NULL;
15351 VkResult res = Map(hAllocation, &pData);
15352 if(res == VK_SUCCESS)
15353 {
15354 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15355 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15356 Unmap(hAllocation);
15357 }
15358 else
15359 {
15360 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15361 }
15362 }
15363}
15364
15365#if VMA_STATS_STRING_ENABLED
15366
15367void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15368{
15369 bool dedicatedAllocationsStarted = false;
15370 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15371 {
15372 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15373 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15374 VMA_ASSERT(pDedicatedAllocVector);
15375 if(pDedicatedAllocVector->empty() == false)
15376 {
15377 if(dedicatedAllocationsStarted == false)
15378 {
15379 dedicatedAllocationsStarted = true;
15380 json.WriteString("DedicatedAllocations");
15381 json.BeginObject();
15382 }
15383
15384 json.BeginString("Type ");
15385 json.ContinueString(memTypeIndex);
15386 json.EndString();
15387
15388 json.BeginArray();
15389
15390 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15391 {
15392 json.BeginObject(true);
15393 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15394 hAlloc->PrintParameters(json);
15395 json.EndObject();
15396 }
15397
15398 json.EndArray();
15399 }
15400 }
15401 if(dedicatedAllocationsStarted)
15402 {
15403 json.EndObject();
15404 }
15405
15406 {
15407 bool allocationsStarted = false;
15408 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15409 {
15410 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15411 {
15412 if(allocationsStarted == false)
15413 {
15414 allocationsStarted = true;
15415 json.WriteString("DefaultPools");
15416 json.BeginObject();
15417 }
15418
15419 json.BeginString("Type ");
15420 json.ContinueString(memTypeIndex);
15421 json.EndString();
15422
15423 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15424 }
15425 }
15426 if(allocationsStarted)
15427 {
15428 json.EndObject();
15429 }
15430 }
15431
15432 // Custom pools
15433 {
15434 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15435 const size_t poolCount = m_Pools.size();
15436 if(poolCount > 0)
15437 {
15438 json.WriteString("Pools");
15439 json.BeginObject();
15440 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15441 {
15442 json.BeginString();
15443 json.ContinueString(m_Pools[poolIndex]->GetId());
15444 json.EndString();
15445
15446 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15447 }
15448 json.EndObject();
15449 }
15450 }
15451}
15452
15453#endif // #if VMA_STATS_STRING_ENABLED
15454
15455////////////////////////////////////////////////////////////////////////////////
15456// Public interface
15457
15458VkResult vmaCreateAllocator(
15459 const VmaAllocatorCreateInfo* pCreateInfo,
15460 VmaAllocator* pAllocator)
15461{
15462 VMA_ASSERT(pCreateInfo && pAllocator);
15463 VMA_DEBUG_LOG("vmaCreateAllocator");
15464 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15465 return (*pAllocator)->Init(pCreateInfo);
15466}
15467
15468void vmaDestroyAllocator(
15469 VmaAllocator allocator)
15470{
15471 if(allocator != VK_NULL_HANDLE)
15472 {
15473 VMA_DEBUG_LOG("vmaDestroyAllocator");
15474 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15475 vma_delete(&allocationCallbacks, allocator);
15476 }
15477}
15478
15479void vmaGetPhysicalDeviceProperties(
15480 VmaAllocator allocator,
15481 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15482{
15483 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15484 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15485}
15486
15487void vmaGetMemoryProperties(
15488 VmaAllocator allocator,
15489 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15490{
15491 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15492 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15493}
15494
15495void vmaGetMemoryTypeProperties(
15496 VmaAllocator allocator,
15497 uint32_t memoryTypeIndex,
15498 VkMemoryPropertyFlags* pFlags)
15499{
15500 VMA_ASSERT(allocator && pFlags);
15501 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15502 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15503}
15504
15505void vmaSetCurrentFrameIndex(
15506 VmaAllocator allocator,
15507 uint32_t frameIndex)
15508{
15509 VMA_ASSERT(allocator);
15510 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15511
15512 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15513
15514 allocator->SetCurrentFrameIndex(frameIndex);
15515}
15516
15517void vmaCalculateStats(
15518 VmaAllocator allocator,
15519 VmaStats* pStats)
15520{
15521 VMA_ASSERT(allocator && pStats);
15522 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15523 allocator->CalculateStats(pStats);
15524}
15525
15526#if VMA_STATS_STRING_ENABLED
15527
15528void vmaBuildStatsString(
15529 VmaAllocator allocator,
15530 char** ppStatsString,
15531 VkBool32 detailedMap)
15532{
15533 VMA_ASSERT(allocator && ppStatsString);
15534 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15535
15536 VmaStringBuilder sb(allocator);
15537 {
15538 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15539 json.BeginObject();
15540
15541 VmaStats stats;
15542 allocator->CalculateStats(&stats);
15543
15544 json.WriteString("Total");
15545 VmaPrintStatInfo(json, stats.total);
15546
15547 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15548 {
15549 json.BeginString("Heap ");
15550 json.ContinueString(heapIndex);
15551 json.EndString();
15552 json.BeginObject();
15553
15554 json.WriteString("Size");
15555 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15556
15557 json.WriteString("Flags");
15558 json.BeginArray(true);
15559 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15560 {
15561 json.WriteString("DEVICE_LOCAL");
15562 }
15563 json.EndArray();
15564
15565 if(stats.memoryHeap[heapIndex].blockCount > 0)
15566 {
15567 json.WriteString("Stats");
15568 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15569 }
15570
15571 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15572 {
15573 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15574 {
15575 json.BeginString("Type ");
15576 json.ContinueString(typeIndex);
15577 json.EndString();
15578
15579 json.BeginObject();
15580
15581 json.WriteString("Flags");
15582 json.BeginArray(true);
15583 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15584 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15585 {
15586 json.WriteString("DEVICE_LOCAL");
15587 }
15588 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15589 {
15590 json.WriteString("HOST_VISIBLE");
15591 }
15592 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15593 {
15594 json.WriteString("HOST_COHERENT");
15595 }
15596 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15597 {
15598 json.WriteString("HOST_CACHED");
15599 }
15600 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15601 {
15602 json.WriteString("LAZILY_ALLOCATED");
15603 }
15604 json.EndArray();
15605
15606 if(stats.memoryType[typeIndex].blockCount > 0)
15607 {
15608 json.WriteString("Stats");
15609 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15610 }
15611
15612 json.EndObject();
15613 }
15614 }
15615
15616 json.EndObject();
15617 }
15618 if(detailedMap == VK_TRUE)
15619 {
15620 allocator->PrintDetailedMap(json);
15621 }
15622
15623 json.EndObject();
15624 }
15625
15626 const size_t len = sb.GetLength();
15627 char* const pChars = vma_new_array(allocator, char, len + 1);
15628 if(len > 0)
15629 {
15630 memcpy(pChars, sb.GetData(), len);
15631 }
15632 pChars[len] = '\0';
15633 *ppStatsString = pChars;
15634}
15635
15636void vmaFreeStatsString(
15637 VmaAllocator allocator,
15638 char* pStatsString)
15639{
15640 if(pStatsString != VMA_NULL)
15641 {
15642 VMA_ASSERT(allocator);
15643 size_t len = strlen(pStatsString);
15644 vma_delete_array(allocator, pStatsString, len + 1);
15645 }
15646}
15647
15648#endif // #if VMA_STATS_STRING_ENABLED
15649
15650/*
15651This function is not protected by any mutex because it just reads immutable data.
15652*/
15653VkResult vmaFindMemoryTypeIndex(
15654 VmaAllocator allocator,
15655 uint32_t memoryTypeBits,
15656 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15657 uint32_t* pMemoryTypeIndex)
15658{
15659 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15660 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15661 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15662
15663 if(pAllocationCreateInfo->memoryTypeBits != 0)
15664 {
15665 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15666 }
15667
15668 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15669 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15670
15671 const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15672 if(mapped)
15673 {
15674 preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15675 }
15676
15677 // Convert usage to requiredFlags and preferredFlags.
15678 switch(pAllocationCreateInfo->usage)
15679 {
15680 case VMA_MEMORY_USAGE_UNKNOWN:
15681 break;
15682 case VMA_MEMORY_USAGE_GPU_ONLY:
15683 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15684 {
15685 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15686 }
15687 break;
15688 case VMA_MEMORY_USAGE_CPU_ONLY:
15689 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15690 break;
15691 case VMA_MEMORY_USAGE_CPU_TO_GPU:
15692 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15693 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15694 {
15695 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15696 }
15697 break;
15698 case VMA_MEMORY_USAGE_GPU_TO_CPU:
15699 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15700 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15701 break;
15702 default:
15703 break;
15704 }
15705
15706 *pMemoryTypeIndex = UINT32_MAX;
15707 uint32_t minCost = UINT32_MAX;
15708 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15709 memTypeIndex < allocator->GetMemoryTypeCount();
15710 ++memTypeIndex, memTypeBit <<= 1)
15711 {
15712 // This memory type is acceptable according to memoryTypeBits bitmask.
15713 if((memTypeBit & memoryTypeBits) != 0)
15714 {
15715 const VkMemoryPropertyFlags currFlags =
15716 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15717 // This memory type contains requiredFlags.
15718 if((requiredFlags & ~currFlags) == 0)
15719 {
15720 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15721 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15722 // Remember memory type with lowest cost.
15723 if(currCost < minCost)
15724 {
15725 *pMemoryTypeIndex = memTypeIndex;
15726 if(currCost == 0)
15727 {
15728 return VK_SUCCESS;
15729 }
15730 minCost = currCost;
15731 }
15732 }
15733 }
15734 }
15735 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15736}
15737
15738VkResult vmaFindMemoryTypeIndexForBufferInfo(
15739 VmaAllocator allocator,
15740 const VkBufferCreateInfo* pBufferCreateInfo,
15741 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15742 uint32_t* pMemoryTypeIndex)
15743{
15744 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15745 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15746 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15747 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15748
15749 const VkDevice hDev = allocator->m_hDevice;
15750 VkBuffer hBuffer = VK_NULL_HANDLE;
15751 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15752 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15753 if(res == VK_SUCCESS)
15754 {
15755 VkMemoryRequirements memReq = {};
15756 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15757 hDev, hBuffer, &memReq);
15758
15759 res = vmaFindMemoryTypeIndex(
15760 allocator,
15761 memReq.memoryTypeBits,
15762 pAllocationCreateInfo,
15763 pMemoryTypeIndex);
15764
15765 allocator->GetVulkanFunctions().vkDestroyBuffer(
15766 hDev, hBuffer, allocator->GetAllocationCallbacks());
15767 }
15768 return res;
15769}
15770
15771VkResult vmaFindMemoryTypeIndexForImageInfo(
15772 VmaAllocator allocator,
15773 const VkImageCreateInfo* pImageCreateInfo,
15774 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15775 uint32_t* pMemoryTypeIndex)
15776{
15777 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15778 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15779 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15780 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15781
15782 const VkDevice hDev = allocator->m_hDevice;
15783 VkImage hImage = VK_NULL_HANDLE;
15784 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15785 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15786 if(res == VK_SUCCESS)
15787 {
15788 VkMemoryRequirements memReq = {};
15789 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15790 hDev, hImage, &memReq);
15791
15792 res = vmaFindMemoryTypeIndex(
15793 allocator,
15794 memReq.memoryTypeBits,
15795 pAllocationCreateInfo,
15796 pMemoryTypeIndex);
15797
15798 allocator->GetVulkanFunctions().vkDestroyImage(
15799 hDev, hImage, allocator->GetAllocationCallbacks());
15800 }
15801 return res;
15802}
15803
15804VkResult vmaCreatePool(
15805 VmaAllocator allocator,
15806 const VmaPoolCreateInfo* pCreateInfo,
15807 VmaPool* pPool)
15808{
15809 VMA_ASSERT(allocator && pCreateInfo && pPool);
15810
15811 VMA_DEBUG_LOG("vmaCreatePool");
15812
15813 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15814
15815 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15816
15817#if VMA_RECORDING_ENABLED
15818 if(allocator->GetRecorder() != VMA_NULL)
15819 {
15820 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15821 }
15822#endif
15823
15824 return res;
15825}
15826
15827void vmaDestroyPool(
15828 VmaAllocator allocator,
15829 VmaPool pool)
15830{
15831 VMA_ASSERT(allocator);
15832
15833 if(pool == VK_NULL_HANDLE)
15834 {
15835 return;
15836 }
15837
15838 VMA_DEBUG_LOG("vmaDestroyPool");
15839
15840 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15841
15842#if VMA_RECORDING_ENABLED
15843 if(allocator->GetRecorder() != VMA_NULL)
15844 {
15845 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15846 }
15847#endif
15848
15849 allocator->DestroyPool(pool);
15850}
15851
15852void vmaGetPoolStats(
15853 VmaAllocator allocator,
15854 VmaPool pool,
15855 VmaPoolStats* pPoolStats)
15856{
15857 VMA_ASSERT(allocator && pool && pPoolStats);
15858
15859 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15860
15861 allocator->GetPoolStats(pool, pPoolStats);
15862}
15863
15864void vmaMakePoolAllocationsLost(
15865 VmaAllocator allocator,
15866 VmaPool pool,
15867 size_t* pLostAllocationCount)
15868{
15869 VMA_ASSERT(allocator && pool);
15870
15871 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15872
15873#if VMA_RECORDING_ENABLED
15874 if(allocator->GetRecorder() != VMA_NULL)
15875 {
15876 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15877 }
15878#endif
15879
15880 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15881}
15882
15883VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15884{
15885 VMA_ASSERT(allocator && pool);
15886
15887 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15888
15889 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15890
15891 return allocator->CheckPoolCorruption(pool);
15892}
15893
15894VkResult vmaAllocateMemory(
15895 VmaAllocator allocator,
15896 const VkMemoryRequirements* pVkMemoryRequirements,
15897 const VmaAllocationCreateInfo* pCreateInfo,
15898 VmaAllocation* pAllocation,
15899 VmaAllocationInfo* pAllocationInfo)
15900{
15901 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15902
15903 VMA_DEBUG_LOG("vmaAllocateMemory");
15904
15905 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15906
15907 VkResult result = allocator->AllocateMemory(
15908 *pVkMemoryRequirements,
15909 false, // requiresDedicatedAllocation
15910 false, // prefersDedicatedAllocation
15911 VK_NULL_HANDLE, // dedicatedBuffer
15912 VK_NULL_HANDLE, // dedicatedImage
15913 *pCreateInfo,
15914 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15915 1, // allocationCount
15916 pAllocation);
15917
15918#if VMA_RECORDING_ENABLED
15919 if(allocator->GetRecorder() != VMA_NULL)
15920 {
15921 allocator->GetRecorder()->RecordAllocateMemory(
15922 allocator->GetCurrentFrameIndex(),
15923 *pVkMemoryRequirements,
15924 *pCreateInfo,
15925 *pAllocation);
15926 }
15927#endif
15928
15929 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15930 {
15931 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15932 }
15933
15934 return result;
15935}
15936
15937VkResult vmaAllocateMemoryPages(
15938 VmaAllocator allocator,
15939 const VkMemoryRequirements* pVkMemoryRequirements,
15940 const VmaAllocationCreateInfo* pCreateInfo,
15941 size_t allocationCount,
15942 VmaAllocation* pAllocations,
15943 VmaAllocationInfo* pAllocationInfo)
15944{
15945 if(allocationCount == 0)
15946 {
15947 return VK_SUCCESS;
15948 }
15949
15950 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15951
15952 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15953
15954 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15955
15956 VkResult result = allocator->AllocateMemory(
15957 *pVkMemoryRequirements,
15958 false, // requiresDedicatedAllocation
15959 false, // prefersDedicatedAllocation
15960 VK_NULL_HANDLE, // dedicatedBuffer
15961 VK_NULL_HANDLE, // dedicatedImage
15962 *pCreateInfo,
15963 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15964 allocationCount,
15965 pAllocations);
15966
15967#if VMA_RECORDING_ENABLED
15968 if(allocator->GetRecorder() != VMA_NULL)
15969 {
15970 allocator->GetRecorder()->RecordAllocateMemoryPages(
15971 allocator->GetCurrentFrameIndex(),
15972 *pVkMemoryRequirements,
15973 *pCreateInfo,
15974 (uint64_t)allocationCount,
15975 pAllocations);
15976 }
15977#endif
15978
15979 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15980 {
15981 for(size_t i = 0; i < allocationCount; ++i)
15982 {
15983 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
15984 }
15985 }
15986
15987 return result;
15988}
15989
15990VkResult vmaAllocateMemoryForBuffer(
15991 VmaAllocator allocator,
15992 VkBuffer buffer,
15993 const VmaAllocationCreateInfo* pCreateInfo,
15994 VmaAllocation* pAllocation,
15995 VmaAllocationInfo* pAllocationInfo)
15996{
15997 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
15998
15999 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16000
16001 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16002
16003 VkMemoryRequirements vkMemReq = {};
16004 bool requiresDedicatedAllocation = false;
16005 bool prefersDedicatedAllocation = false;
16006 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16007 requiresDedicatedAllocation,
16008 prefersDedicatedAllocation);
16009
16010 VkResult result = allocator->AllocateMemory(
16011 vkMemReq,
16012 requiresDedicatedAllocation,
16013 prefersDedicatedAllocation,
16014 buffer, // dedicatedBuffer
16015 VK_NULL_HANDLE, // dedicatedImage
16016 *pCreateInfo,
16017 VMA_SUBALLOCATION_TYPE_BUFFER,
16018 1, // allocationCount
16019 pAllocation);
16020
16021#if VMA_RECORDING_ENABLED
16022 if(allocator->GetRecorder() != VMA_NULL)
16023 {
16024 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16025 allocator->GetCurrentFrameIndex(),
16026 vkMemReq,
16027 requiresDedicatedAllocation,
16028 prefersDedicatedAllocation,
16029 *pCreateInfo,
16030 *pAllocation);
16031 }
16032#endif
16033
16034 if(pAllocationInfo && result == VK_SUCCESS)
16035 {
16036 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16037 }
16038
16039 return result;
16040}
16041
16042VkResult vmaAllocateMemoryForImage(
16043 VmaAllocator allocator,
16044 VkImage image,
16045 const VmaAllocationCreateInfo* pCreateInfo,
16046 VmaAllocation* pAllocation,
16047 VmaAllocationInfo* pAllocationInfo)
16048{
16049 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16050
16051 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16052
16053 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16054
16055 VkMemoryRequirements vkMemReq = {};
16056 bool requiresDedicatedAllocation = false;
16057 bool prefersDedicatedAllocation = false;
16058 allocator->GetImageMemoryRequirements(image, vkMemReq,
16059 requiresDedicatedAllocation, prefersDedicatedAllocation);
16060
16061 VkResult result = allocator->AllocateMemory(
16062 vkMemReq,
16063 requiresDedicatedAllocation,
16064 prefersDedicatedAllocation,
16065 VK_NULL_HANDLE, // dedicatedBuffer
16066 image, // dedicatedImage
16067 *pCreateInfo,
16068 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16069 1, // allocationCount
16070 pAllocation);
16071
16072#if VMA_RECORDING_ENABLED
16073 if(allocator->GetRecorder() != VMA_NULL)
16074 {
16075 allocator->GetRecorder()->RecordAllocateMemoryForImage(
16076 allocator->GetCurrentFrameIndex(),
16077 vkMemReq,
16078 requiresDedicatedAllocation,
16079 prefersDedicatedAllocation,
16080 *pCreateInfo,
16081 *pAllocation);
16082 }
16083#endif
16084
16085 if(pAllocationInfo && result == VK_SUCCESS)
16086 {
16087 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16088 }
16089
16090 return result;
16091}
16092
16093void vmaFreeMemory(
16094 VmaAllocator allocator,
16095 VmaAllocation allocation)
16096{
16097 VMA_ASSERT(allocator);
16098
16099 if(allocation == VK_NULL_HANDLE)
16100 {
16101 return;
16102 }
16103
16104 VMA_DEBUG_LOG("vmaFreeMemory");
16105
16106 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16107
16108#if VMA_RECORDING_ENABLED
16109 if(allocator->GetRecorder() != VMA_NULL)
16110 {
16111 allocator->GetRecorder()->RecordFreeMemory(
16112 allocator->GetCurrentFrameIndex(),
16113 allocation);
16114 }
16115#endif
16116
16117 allocator->FreeMemory(
16118 1, // allocationCount
16119 &allocation);
16120}
16121
16122void vmaFreeMemoryPages(
16123 VmaAllocator allocator,
16124 size_t allocationCount,
16125 VmaAllocation* pAllocations)
16126{
16127 if(allocationCount == 0)
16128 {
16129 return;
16130 }
16131
16132 VMA_ASSERT(allocator);
16133
16134 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16135
16136 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16137
16138#if VMA_RECORDING_ENABLED
16139 if(allocator->GetRecorder() != VMA_NULL)
16140 {
16141 allocator->GetRecorder()->RecordFreeMemoryPages(
16142 allocator->GetCurrentFrameIndex(),
16143 (uint64_t)allocationCount,
16144 pAllocations);
16145 }
16146#endif
16147
16148 allocator->FreeMemory(allocationCount, pAllocations);
16149}
16150
16151VkResult vmaResizeAllocation(
16152 VmaAllocator allocator,
16153 VmaAllocation allocation,
16154 VkDeviceSize newSize)
16155{
16156 VMA_ASSERT(allocator && allocation);
16157
16158 VMA_DEBUG_LOG("vmaResizeAllocation");
16159
16160 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16161
16162#if VMA_RECORDING_ENABLED
16163 if(allocator->GetRecorder() != VMA_NULL)
16164 {
16165 allocator->GetRecorder()->RecordResizeAllocation(
16166 allocator->GetCurrentFrameIndex(),
16167 allocation,
16168 newSize);
16169 }
16170#endif
16171
16172 return allocator->ResizeAllocation(allocation, newSize);
16173}
16174
16175void vmaGetAllocationInfo(
16176 VmaAllocator allocator,
16177 VmaAllocation allocation,
16178 VmaAllocationInfo* pAllocationInfo)
16179{
16180 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16181
16182 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16183
16184#if VMA_RECORDING_ENABLED
16185 if(allocator->GetRecorder() != VMA_NULL)
16186 {
16187 allocator->GetRecorder()->RecordGetAllocationInfo(
16188 allocator->GetCurrentFrameIndex(),
16189 allocation);
16190 }
16191#endif
16192
16193 allocator->GetAllocationInfo(allocation, pAllocationInfo);
16194}
16195
16196VkBool32 vmaTouchAllocation(
16197 VmaAllocator allocator,
16198 VmaAllocation allocation)
16199{
16200 VMA_ASSERT(allocator && allocation);
16201
16202 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16203
16204#if VMA_RECORDING_ENABLED
16205 if(allocator->GetRecorder() != VMA_NULL)
16206 {
16207 allocator->GetRecorder()->RecordTouchAllocation(
16208 allocator->GetCurrentFrameIndex(),
16209 allocation);
16210 }
16211#endif
16212
16213 return allocator->TouchAllocation(allocation);
16214}
16215
16216void vmaSetAllocationUserData(
16217 VmaAllocator allocator,
16218 VmaAllocation allocation,
16219 void* pUserData)
16220{
16221 VMA_ASSERT(allocator && allocation);
16222
16223 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16224
16225 allocation->SetUserData(allocator, pUserData);
16226
16227#if VMA_RECORDING_ENABLED
16228 if(allocator->GetRecorder() != VMA_NULL)
16229 {
16230 allocator->GetRecorder()->RecordSetAllocationUserData(
16231 allocator->GetCurrentFrameIndex(),
16232 allocation,
16233 pUserData);
16234 }
16235#endif
16236}
16237
16238void vmaCreateLostAllocation(
16239 VmaAllocator allocator,
16240 VmaAllocation* pAllocation)
16241{
16242 VMA_ASSERT(allocator && pAllocation);
16243
16244 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16245
16246 allocator->CreateLostAllocation(pAllocation);
16247
16248#if VMA_RECORDING_ENABLED
16249 if(allocator->GetRecorder() != VMA_NULL)
16250 {
16251 allocator->GetRecorder()->RecordCreateLostAllocation(
16252 allocator->GetCurrentFrameIndex(),
16253 *pAllocation);
16254 }
16255#endif
16256}
16257
16258VkResult vmaMapMemory(
16259 VmaAllocator allocator,
16260 VmaAllocation allocation,
16261 void** ppData)
16262{
16263 VMA_ASSERT(allocator && allocation && ppData);
16264
16265 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16266
16267 VkResult res = allocator->Map(allocation, ppData);
16268
16269#if VMA_RECORDING_ENABLED
16270 if(allocator->GetRecorder() != VMA_NULL)
16271 {
16272 allocator->GetRecorder()->RecordMapMemory(
16273 allocator->GetCurrentFrameIndex(),
16274 allocation);
16275 }
16276#endif
16277
16278 return res;
16279}
16280
16281void vmaUnmapMemory(
16282 VmaAllocator allocator,
16283 VmaAllocation allocation)
16284{
16285 VMA_ASSERT(allocator && allocation);
16286
16287 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16288
16289#if VMA_RECORDING_ENABLED
16290 if(allocator->GetRecorder() != VMA_NULL)
16291 {
16292 allocator->GetRecorder()->RecordUnmapMemory(
16293 allocator->GetCurrentFrameIndex(),
16294 allocation);
16295 }
16296#endif
16297
16298 allocator->Unmap(allocation);
16299}
16300
16301void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16302{
16303 VMA_ASSERT(allocator && allocation);
16304
16305 VMA_DEBUG_LOG("vmaFlushAllocation");
16306
16307 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16308
16309 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16310
16311#if VMA_RECORDING_ENABLED
16312 if(allocator->GetRecorder() != VMA_NULL)
16313 {
16314 allocator->GetRecorder()->RecordFlushAllocation(
16315 allocator->GetCurrentFrameIndex(),
16316 allocation, offset, size);
16317 }
16318#endif
16319}
16320
16321void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16322{
16323 VMA_ASSERT(allocator && allocation);
16324
16325 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16326
16327 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16328
16329 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16330
16331#if VMA_RECORDING_ENABLED
16332 if(allocator->GetRecorder() != VMA_NULL)
16333 {
16334 allocator->GetRecorder()->RecordInvalidateAllocation(
16335 allocator->GetCurrentFrameIndex(),
16336 allocation, offset, size);
16337 }
16338#endif
16339}
16340
16341VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16342{
16343 VMA_ASSERT(allocator);
16344
16345 VMA_DEBUG_LOG("vmaCheckCorruption");
16346
16347 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16348
16349 return allocator->CheckCorruption(memoryTypeBits);
16350}
16351
16352VkResult vmaDefragment(
16353 VmaAllocator allocator,
16354 VmaAllocation* pAllocations,
16355 size_t allocationCount,
16356 VkBool32* pAllocationsChanged,
16357 const VmaDefragmentationInfo *pDefragmentationInfo,
16358 VmaDefragmentationStats* pDefragmentationStats)
16359{
16360 // Deprecated interface, reimplemented using new one.
16361
16362 VmaDefragmentationInfo2 info2 = {};
16363 info2.allocationCount = (uint32_t)allocationCount;
16364 info2.pAllocations = pAllocations;
16365 info2.pAllocationsChanged = pAllocationsChanged;
16366 if(pDefragmentationInfo != VMA_NULL)
16367 {
16368 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16369 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16370 }
16371 else
16372 {
16373 info2.maxCpuAllocationsToMove = UINT32_MAX;
16374 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16375 }
16376 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16377
16378 VmaDefragmentationContext ctx;
16379 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16380 if(res == VK_NOT_READY)
16381 {
16382 res = vmaDefragmentationEnd( allocator, ctx);
16383 }
16384 return res;
16385}
16386
16387VkResult vmaDefragmentationBegin(
16388 VmaAllocator allocator,
16389 const VmaDefragmentationInfo2* pInfo,
16390 VmaDefragmentationStats* pStats,
16391 VmaDefragmentationContext *pContext)
16392{
16393 VMA_ASSERT(allocator && pInfo && pContext);
16394
16395 // Degenerate case: Nothing to defragment.
16396 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16397 {
16398 return VK_SUCCESS;
16399 }
16400
16401 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16402 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16403 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16404 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16405
16406 VMA_DEBUG_LOG("vmaDefragmentationBegin");
16407
16408 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16409
16410 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16411
16412#if VMA_RECORDING_ENABLED
16413 if(allocator->GetRecorder() != VMA_NULL)
16414 {
16415 allocator->GetRecorder()->RecordDefragmentationBegin(
16416 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16417 }
16418#endif
16419
16420 return res;
16421}
16422
16423VkResult vmaDefragmentationEnd(
16424 VmaAllocator allocator,
16425 VmaDefragmentationContext context)
16426{
16427 VMA_ASSERT(allocator);
16428
16429 VMA_DEBUG_LOG("vmaDefragmentationEnd");
16430
16431 if(context != VK_NULL_HANDLE)
16432 {
16433 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16434
16435#if VMA_RECORDING_ENABLED
16436 if(allocator->GetRecorder() != VMA_NULL)
16437 {
16438 allocator->GetRecorder()->RecordDefragmentationEnd(
16439 allocator->GetCurrentFrameIndex(), context);
16440 }
16441#endif
16442
16443 return allocator->DefragmentationEnd(context);
16444 }
16445 else
16446 {
16447 return VK_SUCCESS;
16448 }
16449}
16450
16451VkResult vmaBindBufferMemory(
16452 VmaAllocator allocator,
16453 VmaAllocation allocation,
16454 VkBuffer buffer)
16455{
16456 VMA_ASSERT(allocator && allocation && buffer);
16457
16458 VMA_DEBUG_LOG("vmaBindBufferMemory");
16459
16460 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16461
16462 return allocator->BindBufferMemory(allocation, buffer);
16463}
16464
16465VkResult vmaBindImageMemory(
16466 VmaAllocator allocator,
16467 VmaAllocation allocation,
16468 VkImage image)
16469{
16470 VMA_ASSERT(allocator && allocation && image);
16471
16472 VMA_DEBUG_LOG("vmaBindImageMemory");
16473
16474 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16475
16476 return allocator->BindImageMemory(allocation, image);
16477}
16478
16479VkResult vmaCreateBuffer(
16480 VmaAllocator allocator,
16481 const VkBufferCreateInfo* pBufferCreateInfo,
16482 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16483 VkBuffer* pBuffer,
16484 VmaAllocation* pAllocation,
16485 VmaAllocationInfo* pAllocationInfo)
16486{
16487 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16488
16489 if(pBufferCreateInfo->size == 0)
16490 {
16491 return VK_ERROR_VALIDATION_FAILED_EXT;
16492 }
16493
16494 VMA_DEBUG_LOG("vmaCreateBuffer");
16495
16496 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16497
16498 *pBuffer = VK_NULL_HANDLE;
16499 *pAllocation = VK_NULL_HANDLE;
16500
16501 // 1. Create VkBuffer.
16502 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16503 allocator->m_hDevice,
16504 pBufferCreateInfo,
16505 allocator->GetAllocationCallbacks(),
16506 pBuffer);
16507 if(res >= 0)
16508 {
16509 // 2. vkGetBufferMemoryRequirements.
16510 VkMemoryRequirements vkMemReq = {};
16511 bool requiresDedicatedAllocation = false;
16512 bool prefersDedicatedAllocation = false;
16513 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16514 requiresDedicatedAllocation, prefersDedicatedAllocation);
16515
16516 // Make sure alignment requirements for specific buffer usages reported
16517 // in Physical Device Properties are included in alignment reported by memory requirements.
16518 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16519 {
16520 VMA_ASSERT(vkMemReq.alignment %
16521 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16522 }
16523 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16524 {
16525 VMA_ASSERT(vkMemReq.alignment %
16526 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16527 }
16528 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16529 {
16530 VMA_ASSERT(vkMemReq.alignment %
16531 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16532 }
16533
16534 // 3. Allocate memory using allocator.
16535 res = allocator->AllocateMemory(
16536 vkMemReq,
16537 requiresDedicatedAllocation,
16538 prefersDedicatedAllocation,
16539 *pBuffer, // dedicatedBuffer
16540 VK_NULL_HANDLE, // dedicatedImage
16541 *pAllocationCreateInfo,
16542 VMA_SUBALLOCATION_TYPE_BUFFER,
16543 1, // allocationCount
16544 pAllocation);
16545
16546#if VMA_RECORDING_ENABLED
16547 if(allocator->GetRecorder() != VMA_NULL)
16548 {
16549 allocator->GetRecorder()->RecordCreateBuffer(
16550 allocator->GetCurrentFrameIndex(),
16551 *pBufferCreateInfo,
16552 *pAllocationCreateInfo,
16553 *pAllocation);
16554 }
16555#endif
16556
16557 if(res >= 0)
16558 {
16559 // 3. Bind buffer with memory.
16560 res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16561 if(res >= 0)
16562 {
16563 // All steps succeeded.
16564 #if VMA_STATS_STRING_ENABLED
16565 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16566 #endif
16567 if(pAllocationInfo != VMA_NULL)
16568 {
16569 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16570 }
16571
16572 return VK_SUCCESS;
16573 }
16574 allocator->FreeMemory(
16575 1, // allocationCount
16576 pAllocation);
16577 *pAllocation = VK_NULL_HANDLE;
16578 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16579 *pBuffer = VK_NULL_HANDLE;
16580 return res;
16581 }
16582 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16583 *pBuffer = VK_NULL_HANDLE;
16584 return res;
16585 }
16586 return res;
16587}
16588
16589void vmaDestroyBuffer(
16590 VmaAllocator allocator,
16591 VkBuffer buffer,
16592 VmaAllocation allocation)
16593{
16594 VMA_ASSERT(allocator);
16595
16596 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16597 {
16598 return;
16599 }
16600
16601 VMA_DEBUG_LOG("vmaDestroyBuffer");
16602
16603 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16604
16605#if VMA_RECORDING_ENABLED
16606 if(allocator->GetRecorder() != VMA_NULL)
16607 {
16608 allocator->GetRecorder()->RecordDestroyBuffer(
16609 allocator->GetCurrentFrameIndex(),
16610 allocation);
16611 }
16612#endif
16613
16614 if(buffer != VK_NULL_HANDLE)
16615 {
16616 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16617 }
16618
16619 if(allocation != VK_NULL_HANDLE)
16620 {
16621 allocator->FreeMemory(
16622 1, // allocationCount
16623 &allocation);
16624 }
16625}
16626
16627VkResult vmaCreateImage(
16628 VmaAllocator allocator,
16629 const VkImageCreateInfo* pImageCreateInfo,
16630 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16631 VkImage* pImage,
16632 VmaAllocation* pAllocation,
16633 VmaAllocationInfo* pAllocationInfo)
16634{
16635 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16636
16637 if(pImageCreateInfo->extent.width == 0 ||
16638 pImageCreateInfo->extent.height == 0 ||
16639 pImageCreateInfo->extent.depth == 0 ||
16640 pImageCreateInfo->mipLevels == 0 ||
16641 pImageCreateInfo->arrayLayers == 0)
16642 {
16643 return VK_ERROR_VALIDATION_FAILED_EXT;
16644 }
16645
16646 VMA_DEBUG_LOG("vmaCreateImage");
16647
16648 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16649
16650 *pImage = VK_NULL_HANDLE;
16651 *pAllocation = VK_NULL_HANDLE;
16652
16653 // 1. Create VkImage.
16654 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16655 allocator->m_hDevice,
16656 pImageCreateInfo,
16657 allocator->GetAllocationCallbacks(),
16658 pImage);
16659 if(res >= 0)
16660 {
16661 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16662 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16663 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16664
16665 // 2. Allocate memory using allocator.
16666 VkMemoryRequirements vkMemReq = {};
16667 bool requiresDedicatedAllocation = false;
16668 bool prefersDedicatedAllocation = false;
16669 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16670 requiresDedicatedAllocation, prefersDedicatedAllocation);
16671
16672 res = allocator->AllocateMemory(
16673 vkMemReq,
16674 requiresDedicatedAllocation,
16675 prefersDedicatedAllocation,
16676 VK_NULL_HANDLE, // dedicatedBuffer
16677 *pImage, // dedicatedImage
16678 *pAllocationCreateInfo,
16679 suballocType,
16680 1, // allocationCount
16681 pAllocation);
16682
16683#if VMA_RECORDING_ENABLED
16684 if(allocator->GetRecorder() != VMA_NULL)
16685 {
16686 allocator->GetRecorder()->RecordCreateImage(
16687 allocator->GetCurrentFrameIndex(),
16688 *pImageCreateInfo,
16689 *pAllocationCreateInfo,
16690 *pAllocation);
16691 }
16692#endif
16693
16694 if(res >= 0)
16695 {
16696 // 3. Bind image with memory.
16697 res = allocator->BindImageMemory(*pAllocation, *pImage);
16698 if(res >= 0)
16699 {
16700 // All steps succeeded.
16701 #if VMA_STATS_STRING_ENABLED
16702 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16703 #endif
16704 if(pAllocationInfo != VMA_NULL)
16705 {
16706 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16707 }
16708
16709 return VK_SUCCESS;
16710 }
16711 allocator->FreeMemory(
16712 1, // allocationCount
16713 pAllocation);
16714 *pAllocation = VK_NULL_HANDLE;
16715 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16716 *pImage = VK_NULL_HANDLE;
16717 return res;
16718 }
16719 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16720 *pImage = VK_NULL_HANDLE;
16721 return res;
16722 }
16723 return res;
16724}
16725
16726void vmaDestroyImage(
16727 VmaAllocator allocator,
16728 VkImage image,
16729 VmaAllocation allocation)
16730{
16731 VMA_ASSERT(allocator);
16732
16733 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16734 {
16735 return;
16736 }
16737
16738 VMA_DEBUG_LOG("vmaDestroyImage");
16739
16740 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16741
16742#if VMA_RECORDING_ENABLED
16743 if(allocator->GetRecorder() != VMA_NULL)
16744 {
16745 allocator->GetRecorder()->RecordDestroyImage(
16746 allocator->GetCurrentFrameIndex(),
16747 allocation);
16748 }
16749#endif
16750
16751 if(image != VK_NULL_HANDLE)
16752 {
16753 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16754 }
16755 if(allocation != VK_NULL_HANDLE)
16756 {
16757 allocator->FreeMemory(
16758 1, // allocationCount
16759 &allocation);
16760 }
16761}
16762
16763#endif // #ifdef VMA_IMPLEMENTATION