1//
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(__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#elif defined(__APPLE__)
3186#include <cstdlib>
3187// aligned_alloc() is marked as macOS 10.15 only in the 10.15 SDK,
3188// avoid the mess by using a different name
3189void *vma_aligned_alloc(size_t alignment, size_t size)
3190{
3191 // alignment must be >= sizeof(void*)
3192 if(alignment < sizeof(void*))
3193 {
3194 alignment = sizeof(void*);
3195 }
3196
3197 void *pointer;
3198 if(posix_memalign(&pointer, alignment, size) == 0)
3199 return pointer;
3200 return VMA_NULL;
3201}
3202#endif
3203
3204// If your compiler is not compatible with C++11 and definition of
3205// aligned_alloc() function is missing, uncommeting following line may help:
3206
3207//#include <malloc.h>
3208
3209// Normal assert to check for programmer's errors, especially in Debug configuration.
3210#ifndef VMA_ASSERT
3211 #ifdef _DEBUG
3212 #define VMA_ASSERT(expr) assert(expr)
3213 #else
3214 #define VMA_ASSERT(expr)
3215 #endif
3216#endif
3217
3218// Assert that will be called very often, like inside data structures e.g. operator[].
3219// Making it non-empty can make program slow.
3220#ifndef VMA_HEAVY_ASSERT
3221 #ifdef _DEBUG
3222 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3223 #else
3224 #define VMA_HEAVY_ASSERT(expr)
3225 #endif
3226#endif
3227
3228#ifndef VMA_ALIGN_OF
3229 #define VMA_ALIGN_OF(type) (__alignof(type))
3230#endif
3231
3232#ifndef VMA_SYSTEM_ALIGNED_MALLOC
3233 #if defined(_WIN32)
3234 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3235 #elif defined(__APPLE__)
3236 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (vma_aligned_alloc((alignment), (size) ))
3237 #else
3238 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3239 #endif
3240#endif
3241
3242#ifndef VMA_SYSTEM_FREE
3243 #if defined(_WIN32)
3244 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
3245 #else
3246 #define VMA_SYSTEM_FREE(ptr) free(ptr)
3247 #endif
3248#endif
3249
3250#ifndef VMA_MIN
3251 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
3252#endif
3253
3254#ifndef VMA_MAX
3255 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
3256#endif
3257
3258#ifndef VMA_SWAP
3259 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
3260#endif
3261
3262#ifndef VMA_SORT
3263 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3264#endif
3265
3266#ifndef VMA_DEBUG_LOG
3267 #define VMA_DEBUG_LOG(format, ...)
3268 /*
3269 #define VMA_DEBUG_LOG(format, ...) do { \
3270 printf(format, __VA_ARGS__); \
3271 printf("\n"); \
3272 } while(false)
3273 */
3274#endif
3275
3276// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3277#if VMA_STATS_STRING_ENABLED
3278 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3279 {
3280 snprintf(s: outStr, maxlen: strLen, format: "%u", static_cast<unsigned int>(num));
3281 }
3282 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3283 {
3284 snprintf(s: outStr, maxlen: strLen, format: "%llu", static_cast<unsigned long long>(num));
3285 }
3286 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3287 {
3288 snprintf(s: outStr, maxlen: strLen, format: "%p", ptr);
3289 }
3290#endif
3291
3292#ifndef VMA_MUTEX
3293 class VmaMutex
3294 {
3295 public:
3296 void Lock() { m_Mutex.lock(); }
3297 void Unlock() { m_Mutex.unlock(); }
3298 private:
3299 std::mutex m_Mutex;
3300 };
3301 #define VMA_MUTEX VmaMutex
3302#endif
3303
3304// Read-write mutex, where "read" is shared access, "write" is exclusive access.
3305#ifndef VMA_RW_MUTEX
3306 #if VMA_USE_STL_SHARED_MUTEX
3307 // Use std::shared_mutex from C++17.
3308 #include <shared_mutex>
3309 class VmaRWMutex
3310 {
3311 public:
3312 void LockRead() { m_Mutex.lock_shared(); }
3313 void UnlockRead() { m_Mutex.unlock_shared(); }
3314 void LockWrite() { m_Mutex.lock(); }
3315 void UnlockWrite() { m_Mutex.unlock(); }
3316 private:
3317 std::shared_mutex m_Mutex;
3318 };
3319 #define VMA_RW_MUTEX VmaRWMutex
3320 #elif defined(_WIN32) && !defined(__MINGW32__)
3321 // Use SRWLOCK from WinAPI.
3322 class VmaRWMutex
3323 {
3324 public:
3325 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
3326 void LockRead() { AcquireSRWLockShared(&m_Lock); }
3327 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
3328 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
3329 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3330 private:
3331 SRWLOCK m_Lock;
3332 };
3333 #define VMA_RW_MUTEX VmaRWMutex
3334 #else
3335 // Less efficient fallback: Use normal mutex.
3336 class VmaRWMutex
3337 {
3338 public:
3339 void LockRead() { m_Mutex.Lock(); }
3340 void UnlockRead() { m_Mutex.Unlock(); }
3341 void LockWrite() { m_Mutex.Lock(); }
3342 void UnlockWrite() { m_Mutex.Unlock(); }
3343 private:
3344 VMA_MUTEX m_Mutex;
3345 };
3346 #define VMA_RW_MUTEX VmaRWMutex
3347 #endif // #if VMA_USE_STL_SHARED_MUTEX
3348#endif // #ifndef VMA_RW_MUTEX
3349
3350/*
3351If providing your own implementation, you need to implement a subset of std::atomic:
3352
3353- Constructor(uint32_t desired)
3354- uint32_t load() const
3355- void store(uint32_t desired)
3356- bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3357*/
3358#ifndef VMA_ATOMIC_UINT32
3359 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3360#endif
3361
3362#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3363 /**
3364 Every allocation will have its own memory block.
3365 Define to 1 for debugging purposes only.
3366 */
3367 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3368#endif
3369
3370#ifndef VMA_DEBUG_ALIGNMENT
3371 /**
3372 Minimum alignment of all allocations, in bytes.
3373 Set to more than 1 for debugging purposes only. Must be power of two.
3374 */
3375 #define VMA_DEBUG_ALIGNMENT (1)
3376#endif
3377
3378#ifndef VMA_DEBUG_MARGIN
3379 /**
3380 Minimum margin before and after every allocation, in bytes.
3381 Set nonzero for debugging purposes only.
3382 */
3383 #define VMA_DEBUG_MARGIN (0)
3384#endif
3385
3386#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3387 /**
3388 Define this macro to 1 to automatically fill new allocations and destroyed
3389 allocations with some bit pattern.
3390 */
3391 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3392#endif
3393
3394#ifndef VMA_DEBUG_DETECT_CORRUPTION
3395 /**
3396 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3397 enable writing magic value to the margin before and after every allocation and
3398 validating it, so that memory corruptions (out-of-bounds writes) are detected.
3399 */
3400 #define VMA_DEBUG_DETECT_CORRUPTION (0)
3401#endif
3402
3403#ifndef VMA_DEBUG_GLOBAL_MUTEX
3404 /**
3405 Set this to 1 for debugging purposes only, to enable single mutex protecting all
3406 entry calls to the library. Can be useful for debugging multithreading issues.
3407 */
3408 #define VMA_DEBUG_GLOBAL_MUTEX (0)
3409#endif
3410
3411#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3412 /**
3413 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3414 Set to more than 1 for debugging purposes only. Must be power of two.
3415 */
3416 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3417#endif
3418
3419#ifndef VMA_SMALL_HEAP_MAX_SIZE
3420 /// Maximum size of a memory heap in Vulkan to consider it "small".
3421 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3422#endif
3423
3424#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3425 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3426 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3427#endif
3428
3429#ifndef VMA_CLASS_NO_COPY
3430 #define VMA_CLASS_NO_COPY(className) \
3431 private: \
3432 className(const className&) = delete; \
3433 className& operator=(const className&) = delete;
3434#endif
3435
3436static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3437
3438// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3439static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3440
3441static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3442static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3443
3444/*******************************************************************************
3445END OF CONFIGURATION
3446*/
3447
3448static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3449
3450static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3451 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3452
3453// Returns number of bits set to 1 in (v).
3454static inline uint32_t VmaCountBitsSet(uint32_t v)
3455{
3456 uint32_t c = v - ((v >> 1) & 0x55555555);
3457 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3458 c = ((c >> 4) + c) & 0x0F0F0F0F;
3459 c = ((c >> 8) + c) & 0x00FF00FF;
3460 c = ((c >> 16) + c) & 0x0000FFFF;
3461 return c;
3462}
3463
3464// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3465// Use types like uint32_t, uint64_t as T.
3466template <typename T>
3467static inline T VmaAlignUp(T val, T align)
3468{
3469 return (val + align - 1) / align * align;
3470}
3471// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3472// Use types like uint32_t, uint64_t as T.
3473template <typename T>
3474static inline T VmaAlignDown(T val, T align)
3475{
3476 return val / align * align;
3477}
3478
3479// Division with mathematical rounding to nearest number.
3480template <typename T>
3481static inline T VmaRoundDiv(T x, T y)
3482{
3483 return (x + (y / (T)2)) / y;
3484}
3485
3486/*
3487Returns true if given number is a power of two.
3488T must be unsigned integer number or signed integer but always nonnegative.
3489For 0 returns true.
3490*/
3491template <typename T>
3492inline bool VmaIsPow2(T x)
3493{
3494 return (x & (x-1)) == 0;
3495}
3496
3497// Returns smallest power of 2 greater or equal to v.
3498static inline uint32_t VmaNextPow2(uint32_t v)
3499{
3500 v--;
3501 v |= v >> 1;
3502 v |= v >> 2;
3503 v |= v >> 4;
3504 v |= v >> 8;
3505 v |= v >> 16;
3506 v++;
3507 return v;
3508}
3509static inline uint64_t VmaNextPow2(uint64_t v)
3510{
3511 v--;
3512 v |= v >> 1;
3513 v |= v >> 2;
3514 v |= v >> 4;
3515 v |= v >> 8;
3516 v |= v >> 16;
3517 v |= v >> 32;
3518 v++;
3519 return v;
3520}
3521
3522// Returns largest power of 2 less or equal to v.
3523static inline uint32_t VmaPrevPow2(uint32_t v)
3524{
3525 v |= v >> 1;
3526 v |= v >> 2;
3527 v |= v >> 4;
3528 v |= v >> 8;
3529 v |= v >> 16;
3530 v = v ^ (v >> 1);
3531 return v;
3532}
3533static inline uint64_t VmaPrevPow2(uint64_t v)
3534{
3535 v |= v >> 1;
3536 v |= v >> 2;
3537 v |= v >> 4;
3538 v |= v >> 8;
3539 v |= v >> 16;
3540 v |= v >> 32;
3541 v = v ^ (v >> 1);
3542 return v;
3543}
3544
3545static inline bool VmaStrIsEmpty(const char* pStr)
3546{
3547 return pStr == VMA_NULL || *pStr == '\0';
3548}
3549
3550static const char* VmaAlgorithmToStr(uint32_t algorithm)
3551{
3552 switch(algorithm)
3553 {
3554 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3555 return "Linear";
3556 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3557 return "Buddy";
3558 case 0:
3559 return "Default";
3560 default:
3561 VMA_ASSERT(0);
3562 return "";
3563 }
3564}
3565
3566#ifndef VMA_SORT
3567
3568template<typename Iterator, typename Compare>
3569Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3570{
3571 Iterator centerValue = end; --centerValue;
3572 Iterator insertIndex = beg;
3573 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3574 {
3575 if(cmp(*memTypeIndex, *centerValue))
3576 {
3577 if(insertIndex != memTypeIndex)
3578 {
3579 VMA_SWAP(*memTypeIndex, *insertIndex);
3580 }
3581 ++insertIndex;
3582 }
3583 }
3584 if(insertIndex != centerValue)
3585 {
3586 VMA_SWAP(*insertIndex, *centerValue);
3587 }
3588 return insertIndex;
3589}
3590
3591template<typename Iterator, typename Compare>
3592void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3593{
3594 if(beg < end)
3595 {
3596 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3597 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3598 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3599 }
3600}
3601
3602#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3603
3604#endif // #ifndef VMA_SORT
3605
3606/*
3607Returns true if two memory blocks occupy overlapping pages.
3608ResourceA must be in less memory offset than ResourceB.
3609
3610Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3611chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3612*/
3613static inline bool VmaBlocksOnSamePage(
3614 VkDeviceSize resourceAOffset,
3615 VkDeviceSize resourceASize,
3616 VkDeviceSize resourceBOffset,
3617 VkDeviceSize pageSize)
3618{
3619 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3620 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3621 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3622 VkDeviceSize resourceBStart = resourceBOffset;
3623 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3624 return resourceAEndPage == resourceBStartPage;
3625}
3626
3627enum VmaSuballocationType
3628{
3629 VMA_SUBALLOCATION_TYPE_FREE = 0,
3630 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3631 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3632 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3633 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3634 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3635 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3636};
3637
3638/*
3639Returns true if given suballocation types could conflict and must respect
3640VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3641or linear image and another one is optimal image. If type is unknown, behave
3642conservatively.
3643*/
3644static inline bool VmaIsBufferImageGranularityConflict(
3645 VmaSuballocationType suballocType1,
3646 VmaSuballocationType suballocType2)
3647{
3648 if(suballocType1 > suballocType2)
3649 {
3650 VMA_SWAP(suballocType1, suballocType2);
3651 }
3652
3653 switch(suballocType1)
3654 {
3655 case VMA_SUBALLOCATION_TYPE_FREE:
3656 return false;
3657 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3658 return true;
3659 case VMA_SUBALLOCATION_TYPE_BUFFER:
3660 return
3661 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3662 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3663 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3664 return
3665 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3666 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3667 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3668 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3669 return
3670 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3671 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3672 return false;
3673 default:
3674 VMA_ASSERT(0);
3675 return true;
3676 }
3677}
3678
3679static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3680{
3681 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3682 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3683 for(size_t i = 0; i != numberCount; ++i, ++pDst)
3684 {
3685 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3686 }
3687}
3688
3689static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3690{
3691 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3692 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3693 for(size_t i = 0; i != numberCount; ++i, ++pSrc)
3694 {
3695 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3696 {
3697 return false;
3698 }
3699 }
3700 return true;
3701}
3702
3703// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3704struct VmaMutexLock
3705{
3706 VMA_CLASS_NO_COPY(VmaMutexLock)
3707public:
3708 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3709 m_pMutex(useMutex ? &mutex : VMA_NULL)
3710 { if(m_pMutex) { m_pMutex->Lock(); } }
3711 ~VmaMutexLock()
3712 { if(m_pMutex) { m_pMutex->Unlock(); } }
3713private:
3714 VMA_MUTEX* m_pMutex;
3715};
3716
3717// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3718struct VmaMutexLockRead
3719{
3720 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3721public:
3722 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3723 m_pMutex(useMutex ? &mutex : VMA_NULL)
3724 { if(m_pMutex) { m_pMutex->LockRead(); } }
3725 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3726private:
3727 VMA_RW_MUTEX* m_pMutex;
3728};
3729
3730// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3731struct VmaMutexLockWrite
3732{
3733 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3734public:
3735 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3736 m_pMutex(useMutex ? &mutex : VMA_NULL)
3737 { if(m_pMutex) { m_pMutex->LockWrite(); } }
3738 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3739private:
3740 VMA_RW_MUTEX* m_pMutex;
3741};
3742
3743#if VMA_DEBUG_GLOBAL_MUTEX
3744 static VMA_MUTEX gDebugGlobalMutex;
3745 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3746#else
3747 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3748#endif
3749
3750// Minimum size of a free suballocation to register it in the free suballocation collection.
3751static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3752
3753/*
3754Performs binary search and returns iterator to first element that is greater or
3755equal to (key), according to comparison (cmp).
3756
3757Cmp should return true if first argument is less than second argument.
3758
3759Returned value is the found element, if present in the collection or place where
3760new element with value (key) should be inserted.
3761*/
3762template <typename CmpLess, typename IterT, typename KeyT>
3763static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3764{
3765 size_t down = 0, up = (end - beg);
3766 while(down < up)
3767 {
3768 const size_t mid = (down + up) / 2;
3769 if(cmp(*(beg+mid), key))
3770 {
3771 down = mid + 1;
3772 }
3773 else
3774 {
3775 up = mid;
3776 }
3777 }
3778 return beg + down;
3779}
3780
3781/*
3782Returns true if all pointers in the array are not-null and unique.
3783Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3784T must be pointer type, e.g. VmaAllocation, VmaPool.
3785*/
3786template<typename T>
3787static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3788{
3789 for(uint32_t i = 0; i < count; ++i)
3790 {
3791 const T iPtr = arr[i];
3792 if(iPtr == VMA_NULL)
3793 {
3794 return false;
3795 }
3796 for(uint32_t j = i + 1; j < count; ++j)
3797 {
3798 if(iPtr == arr[j])
3799 {
3800 return false;
3801 }
3802 }
3803 }
3804 return true;
3805}
3806
3807////////////////////////////////////////////////////////////////////////////////
3808// Memory allocation
3809
3810static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3811{
3812 if((pAllocationCallbacks != VMA_NULL) &&
3813 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3814 {
3815 return (*pAllocationCallbacks->pfnAllocation)(
3816 pAllocationCallbacks->pUserData,
3817 size,
3818 alignment,
3819 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3820 }
3821 else
3822 {
3823 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3824 }
3825}
3826
3827static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3828{
3829 if((pAllocationCallbacks != VMA_NULL) &&
3830 (pAllocationCallbacks->pfnFree != VMA_NULL))
3831 {
3832 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3833 }
3834 else
3835 {
3836 VMA_SYSTEM_FREE(ptr);
3837 }
3838}
3839
3840template<typename T>
3841static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3842{
3843 return (T*)VmaMalloc(pAllocationCallbacks, size: sizeof(T), VMA_ALIGN_OF(T));
3844}
3845
3846template<typename T>
3847static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3848{
3849 return (T*)VmaMalloc(pAllocationCallbacks, size: sizeof(T) * count, VMA_ALIGN_OF(T));
3850}
3851
3852#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3853
3854#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3855
3856template<typename T>
3857static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3858{
3859 ptr->~T();
3860 VmaFree(pAllocationCallbacks, ptr);
3861}
3862
3863template<typename T>
3864static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3865{
3866 if(ptr != VMA_NULL)
3867 {
3868 for(size_t i = count; i--; )
3869 {
3870 ptr[i].~T();
3871 }
3872 VmaFree(pAllocationCallbacks, ptr);
3873 }
3874}
3875
3876// STL-compatible allocator.
3877template<typename T>
3878class VmaStlAllocator
3879{
3880public:
3881 const VkAllocationCallbacks* const m_pCallbacks;
3882 typedef T value_type;
3883
3884 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
3885 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3886
3887 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
3888 void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); }
3889
3890 template<typename U>
3891 bool operator==(const VmaStlAllocator<U>& rhs) const
3892 {
3893 return m_pCallbacks == rhs.m_pCallbacks;
3894 }
3895 template<typename U>
3896 bool operator!=(const VmaStlAllocator<U>& rhs) const
3897 {
3898 return m_pCallbacks != rhs.m_pCallbacks;
3899 }
3900
3901 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3902};
3903
3904#if VMA_USE_STL_VECTOR
3905
3906#define VmaVector std::vector
3907
3908template<typename T, typename allocatorT>
3909static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3910{
3911 vec.insert(vec.begin() + index, item);
3912}
3913
3914template<typename T, typename allocatorT>
3915static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3916{
3917 vec.erase(vec.begin() + index);
3918}
3919
3920#else // #if VMA_USE_STL_VECTOR
3921
3922/* Class with interface compatible with subset of std::vector.
3923T must be POD because constructors and destructors are not called and memcpy is
3924used for these objects. */
3925template<typename T, typename AllocatorT>
3926class VmaVector
3927{
3928public:
3929 typedef T value_type;
3930
3931 VmaVector(const AllocatorT& allocator) :
3932 m_Allocator(allocator),
3933 m_pArray(VMA_NULL),
3934 m_Count(0),
3935 m_Capacity(0)
3936 {
3937 }
3938
3939 VmaVector(size_t count, const AllocatorT& allocator) :
3940 m_Allocator(allocator),
3941 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3942 m_Count(count),
3943 m_Capacity(count)
3944 {
3945 }
3946
3947 VmaVector(const VmaVector<T, AllocatorT>& src) :
3948 m_Allocator(src.m_Allocator),
3949 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3950 m_Count(src.m_Count),
3951 m_Capacity(src.m_Count)
3952 {
3953 if(m_Count != 0)
3954 {
3955 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3956 }
3957 }
3958
3959 ~VmaVector()
3960 {
3961 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3962 }
3963
3964 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3965 {
3966 if(&rhs != this)
3967 {
3968 resize(newCount: rhs.m_Count);
3969 if(m_Count != 0)
3970 {
3971 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3972 }
3973 }
3974 return *this;
3975 }
3976
3977 bool empty() const { return m_Count == 0; }
3978 size_t size() const { return m_Count; }
3979 T* data() { return m_pArray; }
3980 const T* data() const { return m_pArray; }
3981
3982 T& operator[](size_t index)
3983 {
3984 VMA_HEAVY_ASSERT(index < m_Count);
3985 return m_pArray[index];
3986 }
3987 const T& operator[](size_t index) const
3988 {
3989 VMA_HEAVY_ASSERT(index < m_Count);
3990 return m_pArray[index];
3991 }
3992
3993 T& front()
3994 {
3995 VMA_HEAVY_ASSERT(m_Count > 0);
3996 return m_pArray[0];
3997 }
3998 const T& front() const
3999 {
4000 VMA_HEAVY_ASSERT(m_Count > 0);
4001 return m_pArray[0];
4002 }
4003 T& back()
4004 {
4005 VMA_HEAVY_ASSERT(m_Count > 0);
4006 return m_pArray[m_Count - 1];
4007 }
4008 const T& back() const
4009 {
4010 VMA_HEAVY_ASSERT(m_Count > 0);
4011 return m_pArray[m_Count - 1];
4012 }
4013
4014 void reserve(size_t newCapacity, bool freeMemory = false)
4015 {
4016 newCapacity = VMA_MAX(newCapacity, m_Count);
4017
4018 if((newCapacity < m_Capacity) && !freeMemory)
4019 {
4020 newCapacity = m_Capacity;
4021 }
4022
4023 if(newCapacity != m_Capacity)
4024 {
4025 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4026 if(m_Count != 0)
4027 {
4028 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4029 }
4030 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4031 m_Capacity = newCapacity;
4032 m_pArray = newArray;
4033 }
4034 }
4035
4036 void resize(size_t newCount, bool freeMemory = false)
4037 {
4038 size_t newCapacity = m_Capacity;
4039 if(newCount > m_Capacity)
4040 {
4041 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4042 }
4043 else if(freeMemory)
4044 {
4045 newCapacity = newCount;
4046 }
4047
4048 if(newCapacity != m_Capacity)
4049 {
4050 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4051 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4052 if(elementsToCopy != 0)
4053 {
4054 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4055 }
4056 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4057 m_Capacity = newCapacity;
4058 m_pArray = newArray;
4059 }
4060
4061 m_Count = newCount;
4062 }
4063
4064 void clear(bool freeMemory = false)
4065 {
4066 resize(newCount: 0, freeMemory);
4067 }
4068
4069 void insert(size_t index, const T& src)
4070 {
4071 VMA_HEAVY_ASSERT(index <= m_Count);
4072 const size_t oldCount = size();
4073 resize(newCount: oldCount + 1);
4074 if(index < oldCount)
4075 {
4076 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4077 }
4078 m_pArray[index] = src;
4079 }
4080
4081 void remove(size_t index)
4082 {
4083 VMA_HEAVY_ASSERT(index < m_Count);
4084 const size_t oldCount = size();
4085 if(index < oldCount - 1)
4086 {
4087 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4088 }
4089 resize(newCount: oldCount - 1);
4090 }
4091
4092 void push_back(const T& src)
4093 {
4094 const size_t newIndex = size();
4095 resize(newCount: newIndex + 1);
4096 m_pArray[newIndex] = src;
4097 }
4098
4099 void pop_back()
4100 {
4101 VMA_HEAVY_ASSERT(m_Count > 0);
4102 resize(newCount: size() - 1);
4103 }
4104
4105 void push_front(const T& src)
4106 {
4107 insert(index: 0, src);
4108 }
4109
4110 void pop_front()
4111 {
4112 VMA_HEAVY_ASSERT(m_Count > 0);
4113 remove(index: 0);
4114 }
4115
4116 typedef T* iterator;
4117
4118 iterator begin() { return m_pArray; }
4119 iterator end() { return m_pArray + m_Count; }
4120
4121private:
4122 AllocatorT m_Allocator;
4123 T* m_pArray;
4124 size_t m_Count;
4125 size_t m_Capacity;
4126};
4127
4128template<typename T, typename allocatorT>
4129static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4130{
4131 vec.insert(index, item);
4132}
4133
4134template<typename T, typename allocatorT>
4135static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4136{
4137 vec.remove(index);
4138}
4139
4140#endif // #if VMA_USE_STL_VECTOR
4141
4142template<typename CmpLess, typename VectorT>
4143size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4144{
4145 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4146 vector.data(),
4147 vector.data() + vector.size(),
4148 value,
4149 CmpLess()) - vector.data();
4150 VmaVectorInsert(vector, indexToInsert, value);
4151 return indexToInsert;
4152}
4153
4154template<typename CmpLess, typename VectorT>
4155bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4156{
4157 CmpLess comparator;
4158 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4159 vector.begin(),
4160 vector.end(),
4161 value,
4162 comparator);
4163 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4164 {
4165 size_t indexToRemove = it - vector.begin();
4166 VmaVectorRemove(vector, indexToRemove);
4167 return true;
4168 }
4169 return false;
4170}
4171
4172template<typename CmpLess, typename IterT, typename KeyT>
4173IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4174{
4175 CmpLess comparator;
4176 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4177 beg, end, value, comparator);
4178 if(it == end ||
4179 (!comparator(*it, value) && !comparator(value, *it)))
4180 {
4181 return it;
4182 }
4183 return end;
4184}
4185
4186////////////////////////////////////////////////////////////////////////////////
4187// class VmaPoolAllocator
4188
4189/*
4190Allocator for objects of type T using a list of arrays (pools) to speed up
4191allocation. Number of elements that can be allocated is not bounded because
4192allocator can create multiple blocks.
4193*/
4194template<typename T>
4195class VmaPoolAllocator
4196{
4197 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4198public:
4199 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4200 ~VmaPoolAllocator();
4201 void Clear();
4202 T* Alloc();
4203 void Free(T* ptr);
4204
4205private:
4206 union Item
4207 {
4208 uint32_t NextFreeIndex;
4209 T Value;
4210 };
4211
4212 struct ItemBlock
4213 {
4214 Item* pItems;
4215 uint32_t FirstFreeIndex;
4216 };
4217
4218 const VkAllocationCallbacks* m_pAllocationCallbacks;
4219 size_t m_ItemsPerBlock;
4220 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4221
4222 ItemBlock& CreateNewBlock();
4223};
4224
4225template<typename T>
4226VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4227 m_pAllocationCallbacks(pAllocationCallbacks),
4228 m_ItemsPerBlock(itemsPerBlock),
4229 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4230{
4231 VMA_ASSERT(itemsPerBlock > 0);
4232}
4233
4234template<typename T>
4235VmaPoolAllocator<T>::~VmaPoolAllocator()
4236{
4237 Clear();
4238}
4239
4240template<typename T>
4241void VmaPoolAllocator<T>::Clear()
4242{
4243 for(size_t i = m_ItemBlocks.size(); i--; )
4244 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4245 m_ItemBlocks.clear();
4246}
4247
4248template<typename T>
4249T* VmaPoolAllocator<T>::Alloc()
4250{
4251 for(size_t i = m_ItemBlocks.size(); i--; )
4252 {
4253 ItemBlock& block = m_ItemBlocks[i];
4254 // This block has some free items: Use first one.
4255 if(block.FirstFreeIndex != UINT32_MAX)
4256 {
4257 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4258 block.FirstFreeIndex = pItem->NextFreeIndex;
4259 return &pItem->Value;
4260 }
4261 }
4262
4263 // No block has free item: Create new one and use it.
4264 ItemBlock& newBlock = CreateNewBlock();
4265 Item* const pItem = &newBlock.pItems[0];
4266 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4267 return &pItem->Value;
4268}
4269
4270template<typename T>
4271void VmaPoolAllocator<T>::Free(T* ptr)
4272{
4273 // Search all memory blocks to find ptr.
4274 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4275 {
4276 ItemBlock& block = m_ItemBlocks[i];
4277
4278 // Casting to union.
4279 Item* pItemPtr;
4280 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4281
4282 // Check if pItemPtr is in address range of this block.
4283 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4284 {
4285 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4286 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4287 block.FirstFreeIndex = index;
4288 return;
4289 }
4290 }
4291 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4292}
4293
4294template<typename T>
4295typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4296{
4297 ItemBlock newBlock = {
4298 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4299
4300 m_ItemBlocks.push_back(newBlock);
4301
4302 // Setup singly-linked list of all free items in this block.
4303 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4304 newBlock.pItems[i].NextFreeIndex = i + 1;
4305 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4306 return m_ItemBlocks.back();
4307}
4308
4309////////////////////////////////////////////////////////////////////////////////
4310// class VmaRawList, VmaList
4311
4312#if VMA_USE_STL_LIST
4313
4314#define VmaList std::list
4315
4316#else // #if VMA_USE_STL_LIST
4317
4318template<typename T>
4319struct VmaListItem
4320{
4321 VmaListItem* pPrev;
4322 VmaListItem* pNext;
4323 T Value;
4324};
4325
4326// Doubly linked list.
4327template<typename T>
4328class VmaRawList
4329{
4330 VMA_CLASS_NO_COPY(VmaRawList)
4331public:
4332 typedef VmaListItem<T> ItemType;
4333
4334 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4335 ~VmaRawList();
4336 void Clear();
4337
4338 size_t GetCount() const { return m_Count; }
4339 bool IsEmpty() const { return m_Count == 0; }
4340
4341 ItemType* Front() { return m_pFront; }
4342 const ItemType* Front() const { return m_pFront; }
4343 ItemType* Back() { return m_pBack; }
4344 const ItemType* Back() const { return m_pBack; }
4345
4346 ItemType* PushBack();
4347 ItemType* PushFront();
4348 ItemType* PushBack(const T& value);
4349 ItemType* PushFront(const T& value);
4350 void PopBack();
4351 void PopFront();
4352
4353 // Item can be null - it means PushBack.
4354 ItemType* InsertBefore(ItemType* pItem);
4355 // Item can be null - it means PushFront.
4356 ItemType* InsertAfter(ItemType* pItem);
4357
4358 ItemType* InsertBefore(ItemType* pItem, const T& value);
4359 ItemType* InsertAfter(ItemType* pItem, const T& value);
4360
4361 void Remove(ItemType* pItem);
4362
4363private:
4364 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4365 VmaPoolAllocator<ItemType> m_ItemAllocator;
4366 ItemType* m_pFront;
4367 ItemType* m_pBack;
4368 size_t m_Count;
4369};
4370
4371template<typename T>
4372VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4373 m_pAllocationCallbacks(pAllocationCallbacks),
4374 m_ItemAllocator(pAllocationCallbacks, 128),
4375 m_pFront(VMA_NULL),
4376 m_pBack(VMA_NULL),
4377 m_Count(0)
4378{
4379}
4380
4381template<typename T>
4382VmaRawList<T>::~VmaRawList()
4383{
4384 // Intentionally not calling Clear, because that would be unnecessary
4385 // computations to return all items to m_ItemAllocator as free.
4386}
4387
4388template<typename T>
4389void VmaRawList<T>::Clear()
4390{
4391 if(IsEmpty() == false)
4392 {
4393 ItemType* pItem = m_pBack;
4394 while(pItem != VMA_NULL)
4395 {
4396 ItemType* const pPrevItem = pItem->pPrev;
4397 m_ItemAllocator.Free(pItem);
4398 pItem = pPrevItem;
4399 }
4400 m_pFront = VMA_NULL;
4401 m_pBack = VMA_NULL;
4402 m_Count = 0;
4403 }
4404}
4405
4406template<typename T>
4407VmaListItem<T>* VmaRawList<T>::PushBack()
4408{
4409 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4410 pNewItem->pNext = VMA_NULL;
4411 if(IsEmpty())
4412 {
4413 pNewItem->pPrev = VMA_NULL;
4414 m_pFront = pNewItem;
4415 m_pBack = pNewItem;
4416 m_Count = 1;
4417 }
4418 else
4419 {
4420 pNewItem->pPrev = m_pBack;
4421 m_pBack->pNext = pNewItem;
4422 m_pBack = pNewItem;
4423 ++m_Count;
4424 }
4425 return pNewItem;
4426}
4427
4428template<typename T>
4429VmaListItem<T>* VmaRawList<T>::PushFront()
4430{
4431 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4432 pNewItem->pPrev = VMA_NULL;
4433 if(IsEmpty())
4434 {
4435 pNewItem->pNext = VMA_NULL;
4436 m_pFront = pNewItem;
4437 m_pBack = pNewItem;
4438 m_Count = 1;
4439 }
4440 else
4441 {
4442 pNewItem->pNext = m_pFront;
4443 m_pFront->pPrev = pNewItem;
4444 m_pFront = pNewItem;
4445 ++m_Count;
4446 }
4447 return pNewItem;
4448}
4449
4450template<typename T>
4451VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4452{
4453 ItemType* const pNewItem = PushBack();
4454 pNewItem->Value = value;
4455 return pNewItem;
4456}
4457
4458template<typename T>
4459VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4460{
4461 ItemType* const pNewItem = PushFront();
4462 pNewItem->Value = value;
4463 return pNewItem;
4464}
4465
4466template<typename T>
4467void VmaRawList<T>::PopBack()
4468{
4469 VMA_HEAVY_ASSERT(m_Count > 0);
4470 ItemType* const pBackItem = m_pBack;
4471 ItemType* const pPrevItem = pBackItem->pPrev;
4472 if(pPrevItem != VMA_NULL)
4473 {
4474 pPrevItem->pNext = VMA_NULL;
4475 }
4476 m_pBack = pPrevItem;
4477 m_ItemAllocator.Free(pBackItem);
4478 --m_Count;
4479}
4480
4481template<typename T>
4482void VmaRawList<T>::PopFront()
4483{
4484 VMA_HEAVY_ASSERT(m_Count > 0);
4485 ItemType* const pFrontItem = m_pFront;
4486 ItemType* const pNextItem = pFrontItem->pNext;
4487 if(pNextItem != VMA_NULL)
4488 {
4489 pNextItem->pPrev = VMA_NULL;
4490 }
4491 m_pFront = pNextItem;
4492 m_ItemAllocator.Free(pFrontItem);
4493 --m_Count;
4494}
4495
4496template<typename T>
4497void VmaRawList<T>::Remove(ItemType* pItem)
4498{
4499 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4500 VMA_HEAVY_ASSERT(m_Count > 0);
4501
4502 if(pItem->pPrev != VMA_NULL)
4503 {
4504 pItem->pPrev->pNext = pItem->pNext;
4505 }
4506 else
4507 {
4508 VMA_HEAVY_ASSERT(m_pFront == pItem);
4509 m_pFront = pItem->pNext;
4510 }
4511
4512 if(pItem->pNext != VMA_NULL)
4513 {
4514 pItem->pNext->pPrev = pItem->pPrev;
4515 }
4516 else
4517 {
4518 VMA_HEAVY_ASSERT(m_pBack == pItem);
4519 m_pBack = pItem->pPrev;
4520 }
4521
4522 m_ItemAllocator.Free(pItem);
4523 --m_Count;
4524}
4525
4526template<typename T>
4527VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4528{
4529 if(pItem != VMA_NULL)
4530 {
4531 ItemType* const prevItem = pItem->pPrev;
4532 ItemType* const newItem = m_ItemAllocator.Alloc();
4533 newItem->pPrev = prevItem;
4534 newItem->pNext = pItem;
4535 pItem->pPrev = newItem;
4536 if(prevItem != VMA_NULL)
4537 {
4538 prevItem->pNext = newItem;
4539 }
4540 else
4541 {
4542 VMA_HEAVY_ASSERT(m_pFront == pItem);
4543 m_pFront = newItem;
4544 }
4545 ++m_Count;
4546 return newItem;
4547 }
4548 else
4549 return PushBack();
4550}
4551
4552template<typename T>
4553VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4554{
4555 if(pItem != VMA_NULL)
4556 {
4557 ItemType* const nextItem = pItem->pNext;
4558 ItemType* const newItem = m_ItemAllocator.Alloc();
4559 newItem->pNext = nextItem;
4560 newItem->pPrev = pItem;
4561 pItem->pNext = newItem;
4562 if(nextItem != VMA_NULL)
4563 {
4564 nextItem->pPrev = newItem;
4565 }
4566 else
4567 {
4568 VMA_HEAVY_ASSERT(m_pBack == pItem);
4569 m_pBack = newItem;
4570 }
4571 ++m_Count;
4572 return newItem;
4573 }
4574 else
4575 return PushFront();
4576}
4577
4578template<typename T>
4579VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4580{
4581 ItemType* const newItem = InsertBefore(pItem);
4582 newItem->Value = value;
4583 return newItem;
4584}
4585
4586template<typename T>
4587VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4588{
4589 ItemType* const newItem = InsertAfter(pItem);
4590 newItem->Value = value;
4591 return newItem;
4592}
4593
4594template<typename T, typename AllocatorT>
4595class VmaList
4596{
4597 VMA_CLASS_NO_COPY(VmaList)
4598public:
4599 class iterator
4600 {
4601 public:
4602 iterator() :
4603 m_pList(VMA_NULL),
4604 m_pItem(VMA_NULL)
4605 {
4606 }
4607
4608 T& operator*() const
4609 {
4610 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4611 return m_pItem->Value;
4612 }
4613 T* operator->() const
4614 {
4615 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4616 return &m_pItem->Value;
4617 }
4618
4619 iterator& operator++()
4620 {
4621 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4622 m_pItem = m_pItem->pNext;
4623 return *this;
4624 }
4625 iterator& operator--()
4626 {
4627 if(m_pItem != VMA_NULL)
4628 {
4629 m_pItem = m_pItem->pPrev;
4630 }
4631 else
4632 {
4633 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4634 m_pItem = m_pList->Back();
4635 }
4636 return *this;
4637 }
4638
4639 iterator operator++(int)
4640 {
4641 iterator result = *this;
4642 ++*this;
4643 return result;
4644 }
4645 iterator operator--(int)
4646 {
4647 iterator result = *this;
4648 --*this;
4649 return result;
4650 }
4651
4652 bool operator==(const iterator& rhs) const
4653 {
4654 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4655 return m_pItem == rhs.m_pItem;
4656 }
4657 bool operator!=(const iterator& rhs) const
4658 {
4659 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4660 return m_pItem != rhs.m_pItem;
4661 }
4662
4663 private:
4664 VmaRawList<T>* m_pList;
4665 VmaListItem<T>* m_pItem;
4666
4667 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4668 m_pList(pList),
4669 m_pItem(pItem)
4670 {
4671 }
4672
4673 friend class VmaList<T, AllocatorT>;
4674 };
4675
4676 class const_iterator
4677 {
4678 public:
4679 const_iterator() :
4680 m_pList(VMA_NULL),
4681 m_pItem(VMA_NULL)
4682 {
4683 }
4684
4685 const_iterator(const iterator& src) :
4686 m_pList(src.m_pList),
4687 m_pItem(src.m_pItem)
4688 {
4689 }
4690
4691 const T& operator*() const
4692 {
4693 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4694 return m_pItem->Value;
4695 }
4696 const T* operator->() const
4697 {
4698 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4699 return &m_pItem->Value;
4700 }
4701
4702 const_iterator& operator++()
4703 {
4704 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4705 m_pItem = m_pItem->pNext;
4706 return *this;
4707 }
4708 const_iterator& operator--()
4709 {
4710 if(m_pItem != VMA_NULL)
4711 {
4712 m_pItem = m_pItem->pPrev;
4713 }
4714 else
4715 {
4716 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4717 m_pItem = m_pList->Back();
4718 }
4719 return *this;
4720 }
4721
4722 const_iterator operator++(int)
4723 {
4724 const_iterator result = *this;
4725 ++*this;
4726 return result;
4727 }
4728 const_iterator operator--(int)
4729 {
4730 const_iterator result = *this;
4731 --*this;
4732 return result;
4733 }
4734
4735 bool operator==(const const_iterator& rhs) const
4736 {
4737 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4738 return m_pItem == rhs.m_pItem;
4739 }
4740 bool operator!=(const const_iterator& rhs) const
4741 {
4742 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4743 return m_pItem != rhs.m_pItem;
4744 }
4745
4746 private:
4747 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4748 m_pList(pList),
4749 m_pItem(pItem)
4750 {
4751 }
4752
4753 const VmaRawList<T>* m_pList;
4754 const VmaListItem<T>* m_pItem;
4755
4756 friend class VmaList<T, AllocatorT>;
4757 };
4758
4759 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4760
4761 bool empty() const { return m_RawList.IsEmpty(); }
4762 size_t size() const { return m_RawList.GetCount(); }
4763
4764 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
4765 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4766
4767 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
4768 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4769
4770 void clear() { m_RawList.Clear(); }
4771 void push_back(const T& value) { m_RawList.PushBack(value); }
4772 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4773 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4774
4775private:
4776 VmaRawList<T> m_RawList;
4777};
4778
4779#endif // #if VMA_USE_STL_LIST
4780
4781////////////////////////////////////////////////////////////////////////////////
4782// class VmaMap
4783
4784// Unused in this version.
4785#if 0
4786
4787#if VMA_USE_STL_UNORDERED_MAP
4788
4789#define VmaPair std::pair
4790
4791#define VMA_MAP_TYPE(KeyT, ValueT) \
4792 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4793
4794#else // #if VMA_USE_STL_UNORDERED_MAP
4795
4796template<typename T1, typename T2>
4797struct VmaPair
4798{
4799 T1 first;
4800 T2 second;
4801
4802 VmaPair() : first(), second() { }
4803 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4804};
4805
4806/* Class compatible with subset of interface of std::unordered_map.
4807KeyT, ValueT must be POD because they will be stored in VmaVector.
4808*/
4809template<typename KeyT, typename ValueT>
4810class VmaMap
4811{
4812public:
4813 typedef VmaPair<KeyT, ValueT> PairType;
4814 typedef PairType* iterator;
4815
4816 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4817
4818 iterator begin() { return m_Vector.begin(); }
4819 iterator end() { return m_Vector.end(); }
4820
4821 void insert(const PairType& pair);
4822 iterator find(const KeyT& key);
4823 void erase(iterator it);
4824
4825private:
4826 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4827};
4828
4829#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4830
4831template<typename FirstT, typename SecondT>
4832struct VmaPairFirstLess
4833{
4834 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4835 {
4836 return lhs.first < rhs.first;
4837 }
4838 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4839 {
4840 return lhs.first < rhsFirst;
4841 }
4842};
4843
4844template<typename KeyT, typename ValueT>
4845void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4846{
4847 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4848 m_Vector.data(),
4849 m_Vector.data() + m_Vector.size(),
4850 pair,
4851 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4852 VmaVectorInsert(m_Vector, indexToInsert, pair);
4853}
4854
4855template<typename KeyT, typename ValueT>
4856VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4857{
4858 PairType* it = VmaBinaryFindFirstNotLess(
4859 m_Vector.data(),
4860 m_Vector.data() + m_Vector.size(),
4861 key,
4862 VmaPairFirstLess<KeyT, ValueT>());
4863 if((it != m_Vector.end()) && (it->first == key))
4864 {
4865 return it;
4866 }
4867 else
4868 {
4869 return m_Vector.end();
4870 }
4871}
4872
4873template<typename KeyT, typename ValueT>
4874void VmaMap<KeyT, ValueT>::erase(iterator it)
4875{
4876 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4877}
4878
4879#endif // #if VMA_USE_STL_UNORDERED_MAP
4880
4881#endif // #if 0
4882
4883////////////////////////////////////////////////////////////////////////////////
4884
4885class VmaDeviceMemoryBlock;
4886
4887enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4888
4889struct VmaAllocation_T
4890{
4891 VMA_CLASS_NO_COPY(VmaAllocation_T)
4892private:
4893 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4894
4895 enum FLAGS
4896 {
4897 FLAG_USER_DATA_STRING = 0x01,
4898 };
4899
4900public:
4901 enum ALLOCATION_TYPE
4902 {
4903 ALLOCATION_TYPE_NONE,
4904 ALLOCATION_TYPE_BLOCK,
4905 ALLOCATION_TYPE_DEDICATED,
4906 };
4907
4908 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4909 m_Alignment(1),
4910 m_Size(0),
4911 m_pUserData(VMA_NULL),
4912 m_LastUseFrameIndex(currentFrameIndex),
4913 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4914 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4915 m_MapCount(0),
4916 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4917 {
4918#if VMA_STATS_STRING_ENABLED
4919 m_CreationFrameIndex = currentFrameIndex;
4920 m_BufferImageUsage = 0;
4921#endif
4922 }
4923
4924 ~VmaAllocation_T()
4925 {
4926 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4927
4928 // Check if owned string was freed.
4929 VMA_ASSERT(m_pUserData == VMA_NULL);
4930 }
4931
4932 void InitBlockAllocation(
4933 VmaPool hPool,
4934 VmaDeviceMemoryBlock* block,
4935 VkDeviceSize offset,
4936 VkDeviceSize alignment,
4937 VkDeviceSize size,
4938 VmaSuballocationType suballocationType,
4939 bool mapped,
4940 bool canBecomeLost)
4941 {
4942 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4943 VMA_ASSERT(block != VMA_NULL);
4944 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4945 m_Alignment = alignment;
4946 m_Size = size;
4947 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4948 m_SuballocationType = (uint8_t)suballocationType;
4949 m_BlockAllocation.m_hPool = hPool;
4950 m_BlockAllocation.m_Block = block;
4951 m_BlockAllocation.m_Offset = offset;
4952 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4953 }
4954
4955 void InitLost()
4956 {
4957 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4958 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4959 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4960 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4961 m_BlockAllocation.m_Block = VMA_NULL;
4962 m_BlockAllocation.m_Offset = 0;
4963 m_BlockAllocation.m_CanBecomeLost = true;
4964 }
4965
4966 void ChangeBlockAllocation(
4967 VmaAllocator hAllocator,
4968 VmaDeviceMemoryBlock* block,
4969 VkDeviceSize offset);
4970
4971 void ChangeSize(VkDeviceSize newSize);
4972 void ChangeOffset(VkDeviceSize newOffset);
4973
4974 // pMappedData not null means allocation is created with MAPPED flag.
4975 void InitDedicatedAllocation(
4976 uint32_t memoryTypeIndex,
4977 VkDeviceMemory hMemory,
4978 VmaSuballocationType suballocationType,
4979 void* pMappedData,
4980 VkDeviceSize size)
4981 {
4982 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4983 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4984 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4985 m_Alignment = 0;
4986 m_Size = size;
4987 m_SuballocationType = (uint8_t)suballocationType;
4988 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4989 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
4990 m_DedicatedAllocation.m_hMemory = hMemory;
4991 m_DedicatedAllocation.m_pMappedData = pMappedData;
4992 }
4993
4994 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
4995 VkDeviceSize GetAlignment() const { return m_Alignment; }
4996 VkDeviceSize GetSize() const { return m_Size; }
4997 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
4998 void* GetUserData() const { return m_pUserData; }
4999 void SetUserData(VmaAllocator hAllocator, void* pUserData);
5000 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
5001
5002 VmaDeviceMemoryBlock* GetBlock() const
5003 {
5004 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5005 return m_BlockAllocation.m_Block;
5006 }
5007 VkDeviceSize GetOffset() const;
5008 VkDeviceMemory GetMemory() const;
5009 uint32_t GetMemoryTypeIndex() const;
5010 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5011 void* GetMappedData() const;
5012 bool CanBecomeLost() const;
5013 VmaPool GetPool() const;
5014
5015 uint32_t GetLastUseFrameIndex() const
5016 {
5017 return m_LastUseFrameIndex.load();
5018 }
5019 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5020 {
5021 return m_LastUseFrameIndex.compare_exchange_weak(i1&: expected, i2: desired);
5022 }
5023 /*
5024 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5025 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5026 - Else, returns false.
5027
5028 If hAllocation is already lost, assert - you should not call it then.
5029 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5030 */
5031 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5032
5033 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5034 {
5035 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5036 outInfo.blockCount = 1;
5037 outInfo.allocationCount = 1;
5038 outInfo.unusedRangeCount = 0;
5039 outInfo.usedBytes = m_Size;
5040 outInfo.unusedBytes = 0;
5041 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5042 outInfo.unusedRangeSizeMin = UINT64_MAX;
5043 outInfo.unusedRangeSizeMax = 0;
5044 }
5045
5046 void BlockAllocMap();
5047 void BlockAllocUnmap();
5048 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5049 void DedicatedAllocUnmap(VmaAllocator hAllocator);
5050
5051#if VMA_STATS_STRING_ENABLED
5052 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
5053 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5054
5055 void InitBufferImageUsage(uint32_t bufferImageUsage)
5056 {
5057 VMA_ASSERT(m_BufferImageUsage == 0);
5058 m_BufferImageUsage = bufferImageUsage;
5059 }
5060
5061 void PrintParameters(class VmaJsonWriter& json) const;
5062#endif
5063
5064private:
5065 VkDeviceSize m_Alignment;
5066 VkDeviceSize m_Size;
5067 void* m_pUserData;
5068 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5069 uint8_t m_Type; // ALLOCATION_TYPE
5070 uint8_t m_SuballocationType; // VmaSuballocationType
5071 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5072 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5073 uint8_t m_MapCount;
5074 uint8_t m_Flags; // enum FLAGS
5075
5076 // Allocation out of VmaDeviceMemoryBlock.
5077 struct BlockAllocation
5078 {
5079 VmaPool m_hPool; // Null if belongs to general memory.
5080 VmaDeviceMemoryBlock* m_Block;
5081 VkDeviceSize m_Offset;
5082 bool m_CanBecomeLost;
5083 };
5084
5085 // Allocation for an object that has its own private VkDeviceMemory.
5086 struct DedicatedAllocation
5087 {
5088 uint32_t m_MemoryTypeIndex;
5089 VkDeviceMemory m_hMemory;
5090 void* m_pMappedData; // Not null means memory is mapped.
5091 };
5092
5093 union
5094 {
5095 // Allocation out of VmaDeviceMemoryBlock.
5096 BlockAllocation m_BlockAllocation;
5097 // Allocation for an object that has its own private VkDeviceMemory.
5098 DedicatedAllocation m_DedicatedAllocation;
5099 };
5100
5101#if VMA_STATS_STRING_ENABLED
5102 uint32_t m_CreationFrameIndex;
5103 uint32_t m_BufferImageUsage; // 0 if unknown.
5104#endif
5105
5106 void FreeUserDataString(VmaAllocator hAllocator);
5107};
5108
5109/*
5110Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5111allocated memory block or free.
5112*/
5113struct VmaSuballocation
5114{
5115 VkDeviceSize offset;
5116 VkDeviceSize size;
5117 VmaAllocation hAllocation;
5118 VmaSuballocationType type;
5119};
5120
5121// Comparator for offsets.
5122struct VmaSuballocationOffsetLess
5123{
5124 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5125 {
5126 return lhs.offset < rhs.offset;
5127 }
5128};
5129struct VmaSuballocationOffsetGreater
5130{
5131 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5132 {
5133 return lhs.offset > rhs.offset;
5134 }
5135};
5136
5137typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5138
5139// Cost of one additional allocation lost, as equivalent in bytes.
5140static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5141
5142/*
5143Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5144
5145If canMakeOtherLost was false:
5146- item points to a FREE suballocation.
5147- itemsToMakeLostCount is 0.
5148
5149If canMakeOtherLost was true:
5150- item points to first of sequence of suballocations, which are either FREE,
5151 or point to VmaAllocations that can become lost.
5152- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5153 the requested allocation to succeed.
5154*/
5155struct VmaAllocationRequest
5156{
5157 VkDeviceSize offset;
5158 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5159 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5160 VmaSuballocationList::iterator item;
5161 size_t itemsToMakeLostCount;
5162 void* customData;
5163
5164 VkDeviceSize CalcCost() const
5165 {
5166 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5167 }
5168};
5169
5170/*
5171Data structure used for bookkeeping of allocations and unused ranges of memory
5172in a single VkDeviceMemory block.
5173*/
5174class VmaBlockMetadata
5175{
5176public:
5177 VmaBlockMetadata(VmaAllocator hAllocator);
5178 virtual ~VmaBlockMetadata() { }
5179 virtual void Init(VkDeviceSize size) { m_Size = size; }
5180
5181 // Validates all data structures inside this object. If not valid, returns false.
5182 virtual bool Validate() const = 0;
5183 VkDeviceSize GetSize() const { return m_Size; }
5184 virtual size_t GetAllocationCount() const = 0;
5185 virtual VkDeviceSize GetSumFreeSize() const = 0;
5186 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5187 // Returns true if this block is empty - contains only single free suballocation.
5188 virtual bool IsEmpty() const = 0;
5189
5190 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5191 // Shouldn't modify blockCount.
5192 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5193
5194#if VMA_STATS_STRING_ENABLED
5195 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5196#endif
5197
5198 // Tries to find a place for suballocation with given parameters inside this block.
5199 // If succeeded, fills pAllocationRequest and returns true.
5200 // If failed, returns false.
5201 virtual bool CreateAllocationRequest(
5202 uint32_t currentFrameIndex,
5203 uint32_t frameInUseCount,
5204 VkDeviceSize bufferImageGranularity,
5205 VkDeviceSize allocSize,
5206 VkDeviceSize allocAlignment,
5207 bool upperAddress,
5208 VmaSuballocationType allocType,
5209 bool canMakeOtherLost,
5210 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5211 uint32_t strategy,
5212 VmaAllocationRequest* pAllocationRequest) = 0;
5213
5214 virtual bool MakeRequestedAllocationsLost(
5215 uint32_t currentFrameIndex,
5216 uint32_t frameInUseCount,
5217 VmaAllocationRequest* pAllocationRequest) = 0;
5218
5219 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5220
5221 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5222
5223 // Makes actual allocation based on request. Request must already be checked and valid.
5224 virtual void Alloc(
5225 const VmaAllocationRequest& request,
5226 VmaSuballocationType type,
5227 VkDeviceSize allocSize,
5228 bool upperAddress,
5229 VmaAllocation hAllocation) = 0;
5230
5231 // Frees suballocation assigned to given memory region.
5232 virtual void Free(const VmaAllocation allocation) = 0;
5233 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5234
5235 // Tries to resize (grow or shrink) space for given allocation, in place.
5236 virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; }
5237
5238protected:
5239 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5240
5241#if VMA_STATS_STRING_ENABLED
5242 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5243 VkDeviceSize unusedBytes,
5244 size_t allocationCount,
5245 size_t unusedRangeCount) const;
5246 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5247 VkDeviceSize offset,
5248 VmaAllocation hAllocation) const;
5249 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5250 VkDeviceSize offset,
5251 VkDeviceSize size) const;
5252 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5253#endif
5254
5255private:
5256 VkDeviceSize m_Size;
5257 const VkAllocationCallbacks* m_pAllocationCallbacks;
5258};
5259
5260#define VMA_VALIDATE(cond) do { if(!(cond)) { \
5261 VMA_ASSERT(0 && "Validation failed: " #cond); \
5262 return false; \
5263 } } while(false)
5264
5265class VmaBlockMetadata_Generic : public VmaBlockMetadata
5266{
5267 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5268public:
5269 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5270 virtual ~VmaBlockMetadata_Generic();
5271 virtual void Init(VkDeviceSize size);
5272
5273 virtual bool Validate() const;
5274 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
5275 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5276 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5277 virtual bool IsEmpty() const;
5278
5279 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5280 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5281
5282#if VMA_STATS_STRING_ENABLED
5283 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5284#endif
5285
5286 virtual bool CreateAllocationRequest(
5287 uint32_t currentFrameIndex,
5288 uint32_t frameInUseCount,
5289 VkDeviceSize bufferImageGranularity,
5290 VkDeviceSize allocSize,
5291 VkDeviceSize allocAlignment,
5292 bool upperAddress,
5293 VmaSuballocationType allocType,
5294 bool canMakeOtherLost,
5295 uint32_t strategy,
5296 VmaAllocationRequest* pAllocationRequest);
5297
5298 virtual bool MakeRequestedAllocationsLost(
5299 uint32_t currentFrameIndex,
5300 uint32_t frameInUseCount,
5301 VmaAllocationRequest* pAllocationRequest);
5302
5303 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5304
5305 virtual VkResult CheckCorruption(const void* pBlockData);
5306
5307 virtual void Alloc(
5308 const VmaAllocationRequest& request,
5309 VmaSuballocationType type,
5310 VkDeviceSize allocSize,
5311 bool upperAddress,
5312 VmaAllocation hAllocation);
5313
5314 virtual void Free(const VmaAllocation allocation);
5315 virtual void FreeAtOffset(VkDeviceSize offset);
5316
5317 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5318
5319 ////////////////////////////////////////////////////////////////////////////////
5320 // For defragmentation
5321
5322 bool IsBufferImageGranularityConflictPossible(
5323 VkDeviceSize bufferImageGranularity,
5324 VmaSuballocationType& inOutPrevSuballocType) const;
5325
5326private:
5327 friend class VmaDefragmentationAlgorithm_Generic;
5328 friend class VmaDefragmentationAlgorithm_Fast;
5329
5330 uint32_t m_FreeCount;
5331 VkDeviceSize m_SumFreeSize;
5332 VmaSuballocationList m_Suballocations;
5333 // Suballocations that are free and have size greater than certain threshold.
5334 // Sorted by size, ascending.
5335 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5336
5337 bool ValidateFreeSuballocationList() const;
5338
5339 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5340 // If yes, fills pOffset and returns true. If no, returns false.
5341 bool CheckAllocation(
5342 uint32_t currentFrameIndex,
5343 uint32_t frameInUseCount,
5344 VkDeviceSize bufferImageGranularity,
5345 VkDeviceSize allocSize,
5346 VkDeviceSize allocAlignment,
5347 VmaSuballocationType allocType,
5348 VmaSuballocationList::const_iterator suballocItem,
5349 bool canMakeOtherLost,
5350 VkDeviceSize* pOffset,
5351 size_t* itemsToMakeLostCount,
5352 VkDeviceSize* pSumFreeSize,
5353 VkDeviceSize* pSumItemSize) const;
5354 // Given free suballocation, it merges it with following one, which must also be free.
5355 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5356 // Releases given suballocation, making it free.
5357 // Merges it with adjacent free suballocations if applicable.
5358 // Returns iterator to new free suballocation at this place.
5359 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5360 // Given free suballocation, it inserts it into sorted list of
5361 // m_FreeSuballocationsBySize if it's suitable.
5362 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5363 // Given free suballocation, it removes it from sorted list of
5364 // m_FreeSuballocationsBySize if it's suitable.
5365 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5366};
5367
5368/*
5369Allocations and their references in internal data structure look like this:
5370
5371if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5372
5373 0 +-------+
5374 | |
5375 | |
5376 | |
5377 +-------+
5378 | Alloc | 1st[m_1stNullItemsBeginCount]
5379 +-------+
5380 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5381 +-------+
5382 | ... |
5383 +-------+
5384 | Alloc | 1st[1st.size() - 1]
5385 +-------+
5386 | |
5387 | |
5388 | |
5389GetSize() +-------+
5390
5391if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5392
5393 0 +-------+
5394 | Alloc | 2nd[0]
5395 +-------+
5396 | Alloc | 2nd[1]
5397 +-------+
5398 | ... |
5399 +-------+
5400 | Alloc | 2nd[2nd.size() - 1]
5401 +-------+
5402 | |
5403 | |
5404 | |
5405 +-------+
5406 | Alloc | 1st[m_1stNullItemsBeginCount]
5407 +-------+
5408 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5409 +-------+
5410 | ... |
5411 +-------+
5412 | Alloc | 1st[1st.size() - 1]
5413 +-------+
5414 | |
5415GetSize() +-------+
5416
5417if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5418
5419 0 +-------+
5420 | |
5421 | |
5422 | |
5423 +-------+
5424 | Alloc | 1st[m_1stNullItemsBeginCount]
5425 +-------+
5426 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5427 +-------+
5428 | ... |
5429 +-------+
5430 | Alloc | 1st[1st.size() - 1]
5431 +-------+
5432 | |
5433 | |
5434 | |
5435 +-------+
5436 | Alloc | 2nd[2nd.size() - 1]
5437 +-------+
5438 | ... |
5439 +-------+
5440 | Alloc | 2nd[1]
5441 +-------+
5442 | Alloc | 2nd[0]
5443GetSize() +-------+
5444
5445*/
5446class VmaBlockMetadata_Linear : public VmaBlockMetadata
5447{
5448 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5449public:
5450 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5451 virtual ~VmaBlockMetadata_Linear();
5452 virtual void Init(VkDeviceSize size);
5453
5454 virtual bool Validate() const;
5455 virtual size_t GetAllocationCount() const;
5456 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5457 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5458 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5459
5460 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5461 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5462
5463#if VMA_STATS_STRING_ENABLED
5464 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5465#endif
5466
5467 virtual bool CreateAllocationRequest(
5468 uint32_t currentFrameIndex,
5469 uint32_t frameInUseCount,
5470 VkDeviceSize bufferImageGranularity,
5471 VkDeviceSize allocSize,
5472 VkDeviceSize allocAlignment,
5473 bool upperAddress,
5474 VmaSuballocationType allocType,
5475 bool canMakeOtherLost,
5476 uint32_t strategy,
5477 VmaAllocationRequest* pAllocationRequest);
5478
5479 virtual bool MakeRequestedAllocationsLost(
5480 uint32_t currentFrameIndex,
5481 uint32_t frameInUseCount,
5482 VmaAllocationRequest* pAllocationRequest);
5483
5484 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5485
5486 virtual VkResult CheckCorruption(const void* pBlockData);
5487
5488 virtual void Alloc(
5489 const VmaAllocationRequest& request,
5490 VmaSuballocationType type,
5491 VkDeviceSize allocSize,
5492 bool upperAddress,
5493 VmaAllocation hAllocation);
5494
5495 virtual void Free(const VmaAllocation allocation);
5496 virtual void FreeAtOffset(VkDeviceSize offset);
5497
5498private:
5499 /*
5500 There are two suballocation vectors, used in ping-pong way.
5501 The one with index m_1stVectorIndex is called 1st.
5502 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5503 2nd can be non-empty only when 1st is not empty.
5504 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5505 */
5506 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5507
5508 enum SECOND_VECTOR_MODE
5509 {
5510 SECOND_VECTOR_EMPTY,
5511 /*
5512 Suballocations in 2nd vector are created later than the ones in 1st, but they
5513 all have smaller offset.
5514 */
5515 SECOND_VECTOR_RING_BUFFER,
5516 /*
5517 Suballocations in 2nd vector are upper side of double stack.
5518 They all have offsets higher than those in 1st vector.
5519 Top of this stack means smaller offsets, but higher indices in this vector.
5520 */
5521 SECOND_VECTOR_DOUBLE_STACK,
5522 };
5523
5524 VkDeviceSize m_SumFreeSize;
5525 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5526 uint32_t m_1stVectorIndex;
5527 SECOND_VECTOR_MODE m_2ndVectorMode;
5528
5529 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5530 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5531 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5532 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5533
5534 // Number of items in 1st vector with hAllocation = null at the beginning.
5535 size_t m_1stNullItemsBeginCount;
5536 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5537 size_t m_1stNullItemsMiddleCount;
5538 // Number of items in 2nd vector with hAllocation = null.
5539 size_t m_2ndNullItemsCount;
5540
5541 bool ShouldCompact1st() const;
5542 void CleanupAfterFree();
5543};
5544
5545/*
5546- GetSize() is the original size of allocated memory block.
5547- m_UsableSize is this size aligned down to a power of two.
5548 All allocations and calculations happen relative to m_UsableSize.
5549- GetUnusableSize() is the difference between them.
5550 It is repoted as separate, unused range, not available for allocations.
5551
5552Node at level 0 has size = m_UsableSize.
5553Each next level contains nodes with size 2 times smaller than current level.
5554m_LevelCount is the maximum number of levels to use in the current object.
5555*/
5556class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5557{
5558 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5559public:
5560 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5561 virtual ~VmaBlockMetadata_Buddy();
5562 virtual void Init(VkDeviceSize size);
5563
5564 virtual bool Validate() const;
5565 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
5566 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5567 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5568 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5569
5570 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5571 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5572
5573#if VMA_STATS_STRING_ENABLED
5574 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5575#endif
5576
5577 virtual bool CreateAllocationRequest(
5578 uint32_t currentFrameIndex,
5579 uint32_t frameInUseCount,
5580 VkDeviceSize bufferImageGranularity,
5581 VkDeviceSize allocSize,
5582 VkDeviceSize allocAlignment,
5583 bool upperAddress,
5584 VmaSuballocationType allocType,
5585 bool canMakeOtherLost,
5586 uint32_t strategy,
5587 VmaAllocationRequest* pAllocationRequest);
5588
5589 virtual bool MakeRequestedAllocationsLost(
5590 uint32_t currentFrameIndex,
5591 uint32_t frameInUseCount,
5592 VmaAllocationRequest* pAllocationRequest);
5593
5594 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5595
5596 virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5597
5598 virtual void Alloc(
5599 const VmaAllocationRequest& request,
5600 VmaSuballocationType type,
5601 VkDeviceSize allocSize,
5602 bool upperAddress,
5603 VmaAllocation hAllocation);
5604
5605 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(alloc: allocation, offset: allocation->GetOffset()); }
5606 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5607
5608private:
5609 static const VkDeviceSize MIN_NODE_SIZE = 32;
5610 static const size_t MAX_LEVELS = 30;
5611
5612 struct ValidationContext
5613 {
5614 size_t calculatedAllocationCount;
5615 size_t calculatedFreeCount;
5616 VkDeviceSize calculatedSumFreeSize;
5617
5618 ValidationContext() :
5619 calculatedAllocationCount(0),
5620 calculatedFreeCount(0),
5621 calculatedSumFreeSize(0) { }
5622 };
5623
5624 struct Node
5625 {
5626 VkDeviceSize offset;
5627 enum TYPE
5628 {
5629 TYPE_FREE,
5630 TYPE_ALLOCATION,
5631 TYPE_SPLIT,
5632 TYPE_COUNT
5633 } type;
5634 Node* parent;
5635 Node* buddy;
5636
5637 union
5638 {
5639 struct
5640 {
5641 Node* prev;
5642 Node* next;
5643 } free;
5644 struct
5645 {
5646 VmaAllocation alloc;
5647 } allocation;
5648 struct
5649 {
5650 Node* leftChild;
5651 } split;
5652 };
5653 };
5654
5655 // Size of the memory block aligned down to a power of two.
5656 VkDeviceSize m_UsableSize;
5657 uint32_t m_LevelCount;
5658
5659 Node* m_Root;
5660 struct {
5661 Node* front;
5662 Node* back;
5663 } m_FreeList[MAX_LEVELS];
5664 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5665 size_t m_AllocationCount;
5666 // Number of nodes in the tree with type == TYPE_FREE.
5667 size_t m_FreeCount;
5668 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5669 VkDeviceSize m_SumFreeSize;
5670
5671 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5672 void DeleteNode(Node* node);
5673 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5674 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
5675 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5676 // Alloc passed just for validation. Can be null.
5677 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5678 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5679 // Adds node to the front of FreeList at given level.
5680 // node->type must be FREE.
5681 // node->free.prev, next can be undefined.
5682 void AddToFreeListFront(uint32_t level, Node* node);
5683 // Removes node from FreeList at given level.
5684 // node->type must be FREE.
5685 // node->free.prev, next stay untouched.
5686 void RemoveFromFreeList(uint32_t level, Node* node);
5687
5688#if VMA_STATS_STRING_ENABLED
5689 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5690#endif
5691};
5692
5693/*
5694Represents a single block of device memory (`VkDeviceMemory`) with all the
5695data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5696
5697Thread-safety: This class must be externally synchronized.
5698*/
5699class VmaDeviceMemoryBlock
5700{
5701 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5702public:
5703 VmaBlockMetadata* m_pMetadata;
5704
5705 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5706
5707 ~VmaDeviceMemoryBlock()
5708 {
5709 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5710 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5711 }
5712
5713 // Always call after construction.
5714 void Init(
5715 VmaAllocator hAllocator,
5716 uint32_t newMemoryTypeIndex,
5717 VkDeviceMemory newMemory,
5718 VkDeviceSize newSize,
5719 uint32_t id,
5720 uint32_t algorithm);
5721 // Always call before destruction.
5722 void Destroy(VmaAllocator allocator);
5723
5724 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
5725 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5726 uint32_t GetId() const { return m_Id; }
5727 void* GetMappedData() const { return m_pMappedData; }
5728
5729 // Validates all data structures inside this object. If not valid, returns false.
5730 bool Validate() const;
5731
5732 VkResult CheckCorruption(VmaAllocator hAllocator);
5733
5734 // ppData can be null.
5735 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5736 void Unmap(VmaAllocator hAllocator, uint32_t count);
5737
5738 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5739 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5740
5741 VkResult BindBufferMemory(
5742 const VmaAllocator hAllocator,
5743 const VmaAllocation hAllocation,
5744 VkBuffer hBuffer);
5745 VkResult BindImageMemory(
5746 const VmaAllocator hAllocator,
5747 const VmaAllocation hAllocation,
5748 VkImage hImage);
5749
5750private:
5751 uint32_t m_MemoryTypeIndex;
5752 uint32_t m_Id;
5753 VkDeviceMemory m_hMemory;
5754
5755 /*
5756 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5757 Also protects m_MapCount, m_pMappedData.
5758 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5759 */
5760 VMA_MUTEX m_Mutex;
5761 uint32_t m_MapCount;
5762 void* m_pMappedData;
5763};
5764
5765struct VmaPointerLess
5766{
5767 bool operator()(const void* lhs, const void* rhs) const
5768 {
5769 return lhs < rhs;
5770 }
5771};
5772
5773struct VmaDefragmentationMove
5774{
5775 size_t srcBlockIndex;
5776 size_t dstBlockIndex;
5777 VkDeviceSize srcOffset;
5778 VkDeviceSize dstOffset;
5779 VkDeviceSize size;
5780};
5781
5782class VmaDefragmentationAlgorithm;
5783
5784/*
5785Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5786Vulkan memory type.
5787
5788Synchronized internally with a mutex.
5789*/
5790struct VmaBlockVector
5791{
5792 VMA_CLASS_NO_COPY(VmaBlockVector)
5793public:
5794 VmaBlockVector(
5795 VmaAllocator hAllocator,
5796 uint32_t memoryTypeIndex,
5797 VkDeviceSize preferredBlockSize,
5798 size_t minBlockCount,
5799 size_t maxBlockCount,
5800 VkDeviceSize bufferImageGranularity,
5801 uint32_t frameInUseCount,
5802 bool isCustomPool,
5803 bool explicitBlockSize,
5804 uint32_t algorithm);
5805 ~VmaBlockVector();
5806
5807 VkResult CreateMinBlocks();
5808
5809 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5810 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
5811 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
5812 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
5813 uint32_t GetAlgorithm() const { return m_Algorithm; }
5814
5815 void GetPoolStats(VmaPoolStats* pStats);
5816
5817 bool IsEmpty() const { return m_Blocks.empty(); }
5818 bool IsCorruptionDetectionEnabled() const;
5819
5820 VkResult Allocate(
5821 VmaPool hCurrentPool,
5822 uint32_t currentFrameIndex,
5823 VkDeviceSize size,
5824 VkDeviceSize alignment,
5825 const VmaAllocationCreateInfo& createInfo,
5826 VmaSuballocationType suballocType,
5827 size_t allocationCount,
5828 VmaAllocation* pAllocations);
5829
5830 void Free(
5831 VmaAllocation hAllocation);
5832
5833 // Adds statistics of this BlockVector to pStats.
5834 void AddStats(VmaStats* pStats);
5835
5836#if VMA_STATS_STRING_ENABLED
5837 void PrintDetailedMap(class VmaJsonWriter& json);
5838#endif
5839
5840 void MakePoolAllocationsLost(
5841 uint32_t currentFrameIndex,
5842 size_t* pLostAllocationCount);
5843 VkResult CheckCorruption();
5844
5845 // Saves results in pCtx->res.
5846 void Defragment(
5847 class VmaBlockVectorDefragmentationContext* pCtx,
5848 VmaDefragmentationStats* pStats,
5849 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5850 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5851 VkCommandBuffer commandBuffer);
5852 void DefragmentationEnd(
5853 class VmaBlockVectorDefragmentationContext* pCtx,
5854 VmaDefragmentationStats* pStats);
5855
5856 ////////////////////////////////////////////////////////////////////////////////
5857 // To be used only while the m_Mutex is locked. Used during defragmentation.
5858
5859 size_t GetBlockCount() const { return m_Blocks.size(); }
5860 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5861 size_t CalcAllocationCount() const;
5862 bool IsBufferImageGranularityConflictPossible() const;
5863
5864private:
5865 friend class VmaDefragmentationAlgorithm_Generic;
5866
5867 const VmaAllocator m_hAllocator;
5868 const uint32_t m_MemoryTypeIndex;
5869 const VkDeviceSize m_PreferredBlockSize;
5870 const size_t m_MinBlockCount;
5871 const size_t m_MaxBlockCount;
5872 const VkDeviceSize m_BufferImageGranularity;
5873 const uint32_t m_FrameInUseCount;
5874 const bool m_IsCustomPool;
5875 const bool m_ExplicitBlockSize;
5876 const uint32_t m_Algorithm;
5877 /* There can be at most one allocation that is completely empty - a
5878 hysteresis to avoid pessimistic case of alternating creation and destruction
5879 of a VkDeviceMemory. */
5880 bool m_HasEmptyBlock;
5881 VMA_RW_MUTEX m_Mutex;
5882 // Incrementally sorted by sumFreeSize, ascending.
5883 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5884 uint32_t m_NextBlockId;
5885
5886 VkDeviceSize CalcMaxBlockSize() const;
5887
5888 // Finds and removes given block from vector.
5889 void Remove(VmaDeviceMemoryBlock* pBlock);
5890
5891 // Performs single step in sorting m_Blocks. They may not be fully sorted
5892 // after this call.
5893 void IncrementallySortBlocks();
5894
5895 VkResult AllocatePage(
5896 VmaPool hCurrentPool,
5897 uint32_t currentFrameIndex,
5898 VkDeviceSize size,
5899 VkDeviceSize alignment,
5900 const VmaAllocationCreateInfo& createInfo,
5901 VmaSuballocationType suballocType,
5902 VmaAllocation* pAllocation);
5903
5904 // To be used only without CAN_MAKE_OTHER_LOST flag.
5905 VkResult AllocateFromBlock(
5906 VmaDeviceMemoryBlock* pBlock,
5907 VmaPool hCurrentPool,
5908 uint32_t currentFrameIndex,
5909 VkDeviceSize size,
5910 VkDeviceSize alignment,
5911 VmaAllocationCreateFlags allocFlags,
5912 void* pUserData,
5913 VmaSuballocationType suballocType,
5914 uint32_t strategy,
5915 VmaAllocation* pAllocation);
5916
5917 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5918
5919 // Saves result to pCtx->res.
5920 void ApplyDefragmentationMovesCpu(
5921 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5922 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5923 // Saves result to pCtx->res.
5924 void ApplyDefragmentationMovesGpu(
5925 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5926 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5927 VkCommandBuffer commandBuffer);
5928
5929 /*
5930 Used during defragmentation. pDefragmentationStats is optional. It's in/out
5931 - updated with new data.
5932 */
5933 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5934};
5935
5936struct VmaPool_T
5937{
5938 VMA_CLASS_NO_COPY(VmaPool_T)
5939public:
5940 VmaBlockVector m_BlockVector;
5941
5942 VmaPool_T(
5943 VmaAllocator hAllocator,
5944 const VmaPoolCreateInfo& createInfo,
5945 VkDeviceSize preferredBlockSize);
5946 ~VmaPool_T();
5947
5948 uint32_t GetId() const { return m_Id; }
5949 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5950
5951#if VMA_STATS_STRING_ENABLED
5952 //void PrintDetailedMap(class VmaStringBuilder& sb);
5953#endif
5954
5955private:
5956 uint32_t m_Id;
5957};
5958
5959/*
5960Performs defragmentation:
5961
5962- Updates `pBlockVector->m_pMetadata`.
5963- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5964- Does not move actual data, only returns requested moves as `moves`.
5965*/
5966class VmaDefragmentationAlgorithm
5967{
5968 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5969public:
5970 VmaDefragmentationAlgorithm(
5971 VmaAllocator hAllocator,
5972 VmaBlockVector* pBlockVector,
5973 uint32_t currentFrameIndex) :
5974 m_hAllocator(hAllocator),
5975 m_pBlockVector(pBlockVector),
5976 m_CurrentFrameIndex(currentFrameIndex)
5977 {
5978 }
5979 virtual ~VmaDefragmentationAlgorithm()
5980 {
5981 }
5982
5983 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5984 virtual void AddAll() = 0;
5985
5986 virtual VkResult Defragment(
5987 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5988 VkDeviceSize maxBytesToMove,
5989 uint32_t maxAllocationsToMove) = 0;
5990
5991 virtual VkDeviceSize GetBytesMoved() const = 0;
5992 virtual uint32_t GetAllocationsMoved() const = 0;
5993
5994protected:
5995 VmaAllocator const m_hAllocator;
5996 VmaBlockVector* const m_pBlockVector;
5997 const uint32_t m_CurrentFrameIndex;
5998
5999 struct AllocationInfo
6000 {
6001 VmaAllocation m_hAllocation;
6002 VkBool32* m_pChanged;
6003
6004 AllocationInfo() :
6005 m_hAllocation(VK_NULL_HANDLE),
6006 m_pChanged(VMA_NULL)
6007 {
6008 }
6009 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6010 m_hAllocation(hAlloc),
6011 m_pChanged(pChanged)
6012 {
6013 }
6014 };
6015};
6016
6017class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6018{
6019 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6020public:
6021 VmaDefragmentationAlgorithm_Generic(
6022 VmaAllocator hAllocator,
6023 VmaBlockVector* pBlockVector,
6024 uint32_t currentFrameIndex,
6025 bool overlappingMoveSupported);
6026 virtual ~VmaDefragmentationAlgorithm_Generic();
6027
6028 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6029 virtual void AddAll() { m_AllAllocations = true; }
6030
6031 virtual VkResult Defragment(
6032 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6033 VkDeviceSize maxBytesToMove,
6034 uint32_t maxAllocationsToMove);
6035
6036 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6037 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6038
6039private:
6040 uint32_t m_AllocationCount;
6041 bool m_AllAllocations;
6042
6043 VkDeviceSize m_BytesMoved;
6044 uint32_t m_AllocationsMoved;
6045
6046 struct AllocationInfoSizeGreater
6047 {
6048 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6049 {
6050 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6051 }
6052 };
6053
6054 struct AllocationInfoOffsetGreater
6055 {
6056 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6057 {
6058 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6059 }
6060 };
6061
6062 struct BlockInfo
6063 {
6064 size_t m_OriginalBlockIndex;
6065 VmaDeviceMemoryBlock* m_pBlock;
6066 bool m_HasNonMovableAllocations;
6067 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6068
6069 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6070 m_OriginalBlockIndex(SIZE_MAX),
6071 m_pBlock(VMA_NULL),
6072 m_HasNonMovableAllocations(true),
6073 m_Allocations(pAllocationCallbacks)
6074 {
6075 }
6076
6077 void CalcHasNonMovableAllocations()
6078 {
6079 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6080 const size_t defragmentAllocCount = m_Allocations.size();
6081 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6082 }
6083
6084 void SortAllocationsBySizeDescending()
6085 {
6086 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6087 }
6088
6089 void SortAllocationsByOffsetDescending()
6090 {
6091 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6092 }
6093 };
6094
6095 struct BlockPointerLess
6096 {
6097 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6098 {
6099 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6100 }
6101 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6102 {
6103 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6104 }
6105 };
6106
6107 // 1. Blocks with some non-movable allocations go first.
6108 // 2. Blocks with smaller sumFreeSize go first.
6109 struct BlockInfoCompareMoveDestination
6110 {
6111 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6112 {
6113 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6114 {
6115 return true;
6116 }
6117 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6118 {
6119 return false;
6120 }
6121 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6122 {
6123 return true;
6124 }
6125 return false;
6126 }
6127 };
6128
6129 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6130 BlockInfoVector m_Blocks;
6131
6132 VkResult DefragmentRound(
6133 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6134 VkDeviceSize maxBytesToMove,
6135 uint32_t maxAllocationsToMove);
6136
6137 size_t CalcBlocksWithNonMovableCount() const;
6138
6139 static bool MoveMakesSense(
6140 size_t dstBlockIndex, VkDeviceSize dstOffset,
6141 size_t srcBlockIndex, VkDeviceSize srcOffset);
6142};
6143
6144class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6145{
6146 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6147public:
6148 VmaDefragmentationAlgorithm_Fast(
6149 VmaAllocator hAllocator,
6150 VmaBlockVector* pBlockVector,
6151 uint32_t currentFrameIndex,
6152 bool overlappingMoveSupported);
6153 virtual ~VmaDefragmentationAlgorithm_Fast();
6154
6155 virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; }
6156 virtual void AddAll() { m_AllAllocations = true; }
6157
6158 virtual VkResult Defragment(
6159 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6160 VkDeviceSize maxBytesToMove,
6161 uint32_t maxAllocationsToMove);
6162
6163 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6164 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6165
6166private:
6167 struct BlockInfo
6168 {
6169 size_t origBlockIndex;
6170 };
6171
6172 class FreeSpaceDatabase
6173 {
6174 public:
6175 FreeSpaceDatabase()
6176 {
6177 FreeSpace s = {};
6178 s.blockInfoIndex = SIZE_MAX;
6179 for(size_t i = 0; i < MAX_COUNT; ++i)
6180 {
6181 m_FreeSpaces[i] = s;
6182 }
6183 }
6184
6185 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6186 {
6187 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6188 {
6189 return;
6190 }
6191
6192 // Find first invalid or the smallest structure.
6193 size_t bestIndex = SIZE_MAX;
6194 for(size_t i = 0; i < MAX_COUNT; ++i)
6195 {
6196 // Empty structure.
6197 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6198 {
6199 bestIndex = i;
6200 break;
6201 }
6202 if(m_FreeSpaces[i].size < size &&
6203 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6204 {
6205 bestIndex = i;
6206 }
6207 }
6208
6209 if(bestIndex != SIZE_MAX)
6210 {
6211 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6212 m_FreeSpaces[bestIndex].offset = offset;
6213 m_FreeSpaces[bestIndex].size = size;
6214 }
6215 }
6216
6217 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6218 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6219 {
6220 size_t bestIndex = SIZE_MAX;
6221 VkDeviceSize bestFreeSpaceAfter = 0;
6222 for(size_t i = 0; i < MAX_COUNT; ++i)
6223 {
6224 // Structure is valid.
6225 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6226 {
6227 const VkDeviceSize dstOffset = VmaAlignUp(val: m_FreeSpaces[i].offset, align: alignment);
6228 // Allocation fits into this structure.
6229 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6230 {
6231 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6232 (dstOffset + size);
6233 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6234 {
6235 bestIndex = i;
6236 bestFreeSpaceAfter = freeSpaceAfter;
6237 }
6238 }
6239 }
6240 }
6241
6242 if(bestIndex != SIZE_MAX)
6243 {
6244 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6245 outDstOffset = VmaAlignUp(val: m_FreeSpaces[bestIndex].offset, align: alignment);
6246
6247 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6248 {
6249 // Leave this structure for remaining empty space.
6250 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6251 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6252 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6253 }
6254 else
6255 {
6256 // This structure becomes invalid.
6257 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6258 }
6259
6260 return true;
6261 }
6262
6263 return false;
6264 }
6265
6266 private:
6267 static const size_t MAX_COUNT = 4;
6268
6269 struct FreeSpace
6270 {
6271 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6272 VkDeviceSize offset;
6273 VkDeviceSize size;
6274 } m_FreeSpaces[MAX_COUNT];
6275 };
6276
6277 const bool m_OverlappingMoveSupported;
6278
6279 uint32_t m_AllocationCount;
6280 bool m_AllAllocations;
6281
6282 VkDeviceSize m_BytesMoved;
6283 uint32_t m_AllocationsMoved;
6284
6285 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6286
6287 void PreprocessMetadata();
6288 void PostprocessMetadata();
6289 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6290};
6291
6292struct VmaBlockDefragmentationContext
6293{
6294 enum BLOCK_FLAG
6295 {
6296 BLOCK_FLAG_USED = 0x00000001,
6297 };
6298 uint32_t flags;
6299 VkBuffer hBuffer;
6300
6301 VmaBlockDefragmentationContext() :
6302 flags(0),
6303 hBuffer(VK_NULL_HANDLE)
6304 {
6305 }
6306};
6307
6308class VmaBlockVectorDefragmentationContext
6309{
6310 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6311public:
6312 VkResult res;
6313 bool mutexLocked;
6314 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6315
6316 VmaBlockVectorDefragmentationContext(
6317 VmaAllocator hAllocator,
6318 VmaPool hCustomPool, // Optional.
6319 VmaBlockVector* pBlockVector,
6320 uint32_t currFrameIndex,
6321 uint32_t flags);
6322 ~VmaBlockVectorDefragmentationContext();
6323
6324 VmaPool GetCustomPool() const { return m_hCustomPool; }
6325 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
6326 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6327
6328 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6329 void AddAll() { m_AllAllocations = true; }
6330
6331 void Begin(bool overlappingMoveSupported);
6332
6333private:
6334 const VmaAllocator m_hAllocator;
6335 // Null if not from custom pool.
6336 const VmaPool m_hCustomPool;
6337 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6338 VmaBlockVector* const m_pBlockVector;
6339 const uint32_t m_CurrFrameIndex;
6340 /*const uint32_t m_AlgorithmFlags;*/
6341 // Owner of this object.
6342 VmaDefragmentationAlgorithm* m_pAlgorithm;
6343
6344 struct AllocInfo
6345 {
6346 VmaAllocation hAlloc;
6347 VkBool32* pChanged;
6348 };
6349 // Used between constructor and Begin.
6350 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6351 bool m_AllAllocations;
6352};
6353
6354struct VmaDefragmentationContext_T
6355{
6356private:
6357 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6358public:
6359 VmaDefragmentationContext_T(
6360 VmaAllocator hAllocator,
6361 uint32_t currFrameIndex,
6362 uint32_t flags,
6363 VmaDefragmentationStats* pStats);
6364 ~VmaDefragmentationContext_T();
6365
6366 void AddPools(uint32_t poolCount, VmaPool* pPools);
6367 void AddAllocations(
6368 uint32_t allocationCount,
6369 VmaAllocation* pAllocations,
6370 VkBool32* pAllocationsChanged);
6371
6372 /*
6373 Returns:
6374 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6375 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6376 - Negative value if error occured and object can be destroyed immediately.
6377 */
6378 VkResult Defragment(
6379 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6380 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6381 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6382
6383private:
6384 const VmaAllocator m_hAllocator;
6385 const uint32_t m_CurrFrameIndex;
6386 const uint32_t m_Flags;
6387 VmaDefragmentationStats* const m_pStats;
6388 // Owner of these objects.
6389 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6390 // Owner of these objects.
6391 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6392};
6393
6394#if VMA_RECORDING_ENABLED
6395
6396class VmaRecorder
6397{
6398public:
6399 VmaRecorder();
6400 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6401 void WriteConfiguration(
6402 const VkPhysicalDeviceProperties& devProps,
6403 const VkPhysicalDeviceMemoryProperties& memProps,
6404 bool dedicatedAllocationExtensionEnabled);
6405 ~VmaRecorder();
6406
6407 void RecordCreateAllocator(uint32_t frameIndex);
6408 void RecordDestroyAllocator(uint32_t frameIndex);
6409 void RecordCreatePool(uint32_t frameIndex,
6410 const VmaPoolCreateInfo& createInfo,
6411 VmaPool pool);
6412 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6413 void RecordAllocateMemory(uint32_t frameIndex,
6414 const VkMemoryRequirements& vkMemReq,
6415 const VmaAllocationCreateInfo& createInfo,
6416 VmaAllocation allocation);
6417 void RecordAllocateMemoryPages(uint32_t frameIndex,
6418 const VkMemoryRequirements& vkMemReq,
6419 const VmaAllocationCreateInfo& createInfo,
6420 uint64_t allocationCount,
6421 const VmaAllocation* pAllocations);
6422 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6423 const VkMemoryRequirements& vkMemReq,
6424 bool requiresDedicatedAllocation,
6425 bool prefersDedicatedAllocation,
6426 const VmaAllocationCreateInfo& createInfo,
6427 VmaAllocation allocation);
6428 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6429 const VkMemoryRequirements& vkMemReq,
6430 bool requiresDedicatedAllocation,
6431 bool prefersDedicatedAllocation,
6432 const VmaAllocationCreateInfo& createInfo,
6433 VmaAllocation allocation);
6434 void RecordFreeMemory(uint32_t frameIndex,
6435 VmaAllocation allocation);
6436 void RecordFreeMemoryPages(uint32_t frameIndex,
6437 uint64_t allocationCount,
6438 const VmaAllocation* pAllocations);
6439 void RecordResizeAllocation(
6440 uint32_t frameIndex,
6441 VmaAllocation allocation,
6442 VkDeviceSize newSize);
6443 void RecordSetAllocationUserData(uint32_t frameIndex,
6444 VmaAllocation allocation,
6445 const void* pUserData);
6446 void RecordCreateLostAllocation(uint32_t frameIndex,
6447 VmaAllocation allocation);
6448 void RecordMapMemory(uint32_t frameIndex,
6449 VmaAllocation allocation);
6450 void RecordUnmapMemory(uint32_t frameIndex,
6451 VmaAllocation allocation);
6452 void RecordFlushAllocation(uint32_t frameIndex,
6453 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6454 void RecordInvalidateAllocation(uint32_t frameIndex,
6455 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6456 void RecordCreateBuffer(uint32_t frameIndex,
6457 const VkBufferCreateInfo& bufCreateInfo,
6458 const VmaAllocationCreateInfo& allocCreateInfo,
6459 VmaAllocation allocation);
6460 void RecordCreateImage(uint32_t frameIndex,
6461 const VkImageCreateInfo& imageCreateInfo,
6462 const VmaAllocationCreateInfo& allocCreateInfo,
6463 VmaAllocation allocation);
6464 void RecordDestroyBuffer(uint32_t frameIndex,
6465 VmaAllocation allocation);
6466 void RecordDestroyImage(uint32_t frameIndex,
6467 VmaAllocation allocation);
6468 void RecordTouchAllocation(uint32_t frameIndex,
6469 VmaAllocation allocation);
6470 void RecordGetAllocationInfo(uint32_t frameIndex,
6471 VmaAllocation allocation);
6472 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6473 VmaPool pool);
6474 void RecordDefragmentationBegin(uint32_t frameIndex,
6475 const VmaDefragmentationInfo2& info,
6476 VmaDefragmentationContext ctx);
6477 void RecordDefragmentationEnd(uint32_t frameIndex,
6478 VmaDefragmentationContext ctx);
6479
6480private:
6481 struct CallParams
6482 {
6483 uint32_t threadId;
6484 double time;
6485 };
6486
6487 class UserDataString
6488 {
6489 public:
6490 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
6491 const char* GetString() const { return m_Str; }
6492
6493 private:
6494 char m_PtrStr[17];
6495 const char* m_Str;
6496 };
6497
6498 bool m_UseMutex;
6499 VmaRecordFlags m_Flags;
6500 FILE* m_File;
6501 VMA_MUTEX m_FileMutex;
6502 int64_t m_Freq;
6503 int64_t m_StartCounter;
6504
6505 void GetBasicParams(CallParams& outParams);
6506
6507 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6508 template<typename T>
6509 void PrintPointerList(uint64_t count, const T* pItems)
6510 {
6511 if(count)
6512 {
6513 fprintf(m_File, "%p", pItems[0]);
6514 for(uint64_t i = 1; i < count; ++i)
6515 {
6516 fprintf(m_File, " %p", pItems[i]);
6517 }
6518 }
6519 }
6520
6521 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6522 void Flush();
6523};
6524
6525#endif // #if VMA_RECORDING_ENABLED
6526
6527// Main allocator object.
6528struct VmaAllocator_T
6529{
6530 VMA_CLASS_NO_COPY(VmaAllocator_T)
6531public:
6532 bool m_UseMutex;
6533 bool m_UseKhrDedicatedAllocation;
6534 VkDevice m_hDevice;
6535 bool m_AllocationCallbacksSpecified;
6536 VkAllocationCallbacks m_AllocationCallbacks;
6537 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6538
6539 // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6540 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6541 VMA_MUTEX m_HeapSizeLimitMutex;
6542
6543 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6544 VkPhysicalDeviceMemoryProperties m_MemProps;
6545
6546 // Default pools.
6547 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6548
6549 // Each vector is sorted by memory (handle value).
6550 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6551 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6552 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6553
6554 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6555 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6556 ~VmaAllocator_T();
6557
6558 const VkAllocationCallbacks* GetAllocationCallbacks() const
6559 {
6560 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6561 }
6562 const VmaVulkanFunctions& GetVulkanFunctions() const
6563 {
6564 return m_VulkanFunctions;
6565 }
6566
6567 VkDeviceSize GetBufferImageGranularity() const
6568 {
6569 return VMA_MAX(
6570 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6571 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6572 }
6573
6574 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
6575 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6576
6577 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6578 {
6579 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6580 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6581 }
6582 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
6583 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6584 {
6585 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6586 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6587 }
6588 // Minimum alignment for all allocations in specific memory type.
6589 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6590 {
6591 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6592 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6593 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6594 }
6595
6596 bool IsIntegratedGpu() const
6597 {
6598 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6599 }
6600
6601#if VMA_RECORDING_ENABLED
6602 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6603#endif
6604
6605 void GetBufferMemoryRequirements(
6606 VkBuffer hBuffer,
6607 VkMemoryRequirements& memReq,
6608 bool& requiresDedicatedAllocation,
6609 bool& prefersDedicatedAllocation) const;
6610 void GetImageMemoryRequirements(
6611 VkImage hImage,
6612 VkMemoryRequirements& memReq,
6613 bool& requiresDedicatedAllocation,
6614 bool& prefersDedicatedAllocation) const;
6615
6616 // Main allocation function.
6617 VkResult AllocateMemory(
6618 const VkMemoryRequirements& vkMemReq,
6619 bool requiresDedicatedAllocation,
6620 bool prefersDedicatedAllocation,
6621 VkBuffer dedicatedBuffer,
6622 VkImage dedicatedImage,
6623 const VmaAllocationCreateInfo& createInfo,
6624 VmaSuballocationType suballocType,
6625 size_t allocationCount,
6626 VmaAllocation* pAllocations);
6627
6628 // Main deallocation function.
6629 void FreeMemory(
6630 size_t allocationCount,
6631 const VmaAllocation* pAllocations);
6632
6633 VkResult ResizeAllocation(
6634 const VmaAllocation alloc,
6635 VkDeviceSize newSize);
6636
6637 void CalculateStats(VmaStats* pStats);
6638
6639#if VMA_STATS_STRING_ENABLED
6640 void PrintDetailedMap(class VmaJsonWriter& json);
6641#endif
6642
6643 VkResult DefragmentationBegin(
6644 const VmaDefragmentationInfo2& info,
6645 VmaDefragmentationStats* pStats,
6646 VmaDefragmentationContext* pContext);
6647 VkResult DefragmentationEnd(
6648 VmaDefragmentationContext context);
6649
6650 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6651 bool TouchAllocation(VmaAllocation hAllocation);
6652
6653 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6654 void DestroyPool(VmaPool pool);
6655 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6656
6657 void SetCurrentFrameIndex(uint32_t frameIndex);
6658 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6659
6660 void MakePoolAllocationsLost(
6661 VmaPool hPool,
6662 size_t* pLostAllocationCount);
6663 VkResult CheckPoolCorruption(VmaPool hPool);
6664 VkResult CheckCorruption(uint32_t memoryTypeBits);
6665
6666 void CreateLostAllocation(VmaAllocation* pAllocation);
6667
6668 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6669 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6670
6671 VkResult Map(VmaAllocation hAllocation, void** ppData);
6672 void Unmap(VmaAllocation hAllocation);
6673
6674 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6675 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6676
6677 void FlushOrInvalidateAllocation(
6678 VmaAllocation hAllocation,
6679 VkDeviceSize offset, VkDeviceSize size,
6680 VMA_CACHE_OPERATION op);
6681
6682 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6683
6684private:
6685 VkDeviceSize m_PreferredLargeHeapBlockSize;
6686
6687 VkPhysicalDevice m_PhysicalDevice;
6688 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6689
6690 VMA_RW_MUTEX m_PoolsMutex;
6691 // Protected by m_PoolsMutex. Sorted by pointer value.
6692 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6693 uint32_t m_NextPoolId;
6694
6695 VmaVulkanFunctions m_VulkanFunctions;
6696
6697#if VMA_RECORDING_ENABLED
6698 VmaRecorder* m_pRecorder;
6699#endif
6700
6701 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6702
6703 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6704
6705 VkResult AllocateMemoryOfType(
6706 VkDeviceSize size,
6707 VkDeviceSize alignment,
6708 bool dedicatedAllocation,
6709 VkBuffer dedicatedBuffer,
6710 VkImage dedicatedImage,
6711 const VmaAllocationCreateInfo& createInfo,
6712 uint32_t memTypeIndex,
6713 VmaSuballocationType suballocType,
6714 size_t allocationCount,
6715 VmaAllocation* pAllocations);
6716
6717 // Helper function only to be used inside AllocateDedicatedMemory.
6718 VkResult AllocateDedicatedMemoryPage(
6719 VkDeviceSize size,
6720 VmaSuballocationType suballocType,
6721 uint32_t memTypeIndex,
6722 const VkMemoryAllocateInfo& allocInfo,
6723 bool map,
6724 bool isUserDataString,
6725 void* pUserData,
6726 VmaAllocation* pAllocation);
6727
6728 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6729 VkResult AllocateDedicatedMemory(
6730 VkDeviceSize size,
6731 VmaSuballocationType suballocType,
6732 uint32_t memTypeIndex,
6733 bool map,
6734 bool isUserDataString,
6735 void* pUserData,
6736 VkBuffer dedicatedBuffer,
6737 VkImage dedicatedImage,
6738 size_t allocationCount,
6739 VmaAllocation* pAllocations);
6740
6741 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6742 void FreeDedicatedMemory(VmaAllocation allocation);
6743};
6744
6745////////////////////////////////////////////////////////////////////////////////
6746// Memory allocation #2 after VmaAllocator_T definition
6747
6748static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6749{
6750 return VmaMalloc(pAllocationCallbacks: &hAllocator->m_AllocationCallbacks, size, alignment);
6751}
6752
6753static void VmaFree(VmaAllocator hAllocator, void* ptr)
6754{
6755 VmaFree(pAllocationCallbacks: &hAllocator->m_AllocationCallbacks, ptr);
6756}
6757
6758template<typename T>
6759static T* VmaAllocate(VmaAllocator hAllocator)
6760{
6761 return (T*)VmaMalloc(hAllocator, size: sizeof(T), VMA_ALIGN_OF(T));
6762}
6763
6764template<typename T>
6765static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6766{
6767 return (T*)VmaMalloc(hAllocator, size: sizeof(T) * count, VMA_ALIGN_OF(T));
6768}
6769
6770template<typename T>
6771static void vma_delete(VmaAllocator hAllocator, T* ptr)
6772{
6773 if(ptr != VMA_NULL)
6774 {
6775 ptr->~T();
6776 VmaFree(hAllocator, ptr);
6777 }
6778}
6779
6780template<typename T>
6781static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6782{
6783 if(ptr != VMA_NULL)
6784 {
6785 for(size_t i = count; i--; )
6786 ptr[i].~T();
6787 VmaFree(hAllocator, ptr);
6788 }
6789}
6790
6791////////////////////////////////////////////////////////////////////////////////
6792// VmaStringBuilder
6793
6794#if VMA_STATS_STRING_ENABLED
6795
6796class VmaStringBuilder
6797{
6798public:
6799 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
6800 size_t GetLength() const { return m_Data.size(); }
6801 const char* GetData() const { return m_Data.data(); }
6802
6803 void Add(char ch) { m_Data.push_back(src: ch); }
6804 void Add(const char* pStr);
6805 void AddNewLine() { Add(ch: '\n'); }
6806 void AddNumber(uint32_t num);
6807 void AddNumber(uint64_t num);
6808 void AddPointer(const void* ptr);
6809
6810private:
6811 VmaVector< char, VmaStlAllocator<char> > m_Data;
6812};
6813
6814void VmaStringBuilder::Add(const char* pStr)
6815{
6816 const size_t strLen = strlen(s: pStr);
6817 if(strLen > 0)
6818 {
6819 const size_t oldCount = m_Data.size();
6820 m_Data.resize(newCount: oldCount + strLen);
6821 memcpy(dest: m_Data.data() + oldCount, src: pStr, n: strLen);
6822 }
6823}
6824
6825void VmaStringBuilder::AddNumber(uint32_t num)
6826{
6827 char buf[11];
6828 VmaUint32ToStr(outStr: buf, strLen: sizeof(buf), num);
6829 Add(pStr: buf);
6830}
6831
6832void VmaStringBuilder::AddNumber(uint64_t num)
6833{
6834 char buf[21];
6835 VmaUint64ToStr(outStr: buf, strLen: sizeof(buf), num);
6836 Add(pStr: buf);
6837}
6838
6839void VmaStringBuilder::AddPointer(const void* ptr)
6840{
6841 char buf[21];
6842 VmaPtrToStr(outStr: buf, strLen: sizeof(buf), ptr);
6843 Add(pStr: buf);
6844}
6845
6846#endif // #if VMA_STATS_STRING_ENABLED
6847
6848////////////////////////////////////////////////////////////////////////////////
6849// VmaJsonWriter
6850
6851#if VMA_STATS_STRING_ENABLED
6852
6853class VmaJsonWriter
6854{
6855 VMA_CLASS_NO_COPY(VmaJsonWriter)
6856public:
6857 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6858 ~VmaJsonWriter();
6859
6860 void BeginObject(bool singleLine = false);
6861 void EndObject();
6862
6863 void BeginArray(bool singleLine = false);
6864 void EndArray();
6865
6866 void WriteString(const char* pStr);
6867 void BeginString(const char* pStr = VMA_NULL);
6868 void ContinueString(const char* pStr);
6869 void ContinueString(uint32_t n);
6870 void ContinueString(uint64_t n);
6871 void ContinueString_Pointer(const void* ptr);
6872 void EndString(const char* pStr = VMA_NULL);
6873
6874 void WriteNumber(uint32_t n);
6875 void WriteNumber(uint64_t n);
6876 void WriteBool(bool b);
6877 void WriteNull();
6878
6879private:
6880 static const char* const INDENT;
6881
6882 enum COLLECTION_TYPE
6883 {
6884 COLLECTION_TYPE_OBJECT,
6885 COLLECTION_TYPE_ARRAY,
6886 };
6887 struct StackItem
6888 {
6889 COLLECTION_TYPE type;
6890 uint32_t valueCount;
6891 bool singleLineMode;
6892 };
6893
6894 VmaStringBuilder& m_SB;
6895 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6896 bool m_InsideString;
6897
6898 void BeginValue(bool isString);
6899 void WriteIndent(bool oneLess = false);
6900};
6901
6902const char* const VmaJsonWriter::INDENT = " ";
6903
6904VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6905 m_SB(sb),
6906 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6907 m_InsideString(false)
6908{
6909}
6910
6911VmaJsonWriter::~VmaJsonWriter()
6912{
6913 VMA_ASSERT(!m_InsideString);
6914 VMA_ASSERT(m_Stack.empty());
6915}
6916
6917void VmaJsonWriter::BeginObject(bool singleLine)
6918{
6919 VMA_ASSERT(!m_InsideString);
6920
6921 BeginValue(isString: false);
6922 m_SB.Add(ch: '{');
6923
6924 StackItem item;
6925 item.type = COLLECTION_TYPE_OBJECT;
6926 item.valueCount = 0;
6927 item.singleLineMode = singleLine;
6928 m_Stack.push_back(src: item);
6929}
6930
6931void VmaJsonWriter::EndObject()
6932{
6933 VMA_ASSERT(!m_InsideString);
6934
6935 WriteIndent(oneLess: true);
6936 m_SB.Add(ch: '}');
6937
6938 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6939 m_Stack.pop_back();
6940}
6941
6942void VmaJsonWriter::BeginArray(bool singleLine)
6943{
6944 VMA_ASSERT(!m_InsideString);
6945
6946 BeginValue(isString: false);
6947 m_SB.Add(ch: '[');
6948
6949 StackItem item;
6950 item.type = COLLECTION_TYPE_ARRAY;
6951 item.valueCount = 0;
6952 item.singleLineMode = singleLine;
6953 m_Stack.push_back(src: item);
6954}
6955
6956void VmaJsonWriter::EndArray()
6957{
6958 VMA_ASSERT(!m_InsideString);
6959
6960 WriteIndent(oneLess: true);
6961 m_SB.Add(ch: ']');
6962
6963 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6964 m_Stack.pop_back();
6965}
6966
6967void VmaJsonWriter::WriteString(const char* pStr)
6968{
6969 BeginString(pStr);
6970 EndString();
6971}
6972
6973void VmaJsonWriter::BeginString(const char* pStr)
6974{
6975 VMA_ASSERT(!m_InsideString);
6976
6977 BeginValue(isString: true);
6978 m_SB.Add(ch: '"');
6979 m_InsideString = true;
6980 if(pStr != VMA_NULL && pStr[0] != '\0')
6981 {
6982 ContinueString(pStr);
6983 }
6984}
6985
6986void VmaJsonWriter::ContinueString(const char* pStr)
6987{
6988 VMA_ASSERT(m_InsideString);
6989
6990 const size_t strLen = strlen(s: pStr);
6991 for(size_t i = 0; i < strLen; ++i)
6992 {
6993 char ch = pStr[i];
6994 if(ch == '\\')
6995 {
6996 m_SB.Add(pStr: "\\\\");
6997 }
6998 else if(ch == '"')
6999 {
7000 m_SB.Add(pStr: "\\\"");
7001 }
7002 else if(ch >= 32)
7003 {
7004 m_SB.Add(ch);
7005 }
7006 else switch(ch)
7007 {
7008 case '\b':
7009 m_SB.Add(pStr: "\\b");
7010 break;
7011 case '\f':
7012 m_SB.Add(pStr: "\\f");
7013 break;
7014 case '\n':
7015 m_SB.Add(pStr: "\\n");
7016 break;
7017 case '\r':
7018 m_SB.Add(pStr: "\\r");
7019 break;
7020 case '\t':
7021 m_SB.Add(pStr: "\\t");
7022 break;
7023 default:
7024 VMA_ASSERT(0 && "Character not currently supported.");
7025 break;
7026 }
7027 }
7028}
7029
7030void VmaJsonWriter::ContinueString(uint32_t n)
7031{
7032 VMA_ASSERT(m_InsideString);
7033 m_SB.AddNumber(num: n);
7034}
7035
7036void VmaJsonWriter::ContinueString(uint64_t n)
7037{
7038 VMA_ASSERT(m_InsideString);
7039 m_SB.AddNumber(num: n);
7040}
7041
7042void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7043{
7044 VMA_ASSERT(m_InsideString);
7045 m_SB.AddPointer(ptr);
7046}
7047
7048void VmaJsonWriter::EndString(const char* pStr)
7049{
7050 VMA_ASSERT(m_InsideString);
7051 if(pStr != VMA_NULL && pStr[0] != '\0')
7052 {
7053 ContinueString(pStr);
7054 }
7055 m_SB.Add(ch: '"');
7056 m_InsideString = false;
7057}
7058
7059void VmaJsonWriter::WriteNumber(uint32_t n)
7060{
7061 VMA_ASSERT(!m_InsideString);
7062 BeginValue(isString: false);
7063 m_SB.AddNumber(num: n);
7064}
7065
7066void VmaJsonWriter::WriteNumber(uint64_t n)
7067{
7068 VMA_ASSERT(!m_InsideString);
7069 BeginValue(isString: false);
7070 m_SB.AddNumber(num: n);
7071}
7072
7073void VmaJsonWriter::WriteBool(bool b)
7074{
7075 VMA_ASSERT(!m_InsideString);
7076 BeginValue(isString: false);
7077 m_SB.Add(pStr: b ? "true" : "false");
7078}
7079
7080void VmaJsonWriter::WriteNull()
7081{
7082 VMA_ASSERT(!m_InsideString);
7083 BeginValue(isString: false);
7084 m_SB.Add(pStr: "null");
7085}
7086
7087void VmaJsonWriter::BeginValue(bool isString)
7088{
7089 if(!m_Stack.empty())
7090 {
7091 StackItem& currItem = m_Stack.back();
7092 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7093 currItem.valueCount % 2 == 0)
7094 {
7095 (void) isString;
7096 VMA_ASSERT(isString);
7097 }
7098
7099 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7100 currItem.valueCount % 2 != 0)
7101 {
7102 m_SB.Add(pStr: ": ");
7103 }
7104 else if(currItem.valueCount > 0)
7105 {
7106 m_SB.Add(pStr: ", ");
7107 WriteIndent();
7108 }
7109 else
7110 {
7111 WriteIndent();
7112 }
7113 ++currItem.valueCount;
7114 }
7115}
7116
7117void VmaJsonWriter::WriteIndent(bool oneLess)
7118{
7119 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7120 {
7121 m_SB.AddNewLine();
7122
7123 size_t count = m_Stack.size();
7124 if(count > 0 && oneLess)
7125 {
7126 --count;
7127 }
7128 for(size_t i = 0; i < count; ++i)
7129 {
7130 m_SB.Add(pStr: INDENT);
7131 }
7132 }
7133}
7134
7135#endif // #if VMA_STATS_STRING_ENABLED
7136
7137////////////////////////////////////////////////////////////////////////////////
7138
7139void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7140{
7141 if(IsUserDataString())
7142 {
7143 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7144
7145 FreeUserDataString(hAllocator);
7146
7147 if(pUserData != VMA_NULL)
7148 {
7149 const char* const newStrSrc = (char*)pUserData;
7150 const size_t newStrLen = strlen(s: newStrSrc);
7151 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7152 memcpy(dest: newStrDst, src: newStrSrc, n: newStrLen + 1);
7153 m_pUserData = newStrDst;
7154 }
7155 }
7156 else
7157 {
7158 m_pUserData = pUserData;
7159 }
7160}
7161
7162void VmaAllocation_T::ChangeBlockAllocation(
7163 VmaAllocator hAllocator,
7164 VmaDeviceMemoryBlock* block,
7165 VkDeviceSize offset)
7166{
7167 VMA_ASSERT(block != VMA_NULL);
7168 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7169
7170 // Move mapping reference counter from old block to new block.
7171 if(block != m_BlockAllocation.m_Block)
7172 {
7173 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7174 if(IsPersistentMap())
7175 ++mapRefCount;
7176 m_BlockAllocation.m_Block->Unmap(hAllocator, count: mapRefCount);
7177 block->Map(hAllocator, count: mapRefCount, VMA_NULL);
7178 }
7179
7180 m_BlockAllocation.m_Block = block;
7181 m_BlockAllocation.m_Offset = offset;
7182}
7183
7184void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7185{
7186 VMA_ASSERT(newSize > 0);
7187 m_Size = newSize;
7188}
7189
7190void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7191{
7192 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7193 m_BlockAllocation.m_Offset = newOffset;
7194}
7195
7196VkDeviceSize VmaAllocation_T::GetOffset() const
7197{
7198 switch(m_Type)
7199 {
7200 case ALLOCATION_TYPE_BLOCK:
7201 return m_BlockAllocation.m_Offset;
7202 case ALLOCATION_TYPE_DEDICATED:
7203 return 0;
7204 default:
7205 VMA_ASSERT(0);
7206 return 0;
7207 }
7208}
7209
7210VkDeviceMemory VmaAllocation_T::GetMemory() const
7211{
7212 switch(m_Type)
7213 {
7214 case ALLOCATION_TYPE_BLOCK:
7215 return m_BlockAllocation.m_Block->GetDeviceMemory();
7216 case ALLOCATION_TYPE_DEDICATED:
7217 return m_DedicatedAllocation.m_hMemory;
7218 default:
7219 VMA_ASSERT(0);
7220 return VK_NULL_HANDLE;
7221 }
7222}
7223
7224uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7225{
7226 switch(m_Type)
7227 {
7228 case ALLOCATION_TYPE_BLOCK:
7229 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7230 case ALLOCATION_TYPE_DEDICATED:
7231 return m_DedicatedAllocation.m_MemoryTypeIndex;
7232 default:
7233 VMA_ASSERT(0);
7234 return UINT32_MAX;
7235 }
7236}
7237
7238void* VmaAllocation_T::GetMappedData() const
7239{
7240 switch(m_Type)
7241 {
7242 case ALLOCATION_TYPE_BLOCK:
7243 if(m_MapCount != 0)
7244 {
7245 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7246 VMA_ASSERT(pBlockData != VMA_NULL);
7247 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7248 }
7249 else
7250 {
7251 return VMA_NULL;
7252 }
7253 break;
7254 case ALLOCATION_TYPE_DEDICATED:
7255 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7256 return m_DedicatedAllocation.m_pMappedData;
7257 default:
7258 VMA_ASSERT(0);
7259 return VMA_NULL;
7260 }
7261}
7262
7263bool VmaAllocation_T::CanBecomeLost() const
7264{
7265 switch(m_Type)
7266 {
7267 case ALLOCATION_TYPE_BLOCK:
7268 return m_BlockAllocation.m_CanBecomeLost;
7269 case ALLOCATION_TYPE_DEDICATED:
7270 return false;
7271 default:
7272 VMA_ASSERT(0);
7273 return false;
7274 }
7275}
7276
7277VmaPool VmaAllocation_T::GetPool() const
7278{
7279 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7280 return m_BlockAllocation.m_hPool;
7281}
7282
7283bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7284{
7285 VMA_ASSERT(CanBecomeLost());
7286
7287 /*
7288 Warning: This is a carefully designed algorithm.
7289 Do not modify unless you really know what you're doing :)
7290 */
7291 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7292 for(;;)
7293 {
7294 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7295 {
7296 VMA_ASSERT(0);
7297 return false;
7298 }
7299 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7300 {
7301 return false;
7302 }
7303 else // Last use time earlier than current time.
7304 {
7305 if(CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: VMA_FRAME_INDEX_LOST))
7306 {
7307 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7308 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7309 return true;
7310 }
7311 }
7312 }
7313}
7314
7315#if VMA_STATS_STRING_ENABLED
7316
7317// Correspond to values of enum VmaSuballocationType.
7318static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7319 "FREE",
7320 "UNKNOWN",
7321 "BUFFER",
7322 "IMAGE_UNKNOWN",
7323 "IMAGE_LINEAR",
7324 "IMAGE_OPTIMAL",
7325};
7326
7327void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7328{
7329 json.WriteString(pStr: "Type");
7330 json.WriteString(pStr: VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7331
7332 json.WriteString(pStr: "Size");
7333 json.WriteNumber(n: m_Size);
7334
7335 if(m_pUserData != VMA_NULL)
7336 {
7337 json.WriteString(pStr: "UserData");
7338 if(IsUserDataString())
7339 {
7340 json.WriteString(pStr: (const char*)m_pUserData);
7341 }
7342 else
7343 {
7344 json.BeginString();
7345 json.ContinueString_Pointer(ptr: m_pUserData);
7346 json.EndString();
7347 }
7348 }
7349
7350 json.WriteString(pStr: "CreationFrameIndex");
7351 json.WriteNumber(n: m_CreationFrameIndex);
7352
7353 json.WriteString(pStr: "LastUseFrameIndex");
7354 json.WriteNumber(n: GetLastUseFrameIndex());
7355
7356 if(m_BufferImageUsage != 0)
7357 {
7358 json.WriteString(pStr: "Usage");
7359 json.WriteNumber(n: m_BufferImageUsage);
7360 }
7361}
7362
7363#endif
7364
7365void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7366{
7367 VMA_ASSERT(IsUserDataString());
7368 if(m_pUserData != VMA_NULL)
7369 {
7370 char* const oldStr = (char*)m_pUserData;
7371 const size_t oldStrLen = strlen(s: oldStr);
7372 vma_delete_array(hAllocator, ptr: oldStr, count: oldStrLen + 1);
7373 m_pUserData = VMA_NULL;
7374 }
7375}
7376
7377void VmaAllocation_T::BlockAllocMap()
7378{
7379 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7380
7381 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7382 {
7383 ++m_MapCount;
7384 }
7385 else
7386 {
7387 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7388 }
7389}
7390
7391void VmaAllocation_T::BlockAllocUnmap()
7392{
7393 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7394
7395 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7396 {
7397 --m_MapCount;
7398 }
7399 else
7400 {
7401 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7402 }
7403}
7404
7405VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7406{
7407 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7408
7409 if(m_MapCount != 0)
7410 {
7411 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7412 {
7413 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7414 *ppData = m_DedicatedAllocation.m_pMappedData;
7415 ++m_MapCount;
7416 return VK_SUCCESS;
7417 }
7418 else
7419 {
7420 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7421 return VK_ERROR_MEMORY_MAP_FAILED;
7422 }
7423 }
7424 else
7425 {
7426 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7427 hAllocator->m_hDevice,
7428 m_DedicatedAllocation.m_hMemory,
7429 0, // offset
7430 VK_WHOLE_SIZE,
7431 0, // flags
7432 ppData);
7433 if(result == VK_SUCCESS)
7434 {
7435 m_DedicatedAllocation.m_pMappedData = *ppData;
7436 m_MapCount = 1;
7437 }
7438 return result;
7439 }
7440}
7441
7442void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7443{
7444 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7445
7446 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7447 {
7448 --m_MapCount;
7449 if(m_MapCount == 0)
7450 {
7451 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7452 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7453 hAllocator->m_hDevice,
7454 m_DedicatedAllocation.m_hMemory);
7455 }
7456 }
7457 else
7458 {
7459 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7460 }
7461}
7462
7463#if VMA_STATS_STRING_ENABLED
7464
7465static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7466{
7467 json.BeginObject();
7468
7469 json.WriteString(pStr: "Blocks");
7470 json.WriteNumber(n: stat.blockCount);
7471
7472 json.WriteString(pStr: "Allocations");
7473 json.WriteNumber(n: stat.allocationCount);
7474
7475 json.WriteString(pStr: "UnusedRanges");
7476 json.WriteNumber(n: stat.unusedRangeCount);
7477
7478 json.WriteString(pStr: "UsedBytes");
7479 json.WriteNumber(n: stat.usedBytes);
7480
7481 json.WriteString(pStr: "UnusedBytes");
7482 json.WriteNumber(n: stat.unusedBytes);
7483
7484 if(stat.allocationCount > 1)
7485 {
7486 json.WriteString(pStr: "AllocationSize");
7487 json.BeginObject(singleLine: true);
7488 json.WriteString(pStr: "Min");
7489 json.WriteNumber(n: stat.allocationSizeMin);
7490 json.WriteString(pStr: "Avg");
7491 json.WriteNumber(n: stat.allocationSizeAvg);
7492 json.WriteString(pStr: "Max");
7493 json.WriteNumber(n: stat.allocationSizeMax);
7494 json.EndObject();
7495 }
7496
7497 if(stat.unusedRangeCount > 1)
7498 {
7499 json.WriteString(pStr: "UnusedRangeSize");
7500 json.BeginObject(singleLine: true);
7501 json.WriteString(pStr: "Min");
7502 json.WriteNumber(n: stat.unusedRangeSizeMin);
7503 json.WriteString(pStr: "Avg");
7504 json.WriteNumber(n: stat.unusedRangeSizeAvg);
7505 json.WriteString(pStr: "Max");
7506 json.WriteNumber(n: stat.unusedRangeSizeMax);
7507 json.EndObject();
7508 }
7509
7510 json.EndObject();
7511}
7512
7513#endif // #if VMA_STATS_STRING_ENABLED
7514
7515struct VmaSuballocationItemSizeLess
7516{
7517 bool operator()(
7518 const VmaSuballocationList::iterator lhs,
7519 const VmaSuballocationList::iterator rhs) const
7520 {
7521 return lhs->size < rhs->size;
7522 }
7523 bool operator()(
7524 const VmaSuballocationList::iterator lhs,
7525 VkDeviceSize rhsSize) const
7526 {
7527 return lhs->size < rhsSize;
7528 }
7529};
7530
7531
7532////////////////////////////////////////////////////////////////////////////////
7533// class VmaBlockMetadata
7534
7535VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7536 m_Size(0),
7537 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7538{
7539}
7540
7541#if VMA_STATS_STRING_ENABLED
7542
7543void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7544 VkDeviceSize unusedBytes,
7545 size_t allocationCount,
7546 size_t unusedRangeCount) const
7547{
7548 json.BeginObject();
7549
7550 json.WriteString(pStr: "TotalBytes");
7551 json.WriteNumber(n: GetSize());
7552
7553 json.WriteString(pStr: "UnusedBytes");
7554 json.WriteNumber(n: unusedBytes);
7555
7556 json.WriteString(pStr: "Allocations");
7557 json.WriteNumber(n: (uint64_t)allocationCount);
7558
7559 json.WriteString(pStr: "UnusedRanges");
7560 json.WriteNumber(n: (uint64_t)unusedRangeCount);
7561
7562 json.WriteString(pStr: "Suballocations");
7563 json.BeginArray();
7564}
7565
7566void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7567 VkDeviceSize offset,
7568 VmaAllocation hAllocation) const
7569{
7570 json.BeginObject(singleLine: true);
7571
7572 json.WriteString(pStr: "Offset");
7573 json.WriteNumber(n: offset);
7574
7575 hAllocation->PrintParameters(json);
7576
7577 json.EndObject();
7578}
7579
7580void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7581 VkDeviceSize offset,
7582 VkDeviceSize size) const
7583{
7584 json.BeginObject(singleLine: true);
7585
7586 json.WriteString(pStr: "Offset");
7587 json.WriteNumber(n: offset);
7588
7589 json.WriteString(pStr: "Type");
7590 json.WriteString(pStr: VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7591
7592 json.WriteString(pStr: "Size");
7593 json.WriteNumber(n: size);
7594
7595 json.EndObject();
7596}
7597
7598void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7599{
7600 json.EndArray();
7601 json.EndObject();
7602}
7603
7604#endif // #if VMA_STATS_STRING_ENABLED
7605
7606////////////////////////////////////////////////////////////////////////////////
7607// class VmaBlockMetadata_Generic
7608
7609VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7610 VmaBlockMetadata(hAllocator),
7611 m_FreeCount(0),
7612 m_SumFreeSize(0),
7613 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7614 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7615{
7616}
7617
7618VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7619{
7620}
7621
7622void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7623{
7624 VmaBlockMetadata::Init(size);
7625
7626 m_FreeCount = 1;
7627 m_SumFreeSize = size;
7628
7629 VmaSuballocation suballoc = {};
7630 suballoc.offset = 0;
7631 suballoc.size = size;
7632 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7633 suballoc.hAllocation = VK_NULL_HANDLE;
7634
7635 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7636 m_Suballocations.push_back(value: suballoc);
7637 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7638 --suballocItem;
7639 m_FreeSuballocationsBySize.push_back(src: suballocItem);
7640}
7641
7642bool VmaBlockMetadata_Generic::Validate() const
7643{
7644 VMA_VALIDATE(!m_Suballocations.empty());
7645
7646 // Expected offset of new suballocation as calculated from previous ones.
7647 VkDeviceSize calculatedOffset = 0;
7648 // Expected number of free suballocations as calculated from traversing their list.
7649 uint32_t calculatedFreeCount = 0;
7650 // Expected sum size of free suballocations as calculated from traversing their list.
7651 VkDeviceSize calculatedSumFreeSize = 0;
7652 // Expected number of free suballocations that should be registered in
7653 // m_FreeSuballocationsBySize calculated from traversing their list.
7654 size_t freeSuballocationsToRegister = 0;
7655 // True if previous visited suballocation was free.
7656 bool prevFree = false;
7657
7658 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7659 suballocItem != m_Suballocations.cend();
7660 ++suballocItem)
7661 {
7662 const VmaSuballocation& subAlloc = *suballocItem;
7663
7664 // Actual offset of this suballocation doesn't match expected one.
7665 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7666
7667 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7668 // Two adjacent free suballocations are invalid. They should be merged.
7669 VMA_VALIDATE(!prevFree || !currFree);
7670
7671 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7672
7673 if(currFree)
7674 {
7675 calculatedSumFreeSize += subAlloc.size;
7676 ++calculatedFreeCount;
7677 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7678 {
7679 ++freeSuballocationsToRegister;
7680 }
7681
7682 // Margin required between allocations - every free space must be at least that large.
7683#if VMA_DEBUG_MARGIN
7684 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7685#endif
7686 }
7687 else
7688 {
7689 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7690 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7691
7692 // Margin required between allocations - previous allocation must be free.
7693 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7694 }
7695
7696 calculatedOffset += subAlloc.size;
7697 prevFree = currFree;
7698 }
7699
7700 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7701 // match expected one.
7702 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7703
7704 VkDeviceSize lastSize = 0;
7705 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7706 {
7707 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7708
7709 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7710 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7711 // They must be sorted by size ascending.
7712 VMA_VALIDATE(suballocItem->size >= lastSize);
7713
7714 lastSize = suballocItem->size;
7715 }
7716
7717 // Check if totals match calculacted values.
7718 VMA_VALIDATE(ValidateFreeSuballocationList());
7719 VMA_VALIDATE(calculatedOffset == GetSize());
7720 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7721 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7722
7723 return true;
7724}
7725
7726VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7727{
7728 if(!m_FreeSuballocationsBySize.empty())
7729 {
7730 return m_FreeSuballocationsBySize.back()->size;
7731 }
7732 else
7733 {
7734 return 0;
7735 }
7736}
7737
7738bool VmaBlockMetadata_Generic::IsEmpty() const
7739{
7740 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7741}
7742
7743void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7744{
7745 outInfo.blockCount = 1;
7746
7747 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7748 outInfo.allocationCount = rangeCount - m_FreeCount;
7749 outInfo.unusedRangeCount = m_FreeCount;
7750
7751 outInfo.unusedBytes = m_SumFreeSize;
7752 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7753
7754 outInfo.allocationSizeMin = UINT64_MAX;
7755 outInfo.allocationSizeMax = 0;
7756 outInfo.unusedRangeSizeMin = UINT64_MAX;
7757 outInfo.unusedRangeSizeMax = 0;
7758
7759 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7760 suballocItem != m_Suballocations.cend();
7761 ++suballocItem)
7762 {
7763 const VmaSuballocation& suballoc = *suballocItem;
7764 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7765 {
7766 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7767 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7768 }
7769 else
7770 {
7771 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7772 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7773 }
7774 }
7775}
7776
7777void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7778{
7779 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7780
7781 inoutStats.size += GetSize();
7782 inoutStats.unusedSize += m_SumFreeSize;
7783 inoutStats.allocationCount += rangeCount - m_FreeCount;
7784 inoutStats.unusedRangeCount += m_FreeCount;
7785 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7786}
7787
7788#if VMA_STATS_STRING_ENABLED
7789
7790void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7791{
7792 PrintDetailedMap_Begin(json,
7793 unusedBytes: m_SumFreeSize, // unusedBytes
7794 allocationCount: m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7795 unusedRangeCount: m_FreeCount); // unusedRangeCount
7796
7797 size_t i = 0;
7798 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7799 suballocItem != m_Suballocations.cend();
7800 ++suballocItem, ++i)
7801 {
7802 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7803 {
7804 PrintDetailedMap_UnusedRange(json, offset: suballocItem->offset, size: suballocItem->size);
7805 }
7806 else
7807 {
7808 PrintDetailedMap_Allocation(json, offset: suballocItem->offset, hAllocation: suballocItem->hAllocation);
7809 }
7810 }
7811
7812 PrintDetailedMap_End(json);
7813}
7814
7815#endif // #if VMA_STATS_STRING_ENABLED
7816
7817bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7818 uint32_t currentFrameIndex,
7819 uint32_t frameInUseCount,
7820 VkDeviceSize bufferImageGranularity,
7821 VkDeviceSize allocSize,
7822 VkDeviceSize allocAlignment,
7823 bool upperAddress,
7824 VmaSuballocationType allocType,
7825 bool canMakeOtherLost,
7826 uint32_t strategy,
7827 VmaAllocationRequest* pAllocationRequest)
7828{
7829 VMA_ASSERT(allocSize > 0);
7830 VMA_ASSERT(!upperAddress);
7831 (void) upperAddress;
7832 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7833 VMA_ASSERT(pAllocationRequest != VMA_NULL);
7834 VMA_HEAVY_ASSERT(Validate());
7835
7836 // There is not enough total free space in this block to fullfill the request: Early return.
7837 if(canMakeOtherLost == false &&
7838 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7839 {
7840 return false;
7841 }
7842
7843 // New algorithm, efficiently searching freeSuballocationsBySize.
7844 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7845 if(freeSuballocCount > 0)
7846 {
7847 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7848 {
7849 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7850 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7851 beg: m_FreeSuballocationsBySize.data(),
7852 end: m_FreeSuballocationsBySize.data() + freeSuballocCount,
7853 key: allocSize + 2 * VMA_DEBUG_MARGIN,
7854 cmp: VmaSuballocationItemSizeLess());
7855 size_t index = it - m_FreeSuballocationsBySize.data();
7856 for(; index < freeSuballocCount; ++index)
7857 {
7858 if(CheckAllocation(
7859 currentFrameIndex,
7860 frameInUseCount,
7861 bufferImageGranularity,
7862 allocSize,
7863 allocAlignment,
7864 allocType,
7865 suballocItem: m_FreeSuballocationsBySize[index],
7866 canMakeOtherLost: false, // canMakeOtherLost
7867 pOffset: &pAllocationRequest->offset,
7868 itemsToMakeLostCount: &pAllocationRequest->itemsToMakeLostCount,
7869 pSumFreeSize: &pAllocationRequest->sumFreeSize,
7870 pSumItemSize: &pAllocationRequest->sumItemSize))
7871 {
7872 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7873 return true;
7874 }
7875 }
7876 }
7877 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7878 {
7879 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7880 it != m_Suballocations.end();
7881 ++it)
7882 {
7883 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7884 currentFrameIndex,
7885 frameInUseCount,
7886 bufferImageGranularity,
7887 allocSize,
7888 allocAlignment,
7889 allocType,
7890 suballocItem: it,
7891 canMakeOtherLost: false, // canMakeOtherLost
7892 pOffset: &pAllocationRequest->offset,
7893 itemsToMakeLostCount: &pAllocationRequest->itemsToMakeLostCount,
7894 pSumFreeSize: &pAllocationRequest->sumFreeSize,
7895 pSumItemSize: &pAllocationRequest->sumItemSize))
7896 {
7897 pAllocationRequest->item = it;
7898 return true;
7899 }
7900 }
7901 }
7902 else // WORST_FIT, FIRST_FIT
7903 {
7904 // Search staring from biggest suballocations.
7905 for(size_t index = freeSuballocCount; index--; )
7906 {
7907 if(CheckAllocation(
7908 currentFrameIndex,
7909 frameInUseCount,
7910 bufferImageGranularity,
7911 allocSize,
7912 allocAlignment,
7913 allocType,
7914 suballocItem: m_FreeSuballocationsBySize[index],
7915 canMakeOtherLost: false, // canMakeOtherLost
7916 pOffset: &pAllocationRequest->offset,
7917 itemsToMakeLostCount: &pAllocationRequest->itemsToMakeLostCount,
7918 pSumFreeSize: &pAllocationRequest->sumFreeSize,
7919 pSumItemSize: &pAllocationRequest->sumItemSize))
7920 {
7921 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7922 return true;
7923 }
7924 }
7925 }
7926 }
7927
7928 if(canMakeOtherLost)
7929 {
7930 // Brute-force algorithm. TODO: Come up with something better.
7931
7932 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7933 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7934
7935 VmaAllocationRequest tmpAllocRequest = {};
7936 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7937 suballocIt != m_Suballocations.end();
7938 ++suballocIt)
7939 {
7940 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7941 suballocIt->hAllocation->CanBecomeLost())
7942 {
7943 if(CheckAllocation(
7944 currentFrameIndex,
7945 frameInUseCount,
7946 bufferImageGranularity,
7947 allocSize,
7948 allocAlignment,
7949 allocType,
7950 suballocItem: suballocIt,
7951 canMakeOtherLost,
7952 pOffset: &tmpAllocRequest.offset,
7953 itemsToMakeLostCount: &tmpAllocRequest.itemsToMakeLostCount,
7954 pSumFreeSize: &tmpAllocRequest.sumFreeSize,
7955 pSumItemSize: &tmpAllocRequest.sumItemSize))
7956 {
7957 tmpAllocRequest.item = suballocIt;
7958
7959 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7960 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7961 {
7962 *pAllocationRequest = tmpAllocRequest;
7963 }
7964 }
7965 }
7966 }
7967
7968 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7969 {
7970 return true;
7971 }
7972 }
7973
7974 return false;
7975}
7976
7977bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7978 uint32_t currentFrameIndex,
7979 uint32_t frameInUseCount,
7980 VmaAllocationRequest* pAllocationRequest)
7981{
7982 while(pAllocationRequest->itemsToMakeLostCount > 0)
7983 {
7984 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7985 {
7986 ++pAllocationRequest->item;
7987 }
7988 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7989 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
7990 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
7991 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7992 {
7993 pAllocationRequest->item = FreeSuballocation(suballocItem: pAllocationRequest->item);
7994 --pAllocationRequest->itemsToMakeLostCount;
7995 }
7996 else
7997 {
7998 return false;
7999 }
8000 }
8001
8002 VMA_HEAVY_ASSERT(Validate());
8003 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8004 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8005
8006 return true;
8007}
8008
8009uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8010{
8011 uint32_t lostAllocationCount = 0;
8012 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8013 it != m_Suballocations.end();
8014 ++it)
8015 {
8016 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8017 it->hAllocation->CanBecomeLost() &&
8018 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8019 {
8020 it = FreeSuballocation(suballocItem: it);
8021 ++lostAllocationCount;
8022 }
8023 }
8024 return lostAllocationCount;
8025}
8026
8027VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8028{
8029 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8030 it != m_Suballocations.end();
8031 ++it)
8032 {
8033 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8034 {
8035 if(!VmaValidateMagicValue(pData: pBlockData, offset: it->offset - VMA_DEBUG_MARGIN))
8036 {
8037 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8038 return VK_ERROR_VALIDATION_FAILED_EXT;
8039 }
8040 if(!VmaValidateMagicValue(pData: pBlockData, offset: it->offset + it->size))
8041 {
8042 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8043 return VK_ERROR_VALIDATION_FAILED_EXT;
8044 }
8045 }
8046 }
8047
8048 return VK_SUCCESS;
8049}
8050
8051void VmaBlockMetadata_Generic::Alloc(
8052 const VmaAllocationRequest& request,
8053 VmaSuballocationType type,
8054 VkDeviceSize allocSize,
8055 bool upperAddress,
8056 VmaAllocation hAllocation)
8057{
8058 VMA_ASSERT(!upperAddress);
8059 (void) upperAddress;
8060 VMA_ASSERT(request.item != m_Suballocations.end());
8061 VmaSuballocation& suballoc = *request.item;
8062 // Given suballocation is a free block.
8063 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8064 // Given offset is inside this suballocation.
8065 VMA_ASSERT(request.offset >= suballoc.offset);
8066 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8067 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8068 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8069
8070 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8071 // it to become used.
8072 UnregisterFreeSuballocation(item: request.item);
8073
8074 suballoc.offset = request.offset;
8075 suballoc.size = allocSize;
8076 suballoc.type = type;
8077 suballoc.hAllocation = hAllocation;
8078
8079 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8080 if(paddingEnd)
8081 {
8082 VmaSuballocation paddingSuballoc = {};
8083 paddingSuballoc.offset = request.offset + allocSize;
8084 paddingSuballoc.size = paddingEnd;
8085 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8086 VmaSuballocationList::iterator next = request.item;
8087 ++next;
8088 const VmaSuballocationList::iterator paddingEndItem =
8089 m_Suballocations.insert(it: next, value: paddingSuballoc);
8090 RegisterFreeSuballocation(item: paddingEndItem);
8091 }
8092
8093 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8094 if(paddingBegin)
8095 {
8096 VmaSuballocation paddingSuballoc = {};
8097 paddingSuballoc.offset = request.offset - paddingBegin;
8098 paddingSuballoc.size = paddingBegin;
8099 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8100 const VmaSuballocationList::iterator paddingBeginItem =
8101 m_Suballocations.insert(it: request.item, value: paddingSuballoc);
8102 RegisterFreeSuballocation(item: paddingBeginItem);
8103 }
8104
8105 // Update totals.
8106 m_FreeCount = m_FreeCount - 1;
8107 if(paddingBegin > 0)
8108 {
8109 ++m_FreeCount;
8110 }
8111 if(paddingEnd > 0)
8112 {
8113 ++m_FreeCount;
8114 }
8115 m_SumFreeSize -= allocSize;
8116}
8117
8118void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8119{
8120 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8121 suballocItem != m_Suballocations.end();
8122 ++suballocItem)
8123 {
8124 VmaSuballocation& suballoc = *suballocItem;
8125 if(suballoc.hAllocation == allocation)
8126 {
8127 FreeSuballocation(suballocItem);
8128 VMA_HEAVY_ASSERT(Validate());
8129 return;
8130 }
8131 }
8132 VMA_ASSERT(0 && "Not found!");
8133}
8134
8135void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8136{
8137 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8138 suballocItem != m_Suballocations.end();
8139 ++suballocItem)
8140 {
8141 VmaSuballocation& suballoc = *suballocItem;
8142 if(suballoc.offset == offset)
8143 {
8144 FreeSuballocation(suballocItem);
8145 return;
8146 }
8147 }
8148 VMA_ASSERT(0 && "Not found!");
8149}
8150
8151bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8152{
8153 typedef VmaSuballocationList::iterator iter_type;
8154 for(iter_type suballocItem = m_Suballocations.begin();
8155 suballocItem != m_Suballocations.end();
8156 ++suballocItem)
8157 {
8158 VmaSuballocation& suballoc = *suballocItem;
8159 if(suballoc.hAllocation == alloc)
8160 {
8161 iter_type nextItem = suballocItem;
8162 ++nextItem;
8163
8164 // Should have been ensured on higher level.
8165 VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8166
8167 // Shrinking.
8168 if(newSize < alloc->GetSize())
8169 {
8170 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8171
8172 // There is next item.
8173 if(nextItem != m_Suballocations.end())
8174 {
8175 // Next item is free.
8176 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8177 {
8178 // Grow this next item backward.
8179 UnregisterFreeSuballocation(item: nextItem);
8180 nextItem->offset -= sizeDiff;
8181 nextItem->size += sizeDiff;
8182 RegisterFreeSuballocation(item: nextItem);
8183 }
8184 // Next item is not free.
8185 else
8186 {
8187 // Create free item after current one.
8188 VmaSuballocation newFreeSuballoc;
8189 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8190 newFreeSuballoc.offset = suballoc.offset + newSize;
8191 newFreeSuballoc.size = sizeDiff;
8192 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8193 iter_type newFreeSuballocIt = m_Suballocations.insert(it: nextItem, value: newFreeSuballoc);
8194 RegisterFreeSuballocation(item: newFreeSuballocIt);
8195
8196 ++m_FreeCount;
8197 }
8198 }
8199 // This is the last item.
8200 else
8201 {
8202 // Create free item at the end.
8203 VmaSuballocation newFreeSuballoc;
8204 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8205 newFreeSuballoc.offset = suballoc.offset + newSize;
8206 newFreeSuballoc.size = sizeDiff;
8207 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8208 m_Suballocations.push_back(value: newFreeSuballoc);
8209
8210 iter_type newFreeSuballocIt = m_Suballocations.end();
8211 RegisterFreeSuballocation(item: --newFreeSuballocIt);
8212
8213 ++m_FreeCount;
8214 }
8215
8216 suballoc.size = newSize;
8217 m_SumFreeSize += sizeDiff;
8218 }
8219 // Growing.
8220 else
8221 {
8222 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8223
8224 // There is next item.
8225 if(nextItem != m_Suballocations.end())
8226 {
8227 // Next item is free.
8228 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8229 {
8230 // There is not enough free space, including margin.
8231 if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8232 {
8233 return false;
8234 }
8235
8236 // There is more free space than required.
8237 if(nextItem->size > sizeDiff)
8238 {
8239 // Move and shrink this next item.
8240 UnregisterFreeSuballocation(item: nextItem);
8241 nextItem->offset += sizeDiff;
8242 nextItem->size -= sizeDiff;
8243 RegisterFreeSuballocation(item: nextItem);
8244 }
8245 // There is exactly the amount of free space required.
8246 else
8247 {
8248 // Remove this next free item.
8249 UnregisterFreeSuballocation(item: nextItem);
8250 m_Suballocations.erase(it: nextItem);
8251 --m_FreeCount;
8252 }
8253 }
8254 // Next item is not free - there is no space to grow.
8255 else
8256 {
8257 return false;
8258 }
8259 }
8260 // This is the last item - there is no space to grow.
8261 else
8262 {
8263 return false;
8264 }
8265
8266 suballoc.size = newSize;
8267 m_SumFreeSize -= sizeDiff;
8268 }
8269
8270 // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8271 return true;
8272 }
8273 }
8274 VMA_ASSERT(0 && "Not found!");
8275 return false;
8276}
8277
8278bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8279{
8280 VkDeviceSize lastSize = 0;
8281 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8282 {
8283 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8284
8285 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8286 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8287 VMA_VALIDATE(it->size >= lastSize);
8288 lastSize = it->size;
8289 }
8290 return true;
8291}
8292
8293bool VmaBlockMetadata_Generic::CheckAllocation(
8294 uint32_t currentFrameIndex,
8295 uint32_t frameInUseCount,
8296 VkDeviceSize bufferImageGranularity,
8297 VkDeviceSize allocSize,
8298 VkDeviceSize allocAlignment,
8299 VmaSuballocationType allocType,
8300 VmaSuballocationList::const_iterator suballocItem,
8301 bool canMakeOtherLost,
8302 VkDeviceSize* pOffset,
8303 size_t* itemsToMakeLostCount,
8304 VkDeviceSize* pSumFreeSize,
8305 VkDeviceSize* pSumItemSize) const
8306{
8307 VMA_ASSERT(allocSize > 0);
8308 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8309 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8310 VMA_ASSERT(pOffset != VMA_NULL);
8311
8312 *itemsToMakeLostCount = 0;
8313 *pSumFreeSize = 0;
8314 *pSumItemSize = 0;
8315
8316 if(canMakeOtherLost)
8317 {
8318 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8319 {
8320 *pSumFreeSize = suballocItem->size;
8321 }
8322 else
8323 {
8324 if(suballocItem->hAllocation->CanBecomeLost() &&
8325 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8326 {
8327 ++*itemsToMakeLostCount;
8328 *pSumItemSize = suballocItem->size;
8329 }
8330 else
8331 {
8332 return false;
8333 }
8334 }
8335
8336 // Remaining size is too small for this request: Early return.
8337 if(GetSize() - suballocItem->offset < allocSize)
8338 {
8339 return false;
8340 }
8341
8342 // Start from offset equal to beginning of this suballocation.
8343 *pOffset = suballocItem->offset;
8344
8345 // Apply VMA_DEBUG_MARGIN at the beginning.
8346 if(VMA_DEBUG_MARGIN > 0)
8347 {
8348 *pOffset += VMA_DEBUG_MARGIN;
8349 }
8350
8351 // Apply alignment.
8352 *pOffset = VmaAlignUp(val: *pOffset, align: allocAlignment);
8353
8354 // Check previous suballocations for BufferImageGranularity conflicts.
8355 // Make bigger alignment if necessary.
8356 if(bufferImageGranularity > 1)
8357 {
8358 bool bufferImageGranularityConflict = false;
8359 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8360 while(prevSuballocItem != m_Suballocations.cbegin())
8361 {
8362 --prevSuballocItem;
8363 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8364 if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: *pOffset, pageSize: bufferImageGranularity))
8365 {
8366 if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType))
8367 {
8368 bufferImageGranularityConflict = true;
8369 break;
8370 }
8371 }
8372 else
8373 // Already on previous page.
8374 break;
8375 }
8376 if(bufferImageGranularityConflict)
8377 {
8378 *pOffset = VmaAlignUp(val: *pOffset, align: bufferImageGranularity);
8379 }
8380 }
8381
8382 // Now that we have final *pOffset, check if we are past suballocItem.
8383 // If yes, return false - this function should be called for another suballocItem as starting point.
8384 if(*pOffset >= suballocItem->offset + suballocItem->size)
8385 {
8386 return false;
8387 }
8388
8389 // Calculate padding at the beginning based on current offset.
8390 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8391
8392 // Calculate required margin at the end.
8393 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8394
8395 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8396 // Another early return check.
8397 if(suballocItem->offset + totalSize > GetSize())
8398 {
8399 return false;
8400 }
8401
8402 // Advance lastSuballocItem until desired size is reached.
8403 // Update itemsToMakeLostCount.
8404 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8405 if(totalSize > suballocItem->size)
8406 {
8407 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8408 while(remainingSize > 0)
8409 {
8410 ++lastSuballocItem;
8411 if(lastSuballocItem == m_Suballocations.cend())
8412 {
8413 return false;
8414 }
8415 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8416 {
8417 *pSumFreeSize += lastSuballocItem->size;
8418 }
8419 else
8420 {
8421 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8422 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8423 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8424 {
8425 ++*itemsToMakeLostCount;
8426 *pSumItemSize += lastSuballocItem->size;
8427 }
8428 else
8429 {
8430 return false;
8431 }
8432 }
8433 remainingSize = (lastSuballocItem->size < remainingSize) ?
8434 remainingSize - lastSuballocItem->size : 0;
8435 }
8436 }
8437
8438 // Check next suballocations for BufferImageGranularity conflicts.
8439 // If conflict exists, we must mark more allocations lost or fail.
8440 if(bufferImageGranularity > 1)
8441 {
8442 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8443 ++nextSuballocItem;
8444 while(nextSuballocItem != m_Suballocations.cend())
8445 {
8446 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8447 if(VmaBlocksOnSamePage(resourceAOffset: *pOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity))
8448 {
8449 if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type))
8450 {
8451 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8452 if(nextSuballoc.hAllocation->CanBecomeLost() &&
8453 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8454 {
8455 ++*itemsToMakeLostCount;
8456 }
8457 else
8458 {
8459 return false;
8460 }
8461 }
8462 }
8463 else
8464 {
8465 // Already on next page.
8466 break;
8467 }
8468 ++nextSuballocItem;
8469 }
8470 }
8471 }
8472 else
8473 {
8474 const VmaSuballocation& suballoc = *suballocItem;
8475 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8476
8477 *pSumFreeSize = suballoc.size;
8478
8479 // Size of this suballocation is too small for this request: Early return.
8480 if(suballoc.size < allocSize)
8481 {
8482 return false;
8483 }
8484
8485 // Start from offset equal to beginning of this suballocation.
8486 *pOffset = suballoc.offset;
8487
8488 // Apply VMA_DEBUG_MARGIN at the beginning.
8489 if(VMA_DEBUG_MARGIN > 0)
8490 {
8491 *pOffset += VMA_DEBUG_MARGIN;
8492 }
8493
8494 // Apply alignment.
8495 *pOffset = VmaAlignUp(val: *pOffset, align: allocAlignment);
8496
8497 // Check previous suballocations for BufferImageGranularity conflicts.
8498 // Make bigger alignment if necessary.
8499 if(bufferImageGranularity > 1)
8500 {
8501 bool bufferImageGranularityConflict = false;
8502 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8503 while(prevSuballocItem != m_Suballocations.cbegin())
8504 {
8505 --prevSuballocItem;
8506 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8507 if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: *pOffset, pageSize: bufferImageGranularity))
8508 {
8509 if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType))
8510 {
8511 bufferImageGranularityConflict = true;
8512 break;
8513 }
8514 }
8515 else
8516 // Already on previous page.
8517 break;
8518 }
8519 if(bufferImageGranularityConflict)
8520 {
8521 *pOffset = VmaAlignUp(val: *pOffset, align: bufferImageGranularity);
8522 }
8523 }
8524
8525 // Calculate padding at the beginning based on current offset.
8526 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8527
8528 // Calculate required margin at the end.
8529 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8530
8531 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8532 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8533 {
8534 return false;
8535 }
8536
8537 // Check next suballocations for BufferImageGranularity conflicts.
8538 // If conflict exists, allocation cannot be made here.
8539 if(bufferImageGranularity > 1)
8540 {
8541 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8542 ++nextSuballocItem;
8543 while(nextSuballocItem != m_Suballocations.cend())
8544 {
8545 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8546 if(VmaBlocksOnSamePage(resourceAOffset: *pOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity))
8547 {
8548 if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type))
8549 {
8550 return false;
8551 }
8552 }
8553 else
8554 {
8555 // Already on next page.
8556 break;
8557 }
8558 ++nextSuballocItem;
8559 }
8560 }
8561 }
8562
8563 // All tests passed: Success. pOffset is already filled.
8564 return true;
8565}
8566
8567void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8568{
8569 VMA_ASSERT(item != m_Suballocations.end());
8570 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8571
8572 VmaSuballocationList::iterator nextItem = item;
8573 ++nextItem;
8574 VMA_ASSERT(nextItem != m_Suballocations.end());
8575 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8576
8577 item->size += nextItem->size;
8578 --m_FreeCount;
8579 m_Suballocations.erase(it: nextItem);
8580}
8581
8582VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8583{
8584 // Change this suballocation to be marked as free.
8585 VmaSuballocation& suballoc = *suballocItem;
8586 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8587 suballoc.hAllocation = VK_NULL_HANDLE;
8588
8589 // Update totals.
8590 ++m_FreeCount;
8591 m_SumFreeSize += suballoc.size;
8592
8593 // Merge with previous and/or next suballocation if it's also free.
8594 bool mergeWithNext = false;
8595 bool mergeWithPrev = false;
8596
8597 VmaSuballocationList::iterator nextItem = suballocItem;
8598 ++nextItem;
8599 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8600 {
8601 mergeWithNext = true;
8602 }
8603
8604 VmaSuballocationList::iterator prevItem = suballocItem;
8605 if(suballocItem != m_Suballocations.begin())
8606 {
8607 --prevItem;
8608 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8609 {
8610 mergeWithPrev = true;
8611 }
8612 }
8613
8614 if(mergeWithNext)
8615 {
8616 UnregisterFreeSuballocation(item: nextItem);
8617 MergeFreeWithNext(item: suballocItem);
8618 }
8619
8620 if(mergeWithPrev)
8621 {
8622 UnregisterFreeSuballocation(item: prevItem);
8623 MergeFreeWithNext(item: prevItem);
8624 RegisterFreeSuballocation(item: prevItem);
8625 return prevItem;
8626 }
8627 else
8628 {
8629 RegisterFreeSuballocation(item: suballocItem);
8630 return suballocItem;
8631 }
8632}
8633
8634void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8635{
8636 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8637 VMA_ASSERT(item->size > 0);
8638
8639 // You may want to enable this validation at the beginning or at the end of
8640 // this function, depending on what do you want to check.
8641 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8642
8643 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8644 {
8645 if(m_FreeSuballocationsBySize.empty())
8646 {
8647 m_FreeSuballocationsBySize.push_back(src: item);
8648 }
8649 else
8650 {
8651 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(vector&: m_FreeSuballocationsBySize, value: item);
8652 }
8653 }
8654
8655 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8656}
8657
8658
8659void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8660{
8661 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8662 VMA_ASSERT(item->size > 0);
8663
8664 // You may want to enable this validation at the beginning or at the end of
8665 // this function, depending on what do you want to check.
8666 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8667
8668 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8669 {
8670 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8671 beg: m_FreeSuballocationsBySize.data(),
8672 end: m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8673 key: item,
8674 cmp: VmaSuballocationItemSizeLess());
8675 for(size_t index = it - m_FreeSuballocationsBySize.data();
8676 index < m_FreeSuballocationsBySize.size();
8677 ++index)
8678 {
8679 if(m_FreeSuballocationsBySize[index] == item)
8680 {
8681 VmaVectorRemove(vec&: m_FreeSuballocationsBySize, index);
8682 return;
8683 }
8684 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8685 }
8686 VMA_ASSERT(0 && "Not found.");
8687 }
8688
8689 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8690}
8691
8692bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8693 VkDeviceSize bufferImageGranularity,
8694 VmaSuballocationType& inOutPrevSuballocType) const
8695{
8696 if(bufferImageGranularity == 1 || IsEmpty())
8697 {
8698 return false;
8699 }
8700
8701 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8702 bool typeConflictFound = false;
8703 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8704 it != m_Suballocations.cend();
8705 ++it)
8706 {
8707 const VmaSuballocationType suballocType = it->type;
8708 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8709 {
8710 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8711 if(VmaIsBufferImageGranularityConflict(suballocType1: inOutPrevSuballocType, suballocType2: suballocType))
8712 {
8713 typeConflictFound = true;
8714 }
8715 inOutPrevSuballocType = suballocType;
8716 }
8717 }
8718
8719 return typeConflictFound || minAlignment >= bufferImageGranularity;
8720}
8721
8722////////////////////////////////////////////////////////////////////////////////
8723// class VmaBlockMetadata_Linear
8724
8725VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8726 VmaBlockMetadata(hAllocator),
8727 m_SumFreeSize(0),
8728 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8729 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8730 m_1stVectorIndex(0),
8731 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8732 m_1stNullItemsBeginCount(0),
8733 m_1stNullItemsMiddleCount(0),
8734 m_2ndNullItemsCount(0)
8735{
8736}
8737
8738VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8739{
8740}
8741
8742void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8743{
8744 VmaBlockMetadata::Init(size);
8745 m_SumFreeSize = size;
8746}
8747
8748bool VmaBlockMetadata_Linear::Validate() const
8749{
8750 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8751 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8752
8753 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8754 VMA_VALIDATE(!suballocations1st.empty() ||
8755 suballocations2nd.empty() ||
8756 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8757
8758 if(!suballocations1st.empty())
8759 {
8760 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8761 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8762 // Null item at the end should be just pop_back().
8763 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8764 }
8765 if(!suballocations2nd.empty())
8766 {
8767 // Null item at the end should be just pop_back().
8768 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8769 }
8770
8771 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8772 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8773
8774 VkDeviceSize sumUsedSize = 0;
8775 const size_t suballoc1stCount = suballocations1st.size();
8776 VkDeviceSize offset = VMA_DEBUG_MARGIN;
8777
8778 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8779 {
8780 const size_t suballoc2ndCount = suballocations2nd.size();
8781 size_t nullItem2ndCount = 0;
8782 for(size_t i = 0; i < suballoc2ndCount; ++i)
8783 {
8784 const VmaSuballocation& suballoc = suballocations2nd[i];
8785 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8786
8787 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8788 VMA_VALIDATE(suballoc.offset >= offset);
8789
8790 if(!currFree)
8791 {
8792 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8793 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8794 sumUsedSize += suballoc.size;
8795 }
8796 else
8797 {
8798 ++nullItem2ndCount;
8799 }
8800
8801 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8802 }
8803
8804 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8805 }
8806
8807 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8808 {
8809 const VmaSuballocation& suballoc = suballocations1st[i];
8810 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8811 suballoc.hAllocation == VK_NULL_HANDLE);
8812 }
8813
8814 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8815
8816 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8817 {
8818 const VmaSuballocation& suballoc = suballocations1st[i];
8819 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8820
8821 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8822 VMA_VALIDATE(suballoc.offset >= offset);
8823 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8824
8825 if(!currFree)
8826 {
8827 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8828 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8829 sumUsedSize += suballoc.size;
8830 }
8831 else
8832 {
8833 ++nullItem1stCount;
8834 }
8835
8836 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8837 }
8838 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8839
8840 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8841 {
8842 const size_t suballoc2ndCount = suballocations2nd.size();
8843 size_t nullItem2ndCount = 0;
8844 for(size_t i = suballoc2ndCount; i--; )
8845 {
8846 const VmaSuballocation& suballoc = suballocations2nd[i];
8847 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8848
8849 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8850 VMA_VALIDATE(suballoc.offset >= offset);
8851
8852 if(!currFree)
8853 {
8854 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8855 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8856 sumUsedSize += suballoc.size;
8857 }
8858 else
8859 {
8860 ++nullItem2ndCount;
8861 }
8862
8863 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8864 }
8865
8866 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8867 }
8868
8869 VMA_VALIDATE(offset <= GetSize());
8870 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8871
8872 return true;
8873}
8874
8875size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8876{
8877 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8878 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8879}
8880
8881VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8882{
8883 const VkDeviceSize size = GetSize();
8884
8885 /*
8886 We don't consider gaps inside allocation vectors with freed allocations because
8887 they are not suitable for reuse in linear allocator. We consider only space that
8888 is available for new allocations.
8889 */
8890 if(IsEmpty())
8891 {
8892 return size;
8893 }
8894
8895 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8896
8897 switch(m_2ndVectorMode)
8898 {
8899 case SECOND_VECTOR_EMPTY:
8900 /*
8901 Available space is after end of 1st, as well as before beginning of 1st (which
8902 whould make it a ring buffer).
8903 */
8904 {
8905 const size_t suballocations1stCount = suballocations1st.size();
8906 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8907 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8908 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8909 return VMA_MAX(
8910 firstSuballoc.offset,
8911 size - (lastSuballoc.offset + lastSuballoc.size));
8912 }
8913 break;
8914
8915 case SECOND_VECTOR_RING_BUFFER:
8916 /*
8917 Available space is only between end of 2nd and beginning of 1st.
8918 */
8919 {
8920 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8921 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8922 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8923 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8924 }
8925 break;
8926
8927 case SECOND_VECTOR_DOUBLE_STACK:
8928 /*
8929 Available space is only between end of 1st and top of 2nd.
8930 */
8931 {
8932 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8933 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8934 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8935 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8936 }
8937 break;
8938
8939 default:
8940 VMA_ASSERT(0);
8941 return 0;
8942 }
8943}
8944
8945void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8946{
8947 const VkDeviceSize size = GetSize();
8948 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8949 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8950 const size_t suballoc1stCount = suballocations1st.size();
8951 const size_t suballoc2ndCount = suballocations2nd.size();
8952
8953 outInfo.blockCount = 1;
8954 outInfo.allocationCount = (uint32_t)GetAllocationCount();
8955 outInfo.unusedRangeCount = 0;
8956 outInfo.usedBytes = 0;
8957 outInfo.allocationSizeMin = UINT64_MAX;
8958 outInfo.allocationSizeMax = 0;
8959 outInfo.unusedRangeSizeMin = UINT64_MAX;
8960 outInfo.unusedRangeSizeMax = 0;
8961
8962 VkDeviceSize lastOffset = 0;
8963
8964 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8965 {
8966 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8967 size_t nextAlloc2ndIndex = 0;
8968 while(lastOffset < freeSpace2ndTo1stEnd)
8969 {
8970 // Find next non-null allocation or move nextAllocIndex to the end.
8971 while(nextAlloc2ndIndex < suballoc2ndCount &&
8972 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8973 {
8974 ++nextAlloc2ndIndex;
8975 }
8976
8977 // Found non-null allocation.
8978 if(nextAlloc2ndIndex < suballoc2ndCount)
8979 {
8980 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8981
8982 // 1. Process free space before this allocation.
8983 if(lastOffset < suballoc.offset)
8984 {
8985 // There is free space from lastOffset to suballoc.offset.
8986 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8987 ++outInfo.unusedRangeCount;
8988 outInfo.unusedBytes += unusedRangeSize;
8989 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8990 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8991 }
8992
8993 // 2. Process this allocation.
8994 // There is allocation with suballoc.offset, suballoc.size.
8995 outInfo.usedBytes += suballoc.size;
8996 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
8997 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
8998
8999 // 3. Prepare for next iteration.
9000 lastOffset = suballoc.offset + suballoc.size;
9001 ++nextAlloc2ndIndex;
9002 }
9003 // We are at the end.
9004 else
9005 {
9006 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9007 if(lastOffset < freeSpace2ndTo1stEnd)
9008 {
9009 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9010 ++outInfo.unusedRangeCount;
9011 outInfo.unusedBytes += unusedRangeSize;
9012 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9013 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9014 }
9015
9016 // End of loop.
9017 lastOffset = freeSpace2ndTo1stEnd;
9018 }
9019 }
9020 }
9021
9022 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9023 const VkDeviceSize freeSpace1stTo2ndEnd =
9024 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9025 while(lastOffset < freeSpace1stTo2ndEnd)
9026 {
9027 // Find next non-null allocation or move nextAllocIndex to the end.
9028 while(nextAlloc1stIndex < suballoc1stCount &&
9029 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9030 {
9031 ++nextAlloc1stIndex;
9032 }
9033
9034 // Found non-null allocation.
9035 if(nextAlloc1stIndex < suballoc1stCount)
9036 {
9037 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9038
9039 // 1. Process free space before this allocation.
9040 if(lastOffset < suballoc.offset)
9041 {
9042 // There is free space from lastOffset to suballoc.offset.
9043 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9044 ++outInfo.unusedRangeCount;
9045 outInfo.unusedBytes += unusedRangeSize;
9046 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9047 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9048 }
9049
9050 // 2. Process this allocation.
9051 // There is allocation with suballoc.offset, suballoc.size.
9052 outInfo.usedBytes += suballoc.size;
9053 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9054 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9055
9056 // 3. Prepare for next iteration.
9057 lastOffset = suballoc.offset + suballoc.size;
9058 ++nextAlloc1stIndex;
9059 }
9060 // We are at the end.
9061 else
9062 {
9063 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9064 if(lastOffset < freeSpace1stTo2ndEnd)
9065 {
9066 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9067 ++outInfo.unusedRangeCount;
9068 outInfo.unusedBytes += unusedRangeSize;
9069 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9070 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9071 }
9072
9073 // End of loop.
9074 lastOffset = freeSpace1stTo2ndEnd;
9075 }
9076 }
9077
9078 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9079 {
9080 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9081 while(lastOffset < size)
9082 {
9083 // Find next non-null allocation or move nextAllocIndex to the end.
9084 while(nextAlloc2ndIndex != SIZE_MAX &&
9085 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9086 {
9087 --nextAlloc2ndIndex;
9088 }
9089
9090 // Found non-null allocation.
9091 if(nextAlloc2ndIndex != SIZE_MAX)
9092 {
9093 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9094
9095 // 1. Process free space before this allocation.
9096 if(lastOffset < suballoc.offset)
9097 {
9098 // There is free space from lastOffset to suballoc.offset.
9099 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9100 ++outInfo.unusedRangeCount;
9101 outInfo.unusedBytes += unusedRangeSize;
9102 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9103 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9104 }
9105
9106 // 2. Process this allocation.
9107 // There is allocation with suballoc.offset, suballoc.size.
9108 outInfo.usedBytes += suballoc.size;
9109 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9110 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9111
9112 // 3. Prepare for next iteration.
9113 lastOffset = suballoc.offset + suballoc.size;
9114 --nextAlloc2ndIndex;
9115 }
9116 // We are at the end.
9117 else
9118 {
9119 // There is free space from lastOffset to size.
9120 if(lastOffset < size)
9121 {
9122 const VkDeviceSize unusedRangeSize = size - lastOffset;
9123 ++outInfo.unusedRangeCount;
9124 outInfo.unusedBytes += unusedRangeSize;
9125 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9126 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9127 }
9128
9129 // End of loop.
9130 lastOffset = size;
9131 }
9132 }
9133 }
9134
9135 outInfo.unusedBytes = size - outInfo.usedBytes;
9136}
9137
9138void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9139{
9140 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9141 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9142 const VkDeviceSize size = GetSize();
9143 const size_t suballoc1stCount = suballocations1st.size();
9144 const size_t suballoc2ndCount = suballocations2nd.size();
9145
9146 inoutStats.size += size;
9147
9148 VkDeviceSize lastOffset = 0;
9149
9150 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9151 {
9152 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9153 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9154 while(lastOffset < freeSpace2ndTo1stEnd)
9155 {
9156 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9157 while(nextAlloc2ndIndex < suballoc2ndCount &&
9158 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9159 {
9160 ++nextAlloc2ndIndex;
9161 }
9162
9163 // Found non-null allocation.
9164 if(nextAlloc2ndIndex < suballoc2ndCount)
9165 {
9166 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9167
9168 // 1. Process free space before this allocation.
9169 if(lastOffset < suballoc.offset)
9170 {
9171 // There is free space from lastOffset to suballoc.offset.
9172 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9173 inoutStats.unusedSize += unusedRangeSize;
9174 ++inoutStats.unusedRangeCount;
9175 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9176 }
9177
9178 // 2. Process this allocation.
9179 // There is allocation with suballoc.offset, suballoc.size.
9180 ++inoutStats.allocationCount;
9181
9182 // 3. Prepare for next iteration.
9183 lastOffset = suballoc.offset + suballoc.size;
9184 ++nextAlloc2ndIndex;
9185 }
9186 // We are at the end.
9187 else
9188 {
9189 if(lastOffset < freeSpace2ndTo1stEnd)
9190 {
9191 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9192 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9193 inoutStats.unusedSize += unusedRangeSize;
9194 ++inoutStats.unusedRangeCount;
9195 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9196 }
9197
9198 // End of loop.
9199 lastOffset = freeSpace2ndTo1stEnd;
9200 }
9201 }
9202 }
9203
9204 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9205 const VkDeviceSize freeSpace1stTo2ndEnd =
9206 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9207 while(lastOffset < freeSpace1stTo2ndEnd)
9208 {
9209 // Find next non-null allocation or move nextAllocIndex to the end.
9210 while(nextAlloc1stIndex < suballoc1stCount &&
9211 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9212 {
9213 ++nextAlloc1stIndex;
9214 }
9215
9216 // Found non-null allocation.
9217 if(nextAlloc1stIndex < suballoc1stCount)
9218 {
9219 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9220
9221 // 1. Process free space before this allocation.
9222 if(lastOffset < suballoc.offset)
9223 {
9224 // There is free space from lastOffset to suballoc.offset.
9225 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9226 inoutStats.unusedSize += unusedRangeSize;
9227 ++inoutStats.unusedRangeCount;
9228 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9229 }
9230
9231 // 2. Process this allocation.
9232 // There is allocation with suballoc.offset, suballoc.size.
9233 ++inoutStats.allocationCount;
9234
9235 // 3. Prepare for next iteration.
9236 lastOffset = suballoc.offset + suballoc.size;
9237 ++nextAlloc1stIndex;
9238 }
9239 // We are at the end.
9240 else
9241 {
9242 if(lastOffset < freeSpace1stTo2ndEnd)
9243 {
9244 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9245 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9246 inoutStats.unusedSize += unusedRangeSize;
9247 ++inoutStats.unusedRangeCount;
9248 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9249 }
9250
9251 // End of loop.
9252 lastOffset = freeSpace1stTo2ndEnd;
9253 }
9254 }
9255
9256 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9257 {
9258 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9259 while(lastOffset < size)
9260 {
9261 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9262 while(nextAlloc2ndIndex != SIZE_MAX &&
9263 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9264 {
9265 --nextAlloc2ndIndex;
9266 }
9267
9268 // Found non-null allocation.
9269 if(nextAlloc2ndIndex != SIZE_MAX)
9270 {
9271 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9272
9273 // 1. Process free space before this allocation.
9274 if(lastOffset < suballoc.offset)
9275 {
9276 // There is free space from lastOffset to suballoc.offset.
9277 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9278 inoutStats.unusedSize += unusedRangeSize;
9279 ++inoutStats.unusedRangeCount;
9280 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9281 }
9282
9283 // 2. Process this allocation.
9284 // There is allocation with suballoc.offset, suballoc.size.
9285 ++inoutStats.allocationCount;
9286
9287 // 3. Prepare for next iteration.
9288 lastOffset = suballoc.offset + suballoc.size;
9289 --nextAlloc2ndIndex;
9290 }
9291 // We are at the end.
9292 else
9293 {
9294 if(lastOffset < size)
9295 {
9296 // There is free space from lastOffset to size.
9297 const VkDeviceSize unusedRangeSize = size - lastOffset;
9298 inoutStats.unusedSize += unusedRangeSize;
9299 ++inoutStats.unusedRangeCount;
9300 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9301 }
9302
9303 // End of loop.
9304 lastOffset = size;
9305 }
9306 }
9307 }
9308}
9309
9310#if VMA_STATS_STRING_ENABLED
9311void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9312{
9313 const VkDeviceSize size = GetSize();
9314 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9315 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9316 const size_t suballoc1stCount = suballocations1st.size();
9317 const size_t suballoc2ndCount = suballocations2nd.size();
9318
9319 // FIRST PASS
9320
9321 size_t unusedRangeCount = 0;
9322 VkDeviceSize usedBytes = 0;
9323
9324 VkDeviceSize lastOffset = 0;
9325
9326 size_t alloc2ndCount = 0;
9327 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9328 {
9329 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9330 size_t nextAlloc2ndIndex = 0;
9331 while(lastOffset < freeSpace2ndTo1stEnd)
9332 {
9333 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9334 while(nextAlloc2ndIndex < suballoc2ndCount &&
9335 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9336 {
9337 ++nextAlloc2ndIndex;
9338 }
9339
9340 // Found non-null allocation.
9341 if(nextAlloc2ndIndex < suballoc2ndCount)
9342 {
9343 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9344
9345 // 1. Process free space before this allocation.
9346 if(lastOffset < suballoc.offset)
9347 {
9348 // There is free space from lastOffset to suballoc.offset.
9349 ++unusedRangeCount;
9350 }
9351
9352 // 2. Process this allocation.
9353 // There is allocation with suballoc.offset, suballoc.size.
9354 ++alloc2ndCount;
9355 usedBytes += suballoc.size;
9356
9357 // 3. Prepare for next iteration.
9358 lastOffset = suballoc.offset + suballoc.size;
9359 ++nextAlloc2ndIndex;
9360 }
9361 // We are at the end.
9362 else
9363 {
9364 if(lastOffset < freeSpace2ndTo1stEnd)
9365 {
9366 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9367 ++unusedRangeCount;
9368 }
9369
9370 // End of loop.
9371 lastOffset = freeSpace2ndTo1stEnd;
9372 }
9373 }
9374 }
9375
9376 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9377 size_t alloc1stCount = 0;
9378 const VkDeviceSize freeSpace1stTo2ndEnd =
9379 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9380 while(lastOffset < freeSpace1stTo2ndEnd)
9381 {
9382 // Find next non-null allocation or move nextAllocIndex to the end.
9383 while(nextAlloc1stIndex < suballoc1stCount &&
9384 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9385 {
9386 ++nextAlloc1stIndex;
9387 }
9388
9389 // Found non-null allocation.
9390 if(nextAlloc1stIndex < suballoc1stCount)
9391 {
9392 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9393
9394 // 1. Process free space before this allocation.
9395 if(lastOffset < suballoc.offset)
9396 {
9397 // There is free space from lastOffset to suballoc.offset.
9398 ++unusedRangeCount;
9399 }
9400
9401 // 2. Process this allocation.
9402 // There is allocation with suballoc.offset, suballoc.size.
9403 ++alloc1stCount;
9404 usedBytes += suballoc.size;
9405
9406 // 3. Prepare for next iteration.
9407 lastOffset = suballoc.offset + suballoc.size;
9408 ++nextAlloc1stIndex;
9409 }
9410 // We are at the end.
9411 else
9412 {
9413 if(lastOffset < size)
9414 {
9415 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9416 ++unusedRangeCount;
9417 }
9418
9419 // End of loop.
9420 lastOffset = freeSpace1stTo2ndEnd;
9421 }
9422 }
9423
9424 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9425 {
9426 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9427 while(lastOffset < size)
9428 {
9429 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9430 while(nextAlloc2ndIndex != SIZE_MAX &&
9431 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9432 {
9433 --nextAlloc2ndIndex;
9434 }
9435
9436 // Found non-null allocation.
9437 if(nextAlloc2ndIndex != SIZE_MAX)
9438 {
9439 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9440
9441 // 1. Process free space before this allocation.
9442 if(lastOffset < suballoc.offset)
9443 {
9444 // There is free space from lastOffset to suballoc.offset.
9445 ++unusedRangeCount;
9446 }
9447
9448 // 2. Process this allocation.
9449 // There is allocation with suballoc.offset, suballoc.size.
9450 ++alloc2ndCount;
9451 usedBytes += suballoc.size;
9452
9453 // 3. Prepare for next iteration.
9454 lastOffset = suballoc.offset + suballoc.size;
9455 --nextAlloc2ndIndex;
9456 }
9457 // We are at the end.
9458 else
9459 {
9460 if(lastOffset < size)
9461 {
9462 // There is free space from lastOffset to size.
9463 ++unusedRangeCount;
9464 }
9465
9466 // End of loop.
9467 lastOffset = size;
9468 }
9469 }
9470 }
9471
9472 const VkDeviceSize unusedBytes = size - usedBytes;
9473 PrintDetailedMap_Begin(json, unusedBytes, allocationCount: alloc1stCount + alloc2ndCount, unusedRangeCount);
9474
9475 // SECOND PASS
9476 lastOffset = 0;
9477
9478 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9479 {
9480 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9481 size_t nextAlloc2ndIndex = 0;
9482 while(lastOffset < freeSpace2ndTo1stEnd)
9483 {
9484 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9485 while(nextAlloc2ndIndex < suballoc2ndCount &&
9486 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9487 {
9488 ++nextAlloc2ndIndex;
9489 }
9490
9491 // Found non-null allocation.
9492 if(nextAlloc2ndIndex < suballoc2ndCount)
9493 {
9494 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9495
9496 // 1. Process free space before this allocation.
9497 if(lastOffset < suballoc.offset)
9498 {
9499 // There is free space from lastOffset to suballoc.offset.
9500 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9501 PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize);
9502 }
9503
9504 // 2. Process this allocation.
9505 // There is allocation with suballoc.offset, suballoc.size.
9506 PrintDetailedMap_Allocation(json, offset: suballoc.offset, hAllocation: suballoc.hAllocation);
9507
9508 // 3. Prepare for next iteration.
9509 lastOffset = suballoc.offset + suballoc.size;
9510 ++nextAlloc2ndIndex;
9511 }
9512 // We are at the end.
9513 else
9514 {
9515 if(lastOffset < freeSpace2ndTo1stEnd)
9516 {
9517 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9518 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9519 PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize);
9520 }
9521
9522 // End of loop.
9523 lastOffset = freeSpace2ndTo1stEnd;
9524 }
9525 }
9526 }
9527
9528 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9529 while(lastOffset < freeSpace1stTo2ndEnd)
9530 {
9531 // Find next non-null allocation or move nextAllocIndex to the end.
9532 while(nextAlloc1stIndex < suballoc1stCount &&
9533 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9534 {
9535 ++nextAlloc1stIndex;
9536 }
9537
9538 // Found non-null allocation.
9539 if(nextAlloc1stIndex < suballoc1stCount)
9540 {
9541 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9542
9543 // 1. Process free space before this allocation.
9544 if(lastOffset < suballoc.offset)
9545 {
9546 // There is free space from lastOffset to suballoc.offset.
9547 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9548 PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize);
9549 }
9550
9551 // 2. Process this allocation.
9552 // There is allocation with suballoc.offset, suballoc.size.
9553 PrintDetailedMap_Allocation(json, offset: suballoc.offset, hAllocation: suballoc.hAllocation);
9554
9555 // 3. Prepare for next iteration.
9556 lastOffset = suballoc.offset + suballoc.size;
9557 ++nextAlloc1stIndex;
9558 }
9559 // We are at the end.
9560 else
9561 {
9562 if(lastOffset < freeSpace1stTo2ndEnd)
9563 {
9564 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9565 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9566 PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize);
9567 }
9568
9569 // End of loop.
9570 lastOffset = freeSpace1stTo2ndEnd;
9571 }
9572 }
9573
9574 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9575 {
9576 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9577 while(lastOffset < size)
9578 {
9579 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9580 while(nextAlloc2ndIndex != SIZE_MAX &&
9581 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9582 {
9583 --nextAlloc2ndIndex;
9584 }
9585
9586 // Found non-null allocation.
9587 if(nextAlloc2ndIndex != SIZE_MAX)
9588 {
9589 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9590
9591 // 1. Process free space before this allocation.
9592 if(lastOffset < suballoc.offset)
9593 {
9594 // There is free space from lastOffset to suballoc.offset.
9595 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9596 PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize);
9597 }
9598
9599 // 2. Process this allocation.
9600 // There is allocation with suballoc.offset, suballoc.size.
9601 PrintDetailedMap_Allocation(json, offset: suballoc.offset, hAllocation: suballoc.hAllocation);
9602
9603 // 3. Prepare for next iteration.
9604 lastOffset = suballoc.offset + suballoc.size;
9605 --nextAlloc2ndIndex;
9606 }
9607 // We are at the end.
9608 else
9609 {
9610 if(lastOffset < size)
9611 {
9612 // There is free space from lastOffset to size.
9613 const VkDeviceSize unusedRangeSize = size - lastOffset;
9614 PrintDetailedMap_UnusedRange(json, offset: lastOffset, size: unusedRangeSize);
9615 }
9616
9617 // End of loop.
9618 lastOffset = size;
9619 }
9620 }
9621 }
9622
9623 PrintDetailedMap_End(json);
9624}
9625#endif // #if VMA_STATS_STRING_ENABLED
9626
9627bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9628 uint32_t currentFrameIndex,
9629 uint32_t frameInUseCount,
9630 VkDeviceSize bufferImageGranularity,
9631 VkDeviceSize allocSize,
9632 VkDeviceSize allocAlignment,
9633 bool upperAddress,
9634 VmaSuballocationType allocType,
9635 bool canMakeOtherLost,
9636 uint32_t /*strategy*/,
9637 VmaAllocationRequest* pAllocationRequest)
9638{
9639 VMA_ASSERT(allocSize > 0);
9640 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9641 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9642 VMA_HEAVY_ASSERT(Validate());
9643
9644 const VkDeviceSize size = GetSize();
9645 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9646 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9647
9648 if(upperAddress)
9649 {
9650 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9651 {
9652 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9653 return false;
9654 }
9655
9656 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9657 if(allocSize > size)
9658 {
9659 return false;
9660 }
9661 VkDeviceSize resultBaseOffset = size - allocSize;
9662 if(!suballocations2nd.empty())
9663 {
9664 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9665 resultBaseOffset = lastSuballoc.offset - allocSize;
9666 if(allocSize > lastSuballoc.offset)
9667 {
9668 return false;
9669 }
9670 }
9671
9672 // Start from offset equal to end of free space.
9673 VkDeviceSize resultOffset = resultBaseOffset;
9674
9675 // Apply VMA_DEBUG_MARGIN at the end.
9676 if(VMA_DEBUG_MARGIN > 0)
9677 {
9678#if VMA_DEBUG_MARGIN
9679 if(resultOffset < VMA_DEBUG_MARGIN)
9680 {
9681 return false;
9682 }
9683#endif
9684 resultOffset -= VMA_DEBUG_MARGIN;
9685 }
9686
9687 // Apply alignment.
9688 resultOffset = VmaAlignDown(val: resultOffset, align: allocAlignment);
9689
9690 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9691 // Make bigger alignment if necessary.
9692 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9693 {
9694 bool bufferImageGranularityConflict = false;
9695 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9696 {
9697 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9698 if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity))
9699 {
9700 if(VmaIsBufferImageGranularityConflict(suballocType1: nextSuballoc.type, suballocType2: allocType))
9701 {
9702 bufferImageGranularityConflict = true;
9703 break;
9704 }
9705 }
9706 else
9707 // Already on previous page.
9708 break;
9709 }
9710 if(bufferImageGranularityConflict)
9711 {
9712 resultOffset = VmaAlignDown(val: resultOffset, align: bufferImageGranularity);
9713 }
9714 }
9715
9716 // There is enough free space.
9717 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9718 suballocations1st.back().offset + suballocations1st.back().size :
9719 0;
9720 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9721 {
9722 // Check previous suballocations for BufferImageGranularity conflicts.
9723 // If conflict exists, allocation cannot be made here.
9724 if(bufferImageGranularity > 1)
9725 {
9726 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9727 {
9728 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9729 if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: resultOffset, pageSize: bufferImageGranularity))
9730 {
9731 if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: prevSuballoc.type))
9732 {
9733 return false;
9734 }
9735 }
9736 else
9737 {
9738 // Already on next page.
9739 break;
9740 }
9741 }
9742 }
9743
9744 // All tests passed: Success.
9745 pAllocationRequest->offset = resultOffset;
9746 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9747 pAllocationRequest->sumItemSize = 0;
9748 // pAllocationRequest->item unused.
9749 pAllocationRequest->itemsToMakeLostCount = 0;
9750 return true;
9751 }
9752 }
9753 else // !upperAddress
9754 {
9755 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9756 {
9757 // Try to allocate at the end of 1st vector.
9758
9759 VkDeviceSize resultBaseOffset = 0;
9760 if(!suballocations1st.empty())
9761 {
9762 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9763 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9764 }
9765
9766 // Start from offset equal to beginning of free space.
9767 VkDeviceSize resultOffset = resultBaseOffset;
9768
9769 // Apply VMA_DEBUG_MARGIN at the beginning.
9770 if(VMA_DEBUG_MARGIN > 0)
9771 {
9772 resultOffset += VMA_DEBUG_MARGIN;
9773 }
9774
9775 // Apply alignment.
9776 resultOffset = VmaAlignUp(val: resultOffset, align: allocAlignment);
9777
9778 // Check previous suballocations for BufferImageGranularity conflicts.
9779 // Make bigger alignment if necessary.
9780 if(bufferImageGranularity > 1 && !suballocations1st.empty())
9781 {
9782 bool bufferImageGranularityConflict = false;
9783 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9784 {
9785 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9786 if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: resultOffset, pageSize: bufferImageGranularity))
9787 {
9788 if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType))
9789 {
9790 bufferImageGranularityConflict = true;
9791 break;
9792 }
9793 }
9794 else
9795 // Already on previous page.
9796 break;
9797 }
9798 if(bufferImageGranularityConflict)
9799 {
9800 resultOffset = VmaAlignUp(val: resultOffset, align: bufferImageGranularity);
9801 }
9802 }
9803
9804 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9805 suballocations2nd.back().offset : size;
9806
9807 // There is enough free space at the end after alignment.
9808 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9809 {
9810 // Check next suballocations for BufferImageGranularity conflicts.
9811 // If conflict exists, allocation cannot be made here.
9812 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9813 {
9814 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9815 {
9816 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9817 if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity))
9818 {
9819 if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type))
9820 {
9821 return false;
9822 }
9823 }
9824 else
9825 {
9826 // Already on previous page.
9827 break;
9828 }
9829 }
9830 }
9831
9832 // All tests passed: Success.
9833 pAllocationRequest->offset = resultOffset;
9834 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9835 pAllocationRequest->sumItemSize = 0;
9836 // pAllocationRequest->item unused.
9837 pAllocationRequest->itemsToMakeLostCount = 0;
9838 return true;
9839 }
9840 }
9841
9842 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9843 // beginning of 1st vector as the end of free space.
9844 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9845 {
9846 VMA_ASSERT(!suballocations1st.empty());
9847
9848 VkDeviceSize resultBaseOffset = 0;
9849 if(!suballocations2nd.empty())
9850 {
9851 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9852 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9853 }
9854
9855 // Start from offset equal to beginning of free space.
9856 VkDeviceSize resultOffset = resultBaseOffset;
9857
9858 // Apply VMA_DEBUG_MARGIN at the beginning.
9859 if(VMA_DEBUG_MARGIN > 0)
9860 {
9861 resultOffset += VMA_DEBUG_MARGIN;
9862 }
9863
9864 // Apply alignment.
9865 resultOffset = VmaAlignUp(val: resultOffset, align: allocAlignment);
9866
9867 // Check previous suballocations for BufferImageGranularity conflicts.
9868 // Make bigger alignment if necessary.
9869 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9870 {
9871 bool bufferImageGranularityConflict = false;
9872 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9873 {
9874 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9875 if(VmaBlocksOnSamePage(resourceAOffset: prevSuballoc.offset, resourceASize: prevSuballoc.size, resourceBOffset: resultOffset, pageSize: bufferImageGranularity))
9876 {
9877 if(VmaIsBufferImageGranularityConflict(suballocType1: prevSuballoc.type, suballocType2: allocType))
9878 {
9879 bufferImageGranularityConflict = true;
9880 break;
9881 }
9882 }
9883 else
9884 // Already on previous page.
9885 break;
9886 }
9887 if(bufferImageGranularityConflict)
9888 {
9889 resultOffset = VmaAlignUp(val: resultOffset, align: bufferImageGranularity);
9890 }
9891 }
9892
9893 pAllocationRequest->itemsToMakeLostCount = 0;
9894 pAllocationRequest->sumItemSize = 0;
9895 size_t index1st = m_1stNullItemsBeginCount;
9896
9897 if(canMakeOtherLost)
9898 {
9899 while(index1st < suballocations1st.size() &&
9900 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9901 {
9902 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9903 const VmaSuballocation& suballoc = suballocations1st[index1st];
9904 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9905 {
9906 // No problem.
9907 }
9908 else
9909 {
9910 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
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 ++index1st;
9923 }
9924
9925 // Check next suballocations for BufferImageGranularity conflicts.
9926 // If conflict exists, we must mark more allocations lost or fail.
9927 if(bufferImageGranularity > 1)
9928 {
9929 while(index1st < suballocations1st.size())
9930 {
9931 const VmaSuballocation& suballoc = suballocations1st[index1st];
9932 if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: suballoc.offset, pageSize: bufferImageGranularity))
9933 {
9934 if(suballoc.hAllocation != VK_NULL_HANDLE)
9935 {
9936 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9937 if(suballoc.hAllocation->CanBecomeLost() &&
9938 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9939 {
9940 ++pAllocationRequest->itemsToMakeLostCount;
9941 pAllocationRequest->sumItemSize += suballoc.size;
9942 }
9943 else
9944 {
9945 return false;
9946 }
9947 }
9948 }
9949 else
9950 {
9951 // Already on next page.
9952 break;
9953 }
9954 ++index1st;
9955 }
9956 }
9957 }
9958
9959 // There is enough free space at the end after alignment.
9960 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9961 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9962 {
9963 // Check next suballocations for BufferImageGranularity conflicts.
9964 // If conflict exists, allocation cannot be made here.
9965 if(bufferImageGranularity > 1)
9966 {
9967 for(size_t nextSuballocIndex = index1st;
9968 nextSuballocIndex < suballocations1st.size();
9969 nextSuballocIndex++)
9970 {
9971 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9972 if(VmaBlocksOnSamePage(resourceAOffset: resultOffset, resourceASize: allocSize, resourceBOffset: nextSuballoc.offset, pageSize: bufferImageGranularity))
9973 {
9974 if(VmaIsBufferImageGranularityConflict(suballocType1: allocType, suballocType2: nextSuballoc.type))
9975 {
9976 return false;
9977 }
9978 }
9979 else
9980 {
9981 // Already on next page.
9982 break;
9983 }
9984 }
9985 }
9986
9987 // All tests passed: Success.
9988 pAllocationRequest->offset = resultOffset;
9989 pAllocationRequest->sumFreeSize =
9990 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
9991 - resultBaseOffset
9992 - pAllocationRequest->sumItemSize;
9993 // pAllocationRequest->item unused.
9994 return true;
9995 }
9996 }
9997 }
9998
9999 return false;
10000}
10001
10002bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10003 uint32_t currentFrameIndex,
10004 uint32_t frameInUseCount,
10005 VmaAllocationRequest* pAllocationRequest)
10006{
10007 if(pAllocationRequest->itemsToMakeLostCount == 0)
10008 {
10009 return true;
10010 }
10011
10012 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10013
10014 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10015 size_t index1st = m_1stNullItemsBeginCount;
10016 size_t madeLostCount = 0;
10017 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10018 {
10019 VMA_ASSERT(index1st < suballocations1st.size());
10020 VmaSuballocation& suballoc = suballocations1st[index1st];
10021 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10022 {
10023 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10024 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10025 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10026 {
10027 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10028 suballoc.hAllocation = VK_NULL_HANDLE;
10029 m_SumFreeSize += suballoc.size;
10030 ++m_1stNullItemsMiddleCount;
10031 ++madeLostCount;
10032 }
10033 else
10034 {
10035 return false;
10036 }
10037 }
10038 ++index1st;
10039 }
10040
10041 CleanupAfterFree();
10042 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10043
10044 return true;
10045}
10046
10047uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10048{
10049 uint32_t lostAllocationCount = 0;
10050
10051 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10052 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10053 {
10054 VmaSuballocation& suballoc = suballocations1st[i];
10055 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10056 suballoc.hAllocation->CanBecomeLost() &&
10057 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10058 {
10059 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10060 suballoc.hAllocation = VK_NULL_HANDLE;
10061 ++m_1stNullItemsMiddleCount;
10062 m_SumFreeSize += suballoc.size;
10063 ++lostAllocationCount;
10064 }
10065 }
10066
10067 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10068 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10069 {
10070 VmaSuballocation& suballoc = suballocations2nd[i];
10071 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10072 suballoc.hAllocation->CanBecomeLost() &&
10073 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10074 {
10075 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10076 suballoc.hAllocation = VK_NULL_HANDLE;
10077 ++m_2ndNullItemsCount;
10078 ++lostAllocationCount;
10079 }
10080 }
10081
10082 if(lostAllocationCount)
10083 {
10084 CleanupAfterFree();
10085 }
10086
10087 return lostAllocationCount;
10088}
10089
10090VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10091{
10092 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10093 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10094 {
10095 const VmaSuballocation& suballoc = suballocations1st[i];
10096 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10097 {
10098 if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset - VMA_DEBUG_MARGIN))
10099 {
10100 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10101 return VK_ERROR_VALIDATION_FAILED_EXT;
10102 }
10103 if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset + suballoc.size))
10104 {
10105 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10106 return VK_ERROR_VALIDATION_FAILED_EXT;
10107 }
10108 }
10109 }
10110
10111 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10112 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10113 {
10114 const VmaSuballocation& suballoc = suballocations2nd[i];
10115 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10116 {
10117 if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset - VMA_DEBUG_MARGIN))
10118 {
10119 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10120 return VK_ERROR_VALIDATION_FAILED_EXT;
10121 }
10122 if(!VmaValidateMagicValue(pData: pBlockData, offset: suballoc.offset + suballoc.size))
10123 {
10124 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10125 return VK_ERROR_VALIDATION_FAILED_EXT;
10126 }
10127 }
10128 }
10129
10130 return VK_SUCCESS;
10131}
10132
10133void VmaBlockMetadata_Linear::Alloc(
10134 const VmaAllocationRequest& request,
10135 VmaSuballocationType type,
10136 VkDeviceSize allocSize,
10137 bool upperAddress,
10138 VmaAllocation hAllocation)
10139{
10140 const VmaSuballocation newSuballoc = { .offset: request.offset, .size: allocSize, .hAllocation: hAllocation, .type: type };
10141
10142 if(upperAddress)
10143 {
10144 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10145 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10146 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10147 suballocations2nd.push_back(src: newSuballoc);
10148 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10149 }
10150 else
10151 {
10152 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10153
10154 // First allocation.
10155 if(suballocations1st.empty())
10156 {
10157 suballocations1st.push_back(src: newSuballoc);
10158 }
10159 else
10160 {
10161 // New allocation at the end of 1st vector.
10162 if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10163 {
10164 // Check if it fits before the end of the block.
10165 VMA_ASSERT(request.offset + allocSize <= GetSize());
10166 suballocations1st.push_back(src: newSuballoc);
10167 }
10168 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10169 else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10170 {
10171 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10172
10173 switch(m_2ndVectorMode)
10174 {
10175 case SECOND_VECTOR_EMPTY:
10176 // First allocation from second part ring buffer.
10177 VMA_ASSERT(suballocations2nd.empty());
10178 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10179 break;
10180 case SECOND_VECTOR_RING_BUFFER:
10181 // 2-part ring buffer is already started.
10182 VMA_ASSERT(!suballocations2nd.empty());
10183 break;
10184 case SECOND_VECTOR_DOUBLE_STACK:
10185 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10186 break;
10187 default:
10188 VMA_ASSERT(0);
10189 }
10190
10191 suballocations2nd.push_back(src: newSuballoc);
10192 }
10193 else
10194 {
10195 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10196 }
10197 }
10198 }
10199
10200 m_SumFreeSize -= newSuballoc.size;
10201}
10202
10203void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10204{
10205 FreeAtOffset(offset: allocation->GetOffset());
10206}
10207
10208void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10209{
10210 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10211 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10212
10213 if(!suballocations1st.empty())
10214 {
10215 // First allocation: Mark it as next empty at the beginning.
10216 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10217 if(firstSuballoc.offset == offset)
10218 {
10219 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10220 firstSuballoc.hAllocation = VK_NULL_HANDLE;
10221 m_SumFreeSize += firstSuballoc.size;
10222 ++m_1stNullItemsBeginCount;
10223 CleanupAfterFree();
10224 return;
10225 }
10226 }
10227
10228 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10229 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10230 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10231 {
10232 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10233 if(lastSuballoc.offset == offset)
10234 {
10235 m_SumFreeSize += lastSuballoc.size;
10236 suballocations2nd.pop_back();
10237 CleanupAfterFree();
10238 return;
10239 }
10240 }
10241 // Last allocation in 1st vector.
10242 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10243 {
10244 VmaSuballocation& lastSuballoc = suballocations1st.back();
10245 if(lastSuballoc.offset == offset)
10246 {
10247 m_SumFreeSize += lastSuballoc.size;
10248 suballocations1st.pop_back();
10249 CleanupAfterFree();
10250 return;
10251 }
10252 }
10253
10254 // Item from the middle of 1st vector.
10255 {
10256 VmaSuballocation refSuballoc;
10257 refSuballoc.offset = offset;
10258 // Rest of members stays uninitialized intentionally for better performance.
10259 SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10260 beg: suballocations1st.begin() + m_1stNullItemsBeginCount,
10261 end: suballocations1st.end(),
10262 value: refSuballoc);
10263 if(it != suballocations1st.end())
10264 {
10265 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10266 it->hAllocation = VK_NULL_HANDLE;
10267 ++m_1stNullItemsMiddleCount;
10268 m_SumFreeSize += it->size;
10269 CleanupAfterFree();
10270 return;
10271 }
10272 }
10273
10274 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10275 {
10276 // Item from the middle of 2nd vector.
10277 VmaSuballocation refSuballoc;
10278 refSuballoc.offset = offset;
10279 // Rest of members stays uninitialized intentionally for better performance.
10280 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10281 VmaVectorFindSorted<VmaSuballocationOffsetLess>(beg: suballocations2nd.begin(), end: suballocations2nd.end(), value: refSuballoc) :
10282 VmaVectorFindSorted<VmaSuballocationOffsetGreater>(beg: suballocations2nd.begin(), end: suballocations2nd.end(), value: refSuballoc);
10283 if(it != suballocations2nd.end())
10284 {
10285 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10286 it->hAllocation = VK_NULL_HANDLE;
10287 ++m_2ndNullItemsCount;
10288 m_SumFreeSize += it->size;
10289 CleanupAfterFree();
10290 return;
10291 }
10292 }
10293
10294 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10295}
10296
10297bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10298{
10299 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10300 const size_t suballocCount = AccessSuballocations1st().size();
10301 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10302}
10303
10304void VmaBlockMetadata_Linear::CleanupAfterFree()
10305{
10306 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10307 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10308
10309 if(IsEmpty())
10310 {
10311 suballocations1st.clear();
10312 suballocations2nd.clear();
10313 m_1stNullItemsBeginCount = 0;
10314 m_1stNullItemsMiddleCount = 0;
10315 m_2ndNullItemsCount = 0;
10316 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10317 }
10318 else
10319 {
10320 const size_t suballoc1stCount = suballocations1st.size();
10321 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10322 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10323
10324 // Find more null items at the beginning of 1st vector.
10325 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10326 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10327 {
10328 ++m_1stNullItemsBeginCount;
10329 --m_1stNullItemsMiddleCount;
10330 }
10331
10332 // Find more null items at the end of 1st vector.
10333 while(m_1stNullItemsMiddleCount > 0 &&
10334 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10335 {
10336 --m_1stNullItemsMiddleCount;
10337 suballocations1st.pop_back();
10338 }
10339
10340 // Find more null items at the end of 2nd vector.
10341 while(m_2ndNullItemsCount > 0 &&
10342 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10343 {
10344 --m_2ndNullItemsCount;
10345 suballocations2nd.pop_back();
10346 }
10347
10348 if(ShouldCompact1st())
10349 {
10350 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10351 size_t srcIndex = m_1stNullItemsBeginCount;
10352 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10353 {
10354 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10355 {
10356 ++srcIndex;
10357 }
10358 if(dstIndex != srcIndex)
10359 {
10360 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10361 }
10362 ++srcIndex;
10363 }
10364 suballocations1st.resize(newCount: nonNullItemCount);
10365 m_1stNullItemsBeginCount = 0;
10366 m_1stNullItemsMiddleCount = 0;
10367 }
10368
10369 // 2nd vector became empty.
10370 if(suballocations2nd.empty())
10371 {
10372 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10373 }
10374
10375 // 1st vector became empty.
10376 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10377 {
10378 suballocations1st.clear();
10379 m_1stNullItemsBeginCount = 0;
10380
10381 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10382 {
10383 // Swap 1st with 2nd. Now 2nd is empty.
10384 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10385 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10386 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10387 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10388 {
10389 ++m_1stNullItemsBeginCount;
10390 --m_1stNullItemsMiddleCount;
10391 }
10392 m_2ndNullItemsCount = 0;
10393 m_1stVectorIndex ^= 1;
10394 }
10395 }
10396 }
10397
10398 VMA_HEAVY_ASSERT(Validate());
10399}
10400
10401
10402////////////////////////////////////////////////////////////////////////////////
10403// class VmaBlockMetadata_Buddy
10404
10405VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10406 VmaBlockMetadata(hAllocator),
10407 m_Root(VMA_NULL),
10408 m_AllocationCount(0),
10409 m_FreeCount(1),
10410 m_SumFreeSize(0)
10411{
10412 memset(s: m_FreeList, c: 0, n: sizeof(m_FreeList));
10413}
10414
10415VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10416{
10417 DeleteNode(node: m_Root);
10418}
10419
10420void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10421{
10422 VmaBlockMetadata::Init(size);
10423
10424 m_UsableSize = VmaPrevPow2(v: size);
10425 m_SumFreeSize = m_UsableSize;
10426
10427 // Calculate m_LevelCount.
10428 m_LevelCount = 1;
10429 while(m_LevelCount < MAX_LEVELS &&
10430 LevelToNodeSize(level: m_LevelCount) >= MIN_NODE_SIZE)
10431 {
10432 ++m_LevelCount;
10433 }
10434
10435 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10436 rootNode->offset = 0;
10437 rootNode->type = Node::TYPE_FREE;
10438 rootNode->parent = VMA_NULL;
10439 rootNode->buddy = VMA_NULL;
10440
10441 m_Root = rootNode;
10442 AddToFreeListFront(level: 0, node: rootNode);
10443}
10444
10445bool VmaBlockMetadata_Buddy::Validate() const
10446{
10447 // Validate tree.
10448 ValidationContext ctx;
10449 if(!ValidateNode(ctx, VMA_NULL, curr: m_Root, level: 0, levelNodeSize: LevelToNodeSize(level: 0)))
10450 {
10451 VMA_VALIDATE(false && "ValidateNode failed.");
10452 }
10453 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10454 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10455
10456 // Validate free node lists.
10457 for(uint32_t level = 0; level < m_LevelCount; ++level)
10458 {
10459 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10460 m_FreeList[level].front->free.prev == VMA_NULL);
10461
10462 for(Node* node = m_FreeList[level].front;
10463 node != VMA_NULL;
10464 node = node->free.next)
10465 {
10466 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10467
10468 if(node->free.next == VMA_NULL)
10469 {
10470 VMA_VALIDATE(m_FreeList[level].back == node);
10471 }
10472 else
10473 {
10474 VMA_VALIDATE(node->free.next->free.prev == node);
10475 }
10476 }
10477 }
10478
10479 // Validate that free lists ar higher levels are empty.
10480 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10481 {
10482 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10483 }
10484
10485 return true;
10486}
10487
10488VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10489{
10490 for(uint32_t level = 0; level < m_LevelCount; ++level)
10491 {
10492 if(m_FreeList[level].front != VMA_NULL)
10493 {
10494 return LevelToNodeSize(level);
10495 }
10496 }
10497 return 0;
10498}
10499
10500void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10501{
10502 const VkDeviceSize unusableSize = GetUnusableSize();
10503
10504 outInfo.blockCount = 1;
10505
10506 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10507 outInfo.usedBytes = outInfo.unusedBytes = 0;
10508
10509 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10510 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10511 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10512
10513 CalcAllocationStatInfoNode(outInfo, node: m_Root, levelNodeSize: LevelToNodeSize(level: 0));
10514
10515 if(unusableSize > 0)
10516 {
10517 ++outInfo.unusedRangeCount;
10518 outInfo.unusedBytes += unusableSize;
10519 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10520 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10521 }
10522}
10523
10524void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10525{
10526 const VkDeviceSize unusableSize = GetUnusableSize();
10527
10528 inoutStats.size += GetSize();
10529 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10530 inoutStats.allocationCount += m_AllocationCount;
10531 inoutStats.unusedRangeCount += m_FreeCount;
10532 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10533
10534 if(unusableSize > 0)
10535 {
10536 ++inoutStats.unusedRangeCount;
10537 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10538 }
10539}
10540
10541#if VMA_STATS_STRING_ENABLED
10542
10543void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10544{
10545 // TODO optimize
10546 VmaStatInfo stat;
10547 CalcAllocationStatInfo(outInfo&: stat);
10548
10549 PrintDetailedMap_Begin(
10550 json,
10551 unusedBytes: stat.unusedBytes,
10552 allocationCount: stat.allocationCount,
10553 unusedRangeCount: stat.unusedRangeCount);
10554
10555 PrintDetailedMapNode(json, node: m_Root, levelNodeSize: LevelToNodeSize(level: 0));
10556
10557 const VkDeviceSize unusableSize = GetUnusableSize();
10558 if(unusableSize > 0)
10559 {
10560 PrintDetailedMap_UnusedRange(json,
10561 offset: m_UsableSize, // offset
10562 size: unusableSize); // size
10563 }
10564
10565 PrintDetailedMap_End(json);
10566}
10567
10568#endif // #if VMA_STATS_STRING_ENABLED
10569
10570bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10571 uint32_t /*currentFrameIndex*/,
10572 uint32_t /*frameInUseCount*/,
10573 VkDeviceSize bufferImageGranularity,
10574 VkDeviceSize allocSize,
10575 VkDeviceSize allocAlignment,
10576 bool upperAddress,
10577 VmaSuballocationType allocType,
10578 bool /*canMakeOtherLost*/,
10579 uint32_t /*strategy*/,
10580 VmaAllocationRequest* pAllocationRequest)
10581{
10582 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10583 (void) upperAddress;
10584
10585 // Simple way to respect bufferImageGranularity. May be optimized some day.
10586 // Whenever it might be an OPTIMAL image...
10587 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10588 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10589 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10590 {
10591 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10592 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10593 }
10594
10595 if(allocSize > m_UsableSize)
10596 {
10597 return false;
10598 }
10599
10600 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10601 for(uint32_t level = targetLevel + 1; level--; )
10602 {
10603 for(Node* freeNode = m_FreeList[level].front;
10604 freeNode != VMA_NULL;
10605 freeNode = freeNode->free.next)
10606 {
10607 if(freeNode->offset % allocAlignment == 0)
10608 {
10609 pAllocationRequest->offset = freeNode->offset;
10610 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10611 pAllocationRequest->sumItemSize = 0;
10612 pAllocationRequest->itemsToMakeLostCount = 0;
10613 pAllocationRequest->customData = (void*)(uintptr_t)level;
10614 return true;
10615 }
10616 }
10617 }
10618
10619 return false;
10620}
10621
10622bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10623 uint32_t /*currentFrameIndex*/,
10624 uint32_t /*frameInUseCount*/,
10625 VmaAllocationRequest* pAllocationRequest)
10626{
10627 /*
10628 Lost allocations are not supported in buddy allocator at the moment.
10629 Support might be added in the future.
10630 */
10631 return pAllocationRequest->itemsToMakeLostCount == 0;
10632}
10633
10634uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/)
10635{
10636 /*
10637 Lost allocations are not supported in buddy allocator at the moment.
10638 Support might be added in the future.
10639 */
10640 return 0;
10641}
10642
10643void VmaBlockMetadata_Buddy::Alloc(
10644 const VmaAllocationRequest& request,
10645 VmaSuballocationType /*type*/,
10646 VkDeviceSize allocSize,
10647 bool /*upperAddress*/,
10648 VmaAllocation hAllocation)
10649{
10650 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10651 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10652
10653 Node* currNode = m_FreeList[currLevel].front;
10654 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10655 while(currNode->offset != request.offset)
10656 {
10657 currNode = currNode->free.next;
10658 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10659 }
10660
10661 // Go down, splitting free nodes.
10662 while(currLevel < targetLevel)
10663 {
10664 // currNode is already first free node at currLevel.
10665 // Remove it from list of free nodes at this currLevel.
10666 RemoveFromFreeList(level: currLevel, node: currNode);
10667
10668 const uint32_t childrenLevel = currLevel + 1;
10669
10670 // Create two free sub-nodes.
10671 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10672 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10673
10674 leftChild->offset = currNode->offset;
10675 leftChild->type = Node::TYPE_FREE;
10676 leftChild->parent = currNode;
10677 leftChild->buddy = rightChild;
10678
10679 rightChild->offset = currNode->offset + LevelToNodeSize(level: childrenLevel);
10680 rightChild->type = Node::TYPE_FREE;
10681 rightChild->parent = currNode;
10682 rightChild->buddy = leftChild;
10683
10684 // Convert current currNode to split type.
10685 currNode->type = Node::TYPE_SPLIT;
10686 currNode->split.leftChild = leftChild;
10687
10688 // Add child nodes to free list. Order is important!
10689 AddToFreeListFront(level: childrenLevel, node: rightChild);
10690 AddToFreeListFront(level: childrenLevel, node: leftChild);
10691
10692 ++m_FreeCount;
10693 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10694 ++currLevel;
10695 currNode = m_FreeList[currLevel].front;
10696
10697 /*
10698 We can be sure that currNode, as left child of node previously split,
10699 also fullfills the alignment requirement.
10700 */
10701 }
10702
10703 // Remove from free list.
10704 VMA_ASSERT(currLevel == targetLevel &&
10705 currNode != VMA_NULL &&
10706 currNode->type == Node::TYPE_FREE);
10707 RemoveFromFreeList(level: currLevel, node: currNode);
10708
10709 // Convert to allocation node.
10710 currNode->type = Node::TYPE_ALLOCATION;
10711 currNode->allocation.alloc = hAllocation;
10712
10713 ++m_AllocationCount;
10714 --m_FreeCount;
10715 m_SumFreeSize -= allocSize;
10716}
10717
10718void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10719{
10720 if(node->type == Node::TYPE_SPLIT)
10721 {
10722 DeleteNode(node: node->split.leftChild->buddy);
10723 DeleteNode(node: node->split.leftChild);
10724 }
10725
10726 vma_delete(pAllocationCallbacks: GetAllocationCallbacks(), ptr: node);
10727}
10728
10729bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10730{
10731 VMA_VALIDATE(level < m_LevelCount);
10732 VMA_VALIDATE(curr->parent == parent);
10733 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10734 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10735 switch(curr->type)
10736 {
10737 case Node::TYPE_FREE:
10738 // curr->free.prev, next are validated separately.
10739 ctx.calculatedSumFreeSize += levelNodeSize;
10740 ++ctx.calculatedFreeCount;
10741 break;
10742 case Node::TYPE_ALLOCATION:
10743 ++ctx.calculatedAllocationCount;
10744 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10745 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10746 break;
10747 case Node::TYPE_SPLIT:
10748 {
10749 const uint32_t childrenLevel = level + 1;
10750 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10751 const Node* const leftChild = curr->split.leftChild;
10752 VMA_VALIDATE(leftChild != VMA_NULL);
10753 VMA_VALIDATE(leftChild->offset == curr->offset);
10754 if(!ValidateNode(ctx, parent: curr, curr: leftChild, level: childrenLevel, levelNodeSize: childrenLevelNodeSize))
10755 {
10756 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10757 }
10758 const Node* const rightChild = leftChild->buddy;
10759 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10760 if(!ValidateNode(ctx, parent: curr, curr: rightChild, level: childrenLevel, levelNodeSize: childrenLevelNodeSize))
10761 {
10762 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10763 }
10764 }
10765 break;
10766 default:
10767 return false;
10768 }
10769
10770 return true;
10771}
10772
10773uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10774{
10775 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10776 uint32_t level = 0;
10777 VkDeviceSize currLevelNodeSize = m_UsableSize;
10778 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10779 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10780 {
10781 ++level;
10782 currLevelNodeSize = nextLevelNodeSize;
10783 nextLevelNodeSize = currLevelNodeSize >> 1;
10784 }
10785 return level;
10786}
10787
10788void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10789{
10790 // Find node and level.
10791 Node* node = m_Root;
10792 VkDeviceSize nodeOffset = 0;
10793 uint32_t level = 0;
10794 VkDeviceSize levelNodeSize = LevelToNodeSize(level: 0);
10795 while(node->type == Node::TYPE_SPLIT)
10796 {
10797 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10798 if(offset < nodeOffset + nextLevelSize)
10799 {
10800 node = node->split.leftChild;
10801 }
10802 else
10803 {
10804 node = node->split.leftChild->buddy;
10805 nodeOffset += nextLevelSize;
10806 }
10807 ++level;
10808 levelNodeSize = nextLevelSize;
10809 }
10810
10811 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10812 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10813
10814 ++m_FreeCount;
10815 --m_AllocationCount;
10816 m_SumFreeSize += alloc->GetSize();
10817
10818 node->type = Node::TYPE_FREE;
10819
10820 // Join free nodes if possible.
10821 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10822 {
10823 RemoveFromFreeList(level, node: node->buddy);
10824 Node* const parent = node->parent;
10825
10826 vma_delete(pAllocationCallbacks: GetAllocationCallbacks(), ptr: node->buddy);
10827 vma_delete(pAllocationCallbacks: GetAllocationCallbacks(), ptr: node);
10828 parent->type = Node::TYPE_FREE;
10829
10830 node = parent;
10831 --level;
10832 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10833 --m_FreeCount;
10834 }
10835
10836 AddToFreeListFront(level, node);
10837}
10838
10839void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10840{
10841 switch(node->type)
10842 {
10843 case Node::TYPE_FREE:
10844 ++outInfo.unusedRangeCount;
10845 outInfo.unusedBytes += levelNodeSize;
10846 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10847 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10848 break;
10849 case Node::TYPE_ALLOCATION:
10850 {
10851 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10852 ++outInfo.allocationCount;
10853 outInfo.usedBytes += allocSize;
10854 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10855 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10856
10857 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10858 if(unusedRangeSize > 0)
10859 {
10860 ++outInfo.unusedRangeCount;
10861 outInfo.unusedBytes += unusedRangeSize;
10862 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10863 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10864 }
10865 }
10866 break;
10867 case Node::TYPE_SPLIT:
10868 {
10869 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10870 const Node* const leftChild = node->split.leftChild;
10871 CalcAllocationStatInfoNode(outInfo, node: leftChild, levelNodeSize: childrenNodeSize);
10872 const Node* const rightChild = leftChild->buddy;
10873 CalcAllocationStatInfoNode(outInfo, node: rightChild, levelNodeSize: childrenNodeSize);
10874 }
10875 break;
10876 default:
10877 VMA_ASSERT(0);
10878 }
10879}
10880
10881void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10882{
10883 VMA_ASSERT(node->type == Node::TYPE_FREE);
10884
10885 // List is empty.
10886 Node* const frontNode = m_FreeList[level].front;
10887 if(frontNode == VMA_NULL)
10888 {
10889 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10890 node->free.prev = node->free.next = VMA_NULL;
10891 m_FreeList[level].front = m_FreeList[level].back = node;
10892 }
10893 else
10894 {
10895 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10896 node->free.prev = VMA_NULL;
10897 node->free.next = frontNode;
10898 frontNode->free.prev = node;
10899 m_FreeList[level].front = node;
10900 }
10901}
10902
10903void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10904{
10905 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10906
10907 // It is at the front.
10908 if(node->free.prev == VMA_NULL)
10909 {
10910 VMA_ASSERT(m_FreeList[level].front == node);
10911 m_FreeList[level].front = node->free.next;
10912 }
10913 else
10914 {
10915 Node* const prevFreeNode = node->free.prev;
10916 VMA_ASSERT(prevFreeNode->free.next == node);
10917 prevFreeNode->free.next = node->free.next;
10918 }
10919
10920 // It is at the back.
10921 if(node->free.next == VMA_NULL)
10922 {
10923 VMA_ASSERT(m_FreeList[level].back == node);
10924 m_FreeList[level].back = node->free.prev;
10925 }
10926 else
10927 {
10928 Node* const nextFreeNode = node->free.next;
10929 VMA_ASSERT(nextFreeNode->free.prev == node);
10930 nextFreeNode->free.prev = node->free.prev;
10931 }
10932}
10933
10934#if VMA_STATS_STRING_ENABLED
10935void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10936{
10937 switch(node->type)
10938 {
10939 case Node::TYPE_FREE:
10940 PrintDetailedMap_UnusedRange(json, offset: node->offset, size: levelNodeSize);
10941 break;
10942 case Node::TYPE_ALLOCATION:
10943 {
10944 PrintDetailedMap_Allocation(json, offset: node->offset, hAllocation: node->allocation.alloc);
10945 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10946 if(allocSize < levelNodeSize)
10947 {
10948 PrintDetailedMap_UnusedRange(json, offset: node->offset + allocSize, size: levelNodeSize - allocSize);
10949 }
10950 }
10951 break;
10952 case Node::TYPE_SPLIT:
10953 {
10954 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10955 const Node* const leftChild = node->split.leftChild;
10956 PrintDetailedMapNode(json, node: leftChild, levelNodeSize: childrenNodeSize);
10957 const Node* const rightChild = leftChild->buddy;
10958 PrintDetailedMapNode(json, node: rightChild, levelNodeSize: childrenNodeSize);
10959 }
10960 break;
10961 default:
10962 VMA_ASSERT(0);
10963 }
10964}
10965#endif // #if VMA_STATS_STRING_ENABLED
10966
10967
10968////////////////////////////////////////////////////////////////////////////////
10969// class VmaDeviceMemoryBlock
10970
10971VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) :
10972 m_pMetadata(VMA_NULL),
10973 m_MemoryTypeIndex(UINT32_MAX),
10974 m_Id(0),
10975 m_hMemory(VK_NULL_HANDLE),
10976 m_MapCount(0),
10977 m_pMappedData(VMA_NULL)
10978{
10979}
10980
10981void VmaDeviceMemoryBlock::Init(
10982 VmaAllocator hAllocator,
10983 uint32_t newMemoryTypeIndex,
10984 VkDeviceMemory newMemory,
10985 VkDeviceSize newSize,
10986 uint32_t id,
10987 uint32_t algorithm)
10988{
10989 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
10990
10991 m_MemoryTypeIndex = newMemoryTypeIndex;
10992 m_Id = id;
10993 m_hMemory = newMemory;
10994
10995 switch(algorithm)
10996 {
10997 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
10998 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
10999 break;
11000 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11001 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
11002 break;
11003 default:
11004 VMA_ASSERT(0);
11005 // Fall-through.
11006 case 0:
11007 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
11008 }
11009 m_pMetadata->Init(size: newSize);
11010}
11011
11012void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11013{
11014 // This is the most important assert in the entire library.
11015 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11016 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11017
11018 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11019 allocator->FreeVulkanMemory(memoryType: m_MemoryTypeIndex, size: m_pMetadata->GetSize(), hMemory: m_hMemory);
11020 m_hMemory = VK_NULL_HANDLE;
11021
11022 vma_delete(hAllocator: allocator, ptr: m_pMetadata);
11023 m_pMetadata = VMA_NULL;
11024}
11025
11026bool VmaDeviceMemoryBlock::Validate() const
11027{
11028 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11029 (m_pMetadata->GetSize() != 0));
11030
11031 return m_pMetadata->Validate();
11032}
11033
11034VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11035{
11036 void* pData = nullptr;
11037 VkResult res = Map(hAllocator, count: 1, ppData: &pData);
11038 if(res != VK_SUCCESS)
11039 {
11040 return res;
11041 }
11042
11043 res = m_pMetadata->CheckCorruption(pBlockData: pData);
11044
11045 Unmap(hAllocator, count: 1);
11046
11047 return res;
11048}
11049
11050VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11051{
11052 if(count == 0)
11053 {
11054 return VK_SUCCESS;
11055 }
11056
11057 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11058 if(m_MapCount != 0)
11059 {
11060 m_MapCount += count;
11061 VMA_ASSERT(m_pMappedData != VMA_NULL);
11062 if(ppData != VMA_NULL)
11063 {
11064 *ppData = m_pMappedData;
11065 }
11066 return VK_SUCCESS;
11067 }
11068 else
11069 {
11070 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11071 hAllocator->m_hDevice,
11072 m_hMemory,
11073 0, // offset
11074 VK_WHOLE_SIZE,
11075 0, // flags
11076 &m_pMappedData);
11077 if(result == VK_SUCCESS)
11078 {
11079 if(ppData != VMA_NULL)
11080 {
11081 *ppData = m_pMappedData;
11082 }
11083 m_MapCount = count;
11084 }
11085 return result;
11086 }
11087}
11088
11089void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11090{
11091 if(count == 0)
11092 {
11093 return;
11094 }
11095
11096 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11097 if(m_MapCount >= count)
11098 {
11099 m_MapCount -= count;
11100 if(m_MapCount == 0)
11101 {
11102 m_pMappedData = VMA_NULL;
11103 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11104 }
11105 }
11106 else
11107 {
11108 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11109 }
11110}
11111
11112VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11113{
11114 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11115 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11116
11117 void* pData;
11118 VkResult res = Map(hAllocator, count: 1, ppData: &pData);
11119 if(res != VK_SUCCESS)
11120 {
11121 return res;
11122 }
11123
11124 VmaWriteMagicValue(pData, offset: allocOffset - VMA_DEBUG_MARGIN);
11125 VmaWriteMagicValue(pData, offset: allocOffset + allocSize);
11126
11127 Unmap(hAllocator, count: 1);
11128
11129 return VK_SUCCESS;
11130}
11131
11132VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11133{
11134 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11135 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11136
11137 void* pData;
11138 VkResult res = Map(hAllocator, count: 1, ppData: &pData);
11139 if(res != VK_SUCCESS)
11140 {
11141 return res;
11142 }
11143
11144 if(!VmaValidateMagicValue(pData, offset: allocOffset - VMA_DEBUG_MARGIN))
11145 {
11146 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11147 }
11148 else if(!VmaValidateMagicValue(pData, offset: allocOffset + allocSize))
11149 {
11150 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11151 }
11152
11153 Unmap(hAllocator, count: 1);
11154
11155 return VK_SUCCESS;
11156}
11157
11158VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11159 const VmaAllocator hAllocator,
11160 const VmaAllocation hAllocation,
11161 VkBuffer hBuffer)
11162{
11163 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11164 hAllocation->GetBlock() == this);
11165 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11166 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11167 return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11168 hAllocator->m_hDevice,
11169 hBuffer,
11170 m_hMemory,
11171 hAllocation->GetOffset());
11172}
11173
11174VkResult VmaDeviceMemoryBlock::BindImageMemory(
11175 const VmaAllocator hAllocator,
11176 const VmaAllocation hAllocation,
11177 VkImage hImage)
11178{
11179 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11180 hAllocation->GetBlock() == this);
11181 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11182 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11183 return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11184 hAllocator->m_hDevice,
11185 hImage,
11186 m_hMemory,
11187 hAllocation->GetOffset());
11188}
11189
11190static void InitStatInfo(VmaStatInfo& outInfo)
11191{
11192 memset(s: &outInfo, c: 0, n: sizeof(outInfo));
11193 outInfo.allocationSizeMin = UINT64_MAX;
11194 outInfo.unusedRangeSizeMin = UINT64_MAX;
11195}
11196
11197// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
11198static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11199{
11200 inoutInfo.blockCount += srcInfo.blockCount;
11201 inoutInfo.allocationCount += srcInfo.allocationCount;
11202 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11203 inoutInfo.usedBytes += srcInfo.usedBytes;
11204 inoutInfo.unusedBytes += srcInfo.unusedBytes;
11205 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11206 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11207 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11208 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11209}
11210
11211static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11212{
11213 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11214 VmaRoundDiv<VkDeviceSize>(x: inoutInfo.usedBytes, y: inoutInfo.allocationCount) : 0;
11215 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11216 VmaRoundDiv<VkDeviceSize>(x: inoutInfo.unusedBytes, y: inoutInfo.unusedRangeCount) : 0;
11217}
11218
11219VmaPool_T::VmaPool_T(
11220 VmaAllocator hAllocator,
11221 const VmaPoolCreateInfo& createInfo,
11222 VkDeviceSize preferredBlockSize) :
11223 m_BlockVector(
11224 hAllocator,
11225 createInfo.memoryTypeIndex,
11226 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11227 createInfo.minBlockCount,
11228 createInfo.maxBlockCount,
11229 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11230 createInfo.frameInUseCount,
11231 true, // isCustomPool
11232 createInfo.blockSize != 0, // explicitBlockSize
11233 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11234 m_Id(0)
11235{
11236}
11237
11238VmaPool_T::~VmaPool_T()
11239{
11240}
11241
11242#if VMA_STATS_STRING_ENABLED
11243
11244#endif // #if VMA_STATS_STRING_ENABLED
11245
11246VmaBlockVector::VmaBlockVector(
11247 VmaAllocator hAllocator,
11248 uint32_t memoryTypeIndex,
11249 VkDeviceSize preferredBlockSize,
11250 size_t minBlockCount,
11251 size_t maxBlockCount,
11252 VkDeviceSize bufferImageGranularity,
11253 uint32_t frameInUseCount,
11254 bool isCustomPool,
11255 bool explicitBlockSize,
11256 uint32_t algorithm) :
11257 m_hAllocator(hAllocator),
11258 m_MemoryTypeIndex(memoryTypeIndex),
11259 m_PreferredBlockSize(preferredBlockSize),
11260 m_MinBlockCount(minBlockCount),
11261 m_MaxBlockCount(maxBlockCount),
11262 m_BufferImageGranularity(bufferImageGranularity),
11263 m_FrameInUseCount(frameInUseCount),
11264 m_IsCustomPool(isCustomPool),
11265 m_ExplicitBlockSize(explicitBlockSize),
11266 m_Algorithm(algorithm),
11267 m_HasEmptyBlock(false),
11268 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11269 m_NextBlockId(0)
11270{
11271}
11272
11273VmaBlockVector::~VmaBlockVector()
11274{
11275 for(size_t i = m_Blocks.size(); i--; )
11276 {
11277 m_Blocks[i]->Destroy(allocator: m_hAllocator);
11278 vma_delete(hAllocator: m_hAllocator, ptr: m_Blocks[i]);
11279 }
11280}
11281
11282VkResult VmaBlockVector::CreateMinBlocks()
11283{
11284 for(size_t i = 0; i < m_MinBlockCount; ++i)
11285 {
11286 VkResult res = CreateBlock(blockSize: m_PreferredBlockSize, VMA_NULL);
11287 if(res != VK_SUCCESS)
11288 {
11289 return res;
11290 }
11291 }
11292 return VK_SUCCESS;
11293}
11294
11295void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11296{
11297 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11298
11299 const size_t blockCount = m_Blocks.size();
11300
11301 pStats->size = 0;
11302 pStats->unusedSize = 0;
11303 pStats->allocationCount = 0;
11304 pStats->unusedRangeCount = 0;
11305 pStats->unusedRangeSizeMax = 0;
11306 pStats->blockCount = blockCount;
11307
11308 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11309 {
11310 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11311 VMA_ASSERT(pBlock);
11312 VMA_HEAVY_ASSERT(pBlock->Validate());
11313 pBlock->m_pMetadata->AddPoolStats(inoutStats&: *pStats);
11314 }
11315}
11316
11317bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11318{
11319 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11320 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11321 (VMA_DEBUG_MARGIN > 0) &&
11322 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11323}
11324
11325static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11326
11327VkResult VmaBlockVector::Allocate(
11328 VmaPool hCurrentPool,
11329 uint32_t currentFrameIndex,
11330 VkDeviceSize size,
11331 VkDeviceSize alignment,
11332 const VmaAllocationCreateInfo& createInfo,
11333 VmaSuballocationType suballocType,
11334 size_t allocationCount,
11335 VmaAllocation* pAllocations)
11336{
11337 size_t allocIndex;
11338 VkResult res = VK_SUCCESS;
11339
11340 {
11341 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11342 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11343 {
11344 res = AllocatePage(
11345 hCurrentPool,
11346 currentFrameIndex,
11347 size,
11348 alignment,
11349 createInfo,
11350 suballocType,
11351 pAllocation: pAllocations + allocIndex);
11352 if(res != VK_SUCCESS)
11353 {
11354 break;
11355 }
11356 }
11357 }
11358
11359 if(res != VK_SUCCESS)
11360 {
11361 // Free all already created allocations.
11362 while(allocIndex--)
11363 {
11364 Free(hAllocation: pAllocations[allocIndex]);
11365 }
11366 memset(s: pAllocations, c: 0, n: sizeof(VmaAllocation) * allocationCount);
11367 }
11368
11369 return res;
11370}
11371
11372VkResult VmaBlockVector::AllocatePage(
11373 VmaPool hCurrentPool,
11374 uint32_t currentFrameIndex,
11375 VkDeviceSize size,
11376 VkDeviceSize alignment,
11377 const VmaAllocationCreateInfo& createInfo,
11378 VmaSuballocationType suballocType,
11379 VmaAllocation* pAllocation)
11380{
11381 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11382 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11383 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11384 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11385 const bool canCreateNewBlock =
11386 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11387 (m_Blocks.size() < m_MaxBlockCount);
11388 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11389
11390 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11391 // Which in turn is available only when maxBlockCount = 1.
11392 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11393 {
11394 canMakeOtherLost = false;
11395 }
11396
11397 // Upper address can only be used with linear allocator and within single memory block.
11398 if(isUpperAddress &&
11399 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11400 {
11401 return VK_ERROR_FEATURE_NOT_PRESENT;
11402 }
11403
11404 // Validate strategy.
11405 switch(strategy)
11406 {
11407 case 0:
11408 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11409 break;
11410 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11411 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11412 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11413 break;
11414 default:
11415 return VK_ERROR_FEATURE_NOT_PRESENT;
11416 }
11417
11418 // Early reject: requested allocation size is larger that maximum block size for this block vector.
11419 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11420 {
11421 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11422 }
11423
11424 /*
11425 Under certain condition, this whole section can be skipped for optimization, so
11426 we move on directly to trying to allocate with canMakeOtherLost. That's the case
11427 e.g. for custom pools with linear algorithm.
11428 */
11429 if(!canMakeOtherLost || canCreateNewBlock)
11430 {
11431 // 1. Search existing allocations. Try to allocate without making other allocations lost.
11432 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11433 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11434
11435 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11436 {
11437 // Use only last block.
11438 if(!m_Blocks.empty())
11439 {
11440 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11441 VMA_ASSERT(pCurrBlock);
11442 VkResult res = AllocateFromBlock(
11443 pBlock: pCurrBlock,
11444 hCurrentPool,
11445 currentFrameIndex,
11446 size,
11447 alignment,
11448 allocFlags: allocFlagsCopy,
11449 pUserData: createInfo.pUserData,
11450 suballocType,
11451 strategy,
11452 pAllocation);
11453 if(res == VK_SUCCESS)
11454 {
11455 VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11456 return VK_SUCCESS;
11457 }
11458 }
11459 }
11460 else
11461 {
11462 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11463 {
11464 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11465 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11466 {
11467 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11468 VMA_ASSERT(pCurrBlock);
11469 VkResult res = AllocateFromBlock(
11470 pBlock: pCurrBlock,
11471 hCurrentPool,
11472 currentFrameIndex,
11473 size,
11474 alignment,
11475 allocFlags: allocFlagsCopy,
11476 pUserData: createInfo.pUserData,
11477 suballocType,
11478 strategy,
11479 pAllocation);
11480 if(res == VK_SUCCESS)
11481 {
11482 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11483 return VK_SUCCESS;
11484 }
11485 }
11486 }
11487 else // WORST_FIT, FIRST_FIT
11488 {
11489 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11490 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11491 {
11492 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11493 VMA_ASSERT(pCurrBlock);
11494 VkResult res = AllocateFromBlock(
11495 pBlock: pCurrBlock,
11496 hCurrentPool,
11497 currentFrameIndex,
11498 size,
11499 alignment,
11500 allocFlags: allocFlagsCopy,
11501 pUserData: createInfo.pUserData,
11502 suballocType,
11503 strategy,
11504 pAllocation);
11505 if(res == VK_SUCCESS)
11506 {
11507 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11508 return VK_SUCCESS;
11509 }
11510 }
11511 }
11512 }
11513
11514 // 2. Try to create new block.
11515 if(canCreateNewBlock)
11516 {
11517 // Calculate optimal size for new block.
11518 VkDeviceSize newBlockSize = m_PreferredBlockSize;
11519 uint32_t newBlockSizeShift = 0;
11520 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11521
11522 if(!m_ExplicitBlockSize)
11523 {
11524 // Allocate 1/8, 1/4, 1/2 as first blocks.
11525 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11526 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11527 {
11528 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11529 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11530 {
11531 newBlockSize = smallerNewBlockSize;
11532 ++newBlockSizeShift;
11533 }
11534 else
11535 {
11536 break;
11537 }
11538 }
11539 }
11540
11541 size_t newBlockIndex = 0;
11542 VkResult res = CreateBlock(blockSize: newBlockSize, pNewBlockIndex: &newBlockIndex);
11543 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11544 if(!m_ExplicitBlockSize)
11545 {
11546 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11547 {
11548 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11549 if(smallerNewBlockSize >= size)
11550 {
11551 newBlockSize = smallerNewBlockSize;
11552 ++newBlockSizeShift;
11553 res = CreateBlock(blockSize: newBlockSize, pNewBlockIndex: &newBlockIndex);
11554 }
11555 else
11556 {
11557 break;
11558 }
11559 }
11560 }
11561
11562 if(res == VK_SUCCESS)
11563 {
11564 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11565 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11566
11567 res = AllocateFromBlock(
11568 pBlock,
11569 hCurrentPool,
11570 currentFrameIndex,
11571 size,
11572 alignment,
11573 allocFlags: allocFlagsCopy,
11574 pUserData: createInfo.pUserData,
11575 suballocType,
11576 strategy,
11577 pAllocation);
11578 if(res == VK_SUCCESS)
11579 {
11580 VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
11581 return VK_SUCCESS;
11582 }
11583 else
11584 {
11585 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11586 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11587 }
11588 }
11589 }
11590 }
11591
11592 // 3. Try to allocate from existing blocks with making other allocations lost.
11593 if(canMakeOtherLost)
11594 {
11595 uint32_t tryIndex = 0;
11596 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11597 {
11598 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11599 VmaAllocationRequest bestRequest = {};
11600 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11601
11602 // 1. Search existing allocations.
11603 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11604 {
11605 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11606 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11607 {
11608 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11609 VMA_ASSERT(pCurrBlock);
11610 VmaAllocationRequest currRequest = {};
11611 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11612 currentFrameIndex,
11613 frameInUseCount: m_FrameInUseCount,
11614 bufferImageGranularity: m_BufferImageGranularity,
11615 allocSize: size,
11616 allocAlignment: alignment,
11617 upperAddress: (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11618 allocType: suballocType,
11619 canMakeOtherLost,
11620 strategy,
11621 pAllocationRequest: &currRequest))
11622 {
11623 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11624 if(pBestRequestBlock == VMA_NULL ||
11625 currRequestCost < bestRequestCost)
11626 {
11627 pBestRequestBlock = pCurrBlock;
11628 bestRequest = currRequest;
11629 bestRequestCost = currRequestCost;
11630
11631 if(bestRequestCost == 0)
11632 {
11633 break;
11634 }
11635 }
11636 }
11637 }
11638 }
11639 else // WORST_FIT, FIRST_FIT
11640 {
11641 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11642 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11643 {
11644 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11645 VMA_ASSERT(pCurrBlock);
11646 VmaAllocationRequest currRequest = {};
11647 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11648 currentFrameIndex,
11649 frameInUseCount: m_FrameInUseCount,
11650 bufferImageGranularity: m_BufferImageGranularity,
11651 allocSize: size,
11652 allocAlignment: alignment,
11653 upperAddress: (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11654 allocType: suballocType,
11655 canMakeOtherLost,
11656 strategy,
11657 pAllocationRequest: &currRequest))
11658 {
11659 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11660 if(pBestRequestBlock == VMA_NULL ||
11661 currRequestCost < bestRequestCost ||
11662 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11663 {
11664 pBestRequestBlock = pCurrBlock;
11665 bestRequest = currRequest;
11666 bestRequestCost = currRequestCost;
11667
11668 if(bestRequestCost == 0 ||
11669 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11670 {
11671 break;
11672 }
11673 }
11674 }
11675 }
11676 }
11677
11678 if(pBestRequestBlock != VMA_NULL)
11679 {
11680 if(mapped)
11681 {
11682 VkResult res = pBestRequestBlock->Map(hAllocator: m_hAllocator, count: 1, VMA_NULL);
11683 if(res != VK_SUCCESS)
11684 {
11685 return res;
11686 }
11687 }
11688
11689 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11690 currentFrameIndex,
11691 frameInUseCount: m_FrameInUseCount,
11692 pAllocationRequest: &bestRequest))
11693 {
11694 // We no longer have an empty Allocation.
11695 if(pBestRequestBlock->m_pMetadata->IsEmpty())
11696 {
11697 m_HasEmptyBlock = false;
11698 }
11699 // Allocate from this pBlock.
11700 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11701 pBestRequestBlock->m_pMetadata->Alloc(request: bestRequest, type: suballocType, allocSize: size, upperAddress: isUpperAddress, hAllocation: *pAllocation);
11702 (*pAllocation)->InitBlockAllocation(
11703 hPool: hCurrentPool,
11704 block: pBestRequestBlock,
11705 offset: bestRequest.offset,
11706 alignment,
11707 size,
11708 suballocationType: suballocType,
11709 mapped,
11710 canBecomeLost: (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11711 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11712 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
11713 (*pAllocation)->SetUserData(hAllocator: m_hAllocator, pUserData: createInfo.pUserData);
11714 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11715 {
11716 m_hAllocator->FillAllocation(hAllocation: *pAllocation, pattern: VMA_ALLOCATION_FILL_PATTERN_CREATED);
11717 }
11718 if(IsCorruptionDetectionEnabled())
11719 {
11720 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(hAllocator: m_hAllocator, allocOffset: bestRequest.offset, allocSize: size);
11721 (void) res;
11722 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11723 }
11724 return VK_SUCCESS;
11725 }
11726 // else: Some allocations must have been touched while we are here. Next try.
11727 }
11728 else
11729 {
11730 // Could not find place in any of the blocks - break outer loop.
11731 break;
11732 }
11733 }
11734 /* Maximum number of tries exceeded - a very unlike event when many other
11735 threads are simultaneously touching allocations making it impossible to make
11736 lost at the same time as we try to allocate. */
11737 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11738 {
11739 return VK_ERROR_TOO_MANY_OBJECTS;
11740 }
11741 }
11742
11743 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11744}
11745
11746void VmaBlockVector::Free(
11747 VmaAllocation hAllocation)
11748{
11749 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11750
11751 // Scope for lock.
11752 {
11753 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11754
11755 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11756
11757 if(IsCorruptionDetectionEnabled())
11758 {
11759 VkResult res = pBlock->ValidateMagicValueAroundAllocation(hAllocator: m_hAllocator, allocOffset: hAllocation->GetOffset(), allocSize: hAllocation->GetSize());
11760 (void) res;
11761 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11762 }
11763
11764 if(hAllocation->IsPersistentMap())
11765 {
11766 pBlock->Unmap(hAllocator: m_hAllocator, count: 1);
11767 }
11768
11769 pBlock->m_pMetadata->Free(allocation: hAllocation);
11770 VMA_HEAVY_ASSERT(pBlock->Validate());
11771
11772 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11773
11774 // pBlock became empty after this deallocation.
11775 if(pBlock->m_pMetadata->IsEmpty())
11776 {
11777 // Already has empty Allocation. We don't want to have two, so delete this one.
11778 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11779 {
11780 pBlockToDelete = pBlock;
11781 Remove(pBlock);
11782 }
11783 // We now have first empty block.
11784 else
11785 {
11786 m_HasEmptyBlock = true;
11787 }
11788 }
11789 // pBlock didn't become empty, but we have another empty block - find and free that one.
11790 // (This is optional, heuristics.)
11791 else if(m_HasEmptyBlock)
11792 {
11793 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11794 if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11795 {
11796 pBlockToDelete = pLastBlock;
11797 m_Blocks.pop_back();
11798 m_HasEmptyBlock = false;
11799 }
11800 }
11801
11802 IncrementallySortBlocks();
11803 }
11804
11805 // Destruction of a free Allocation. Deferred until this point, outside of mutex
11806 // lock, for performance reason.
11807 if(pBlockToDelete != VMA_NULL)
11808 {
11809 VMA_DEBUG_LOG(" Deleted empty allocation");
11810 pBlockToDelete->Destroy(allocator: m_hAllocator);
11811 vma_delete(hAllocator: m_hAllocator, ptr: pBlockToDelete);
11812 }
11813}
11814
11815VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11816{
11817 VkDeviceSize result = 0;
11818 for(size_t i = m_Blocks.size(); i--; )
11819 {
11820 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11821 if(result >= m_PreferredBlockSize)
11822 {
11823 break;
11824 }
11825 }
11826 return result;
11827}
11828
11829void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11830{
11831 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11832 {
11833 if(m_Blocks[blockIndex] == pBlock)
11834 {
11835 VmaVectorRemove(vec&: m_Blocks, index: blockIndex);
11836 return;
11837 }
11838 }
11839 VMA_ASSERT(0);
11840}
11841
11842void VmaBlockVector::IncrementallySortBlocks()
11843{
11844 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11845 {
11846 // Bubble sort only until first swap.
11847 for(size_t i = 1; i < m_Blocks.size(); ++i)
11848 {
11849 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11850 {
11851 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11852 return;
11853 }
11854 }
11855 }
11856}
11857
11858VkResult VmaBlockVector::AllocateFromBlock(
11859 VmaDeviceMemoryBlock* pBlock,
11860 VmaPool hCurrentPool,
11861 uint32_t currentFrameIndex,
11862 VkDeviceSize size,
11863 VkDeviceSize alignment,
11864 VmaAllocationCreateFlags allocFlags,
11865 void* pUserData,
11866 VmaSuballocationType suballocType,
11867 uint32_t strategy,
11868 VmaAllocation* pAllocation)
11869{
11870 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11871 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11872 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11873 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11874
11875 VmaAllocationRequest currRequest = {};
11876 if(pBlock->m_pMetadata->CreateAllocationRequest(
11877 currentFrameIndex,
11878 frameInUseCount: m_FrameInUseCount,
11879 bufferImageGranularity: m_BufferImageGranularity,
11880 allocSize: size,
11881 allocAlignment: alignment,
11882 upperAddress: isUpperAddress,
11883 allocType: suballocType,
11884 canMakeOtherLost: false, // canMakeOtherLost
11885 strategy,
11886 pAllocationRequest: &currRequest))
11887 {
11888 // Allocate from pCurrBlock.
11889 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11890
11891 if(mapped)
11892 {
11893 VkResult res = pBlock->Map(hAllocator: m_hAllocator, count: 1, VMA_NULL);
11894 if(res != VK_SUCCESS)
11895 {
11896 return res;
11897 }
11898 }
11899
11900 // We no longer have an empty Allocation.
11901 if(pBlock->m_pMetadata->IsEmpty())
11902 {
11903 m_HasEmptyBlock = false;
11904 }
11905
11906 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11907 pBlock->m_pMetadata->Alloc(request: currRequest, type: suballocType, allocSize: size, upperAddress: isUpperAddress, hAllocation: *pAllocation);
11908 (*pAllocation)->InitBlockAllocation(
11909 hPool: hCurrentPool,
11910 block: pBlock,
11911 offset: currRequest.offset,
11912 alignment,
11913 size,
11914 suballocationType: suballocType,
11915 mapped,
11916 canBecomeLost: (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11917 VMA_HEAVY_ASSERT(pBlock->Validate());
11918 (*pAllocation)->SetUserData(hAllocator: m_hAllocator, pUserData);
11919 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11920 {
11921 m_hAllocator->FillAllocation(hAllocation: *pAllocation, pattern: VMA_ALLOCATION_FILL_PATTERN_CREATED);
11922 }
11923 if(IsCorruptionDetectionEnabled())
11924 {
11925 VkResult res = pBlock->WriteMagicValueAroundAllocation(hAllocator: m_hAllocator, allocOffset: currRequest.offset, allocSize: size);
11926 (void) res;
11927 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11928 }
11929 return VK_SUCCESS;
11930 }
11931 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11932}
11933
11934VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11935{
11936 VkMemoryAllocateInfo allocInfo = {};
11937 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
11938 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11939 allocInfo.allocationSize = blockSize;
11940 VkDeviceMemory mem = VK_NULL_HANDLE;
11941 VkResult res = m_hAllocator->AllocateVulkanMemory(pAllocateInfo: &allocInfo, pMemory: &mem);
11942 if(res < 0)
11943 {
11944 return res;
11945 }
11946
11947 // New VkDeviceMemory successfully created.
11948
11949 // Create new Allocation for it.
11950 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11951 pBlock->Init(
11952 hAllocator: m_hAllocator,
11953 newMemoryTypeIndex: m_MemoryTypeIndex,
11954 newMemory: mem,
11955 newSize: allocInfo.allocationSize,
11956 id: m_NextBlockId++,
11957 algorithm: m_Algorithm);
11958
11959 m_Blocks.push_back(src: pBlock);
11960 if(pNewBlockIndex != VMA_NULL)
11961 {
11962 *pNewBlockIndex = m_Blocks.size() - 1;
11963 }
11964
11965 return VK_SUCCESS;
11966}
11967
11968void VmaBlockVector::ApplyDefragmentationMovesCpu(
11969 class VmaBlockVectorDefragmentationContext* pDefragCtx,
11970 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11971{
11972 const size_t blockCount = m_Blocks.size();
11973 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(memTypeIndex: m_MemoryTypeIndex);
11974
11975 enum BLOCK_FLAG
11976 {
11977 BLOCK_FLAG_USED = 0x00000001,
11978 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11979 };
11980
11981 struct BlockInfo
11982 {
11983 uint32_t flags;
11984 void* pMappedData;
11985 };
11986 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11987 blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11988 memset(s: blockInfo.data(), c: 0, n: blockCount * sizeof(BlockInfo));
11989
11990 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11991 const size_t moveCount = moves.size();
11992 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11993 {
11994 const VmaDefragmentationMove& move = moves[moveIndex];
11995 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
11996 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
11997 }
11998
11999 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12000
12001 // Go over all blocks. Get mapped pointer or map if necessary.
12002 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12003 {
12004 BlockInfo& currBlockInfo = blockInfo[blockIndex];
12005 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12006 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12007 {
12008 currBlockInfo.pMappedData = pBlock->GetMappedData();
12009 // It is not originally mapped - map it.
12010 if(currBlockInfo.pMappedData == VMA_NULL)
12011 {
12012 pDefragCtx->res = pBlock->Map(hAllocator: m_hAllocator, count: 1, ppData: &currBlockInfo.pMappedData);
12013 if(pDefragCtx->res == VK_SUCCESS)
12014 {
12015 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12016 }
12017 }
12018 }
12019 }
12020
12021 // Go over all moves. Do actual data transfer.
12022 if(pDefragCtx->res == VK_SUCCESS)
12023 {
12024 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12025 VkMappedMemoryRange memRange = {};
12026 memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
12027
12028 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12029 {
12030 const VmaDefragmentationMove& move = moves[moveIndex];
12031
12032 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12033 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12034
12035 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12036
12037 // Invalidate source.
12038 if(isNonCoherent)
12039 {
12040 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12041 memRange.memory = pSrcBlock->GetDeviceMemory();
12042 memRange.offset = VmaAlignDown(val: move.srcOffset, align: nonCoherentAtomSize);
12043 memRange.size = VMA_MIN(
12044 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12045 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12046 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12047 }
12048
12049 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12050 memmove(
12051 dest: reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12052 src: reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12053 n: static_cast<size_t>(move.size));
12054
12055 if(IsCorruptionDetectionEnabled())
12056 {
12057 VmaWriteMagicValue(pData: dstBlockInfo.pMappedData, offset: move.dstOffset - VMA_DEBUG_MARGIN);
12058 VmaWriteMagicValue(pData: dstBlockInfo.pMappedData, offset: move.dstOffset + move.size);
12059 }
12060
12061 // Flush destination.
12062 if(isNonCoherent)
12063 {
12064 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12065 memRange.memory = pDstBlock->GetDeviceMemory();
12066 memRange.offset = VmaAlignDown(val: move.dstOffset, align: nonCoherentAtomSize);
12067 memRange.size = VMA_MIN(
12068 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12069 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12070 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12071 }
12072 }
12073 }
12074
12075 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12076 // Regardless of pCtx->res == VK_SUCCESS.
12077 for(size_t blockIndex = blockCount; blockIndex--; )
12078 {
12079 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12080 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12081 {
12082 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12083 pBlock->Unmap(hAllocator: m_hAllocator, count: 1);
12084 }
12085 }
12086}
12087
12088void VmaBlockVector::ApplyDefragmentationMovesGpu(
12089 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12090 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12091 VkCommandBuffer commandBuffer)
12092{
12093 const size_t blockCount = m_Blocks.size();
12094
12095 pDefragCtx->blockContexts.resize(newCount: blockCount);
12096 for (size_t i = 0; i < blockCount; ++i)
12097 pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext();
12098
12099 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12100 const size_t moveCount = moves.size();
12101 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12102 {
12103 const VmaDefragmentationMove& move = moves[moveIndex];
12104 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12105 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12106 }
12107
12108 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12109
12110 // Go over all blocks. Create and bind buffer for whole block if necessary.
12111 {
12112 VkBufferCreateInfo bufCreateInfo = {};
12113 bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
12114 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12115 VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12116
12117 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12118 {
12119 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12120 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12121 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12122 {
12123 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12124 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12125 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12126 if(pDefragCtx->res == VK_SUCCESS)
12127 {
12128 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12129 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12130 }
12131 }
12132 }
12133 }
12134
12135 // Go over all moves. Post data transfer commands to command buffer.
12136 if(pDefragCtx->res == VK_SUCCESS)
12137 {
12138 /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12139 VkMappedMemoryRange memRange = {};
12140 memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/
12141
12142 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12143 {
12144 const VmaDefragmentationMove& move = moves[moveIndex];
12145
12146 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12147 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12148
12149 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12150
12151 VkBufferCopy region = {
12152 .srcOffset: move.srcOffset,
12153 .dstOffset: move.dstOffset,
12154 .size: move.size };
12155 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12156 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12157 }
12158 }
12159
12160 // Save buffers to defrag context for later destruction.
12161 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12162 {
12163 pDefragCtx->res = VK_NOT_READY;
12164 }
12165}
12166
12167void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12168{
12169 m_HasEmptyBlock = false;
12170 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12171 {
12172 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12173 if(pBlock->m_pMetadata->IsEmpty())
12174 {
12175 if(m_Blocks.size() > m_MinBlockCount)
12176 {
12177 if(pDefragmentationStats != VMA_NULL)
12178 {
12179 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12180 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12181 }
12182
12183 VmaVectorRemove(vec&: m_Blocks, index: blockIndex);
12184 pBlock->Destroy(allocator: m_hAllocator);
12185 vma_delete(hAllocator: m_hAllocator, ptr: pBlock);
12186 }
12187 else
12188 {
12189 m_HasEmptyBlock = true;
12190 }
12191 }
12192 }
12193}
12194
12195#if VMA_STATS_STRING_ENABLED
12196
12197void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12198{
12199 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12200
12201 json.BeginObject();
12202
12203 if(m_IsCustomPool)
12204 {
12205 json.WriteString(pStr: "MemoryTypeIndex");
12206 json.WriteNumber(n: m_MemoryTypeIndex);
12207
12208 json.WriteString(pStr: "BlockSize");
12209 json.WriteNumber(n: m_PreferredBlockSize);
12210
12211 json.WriteString(pStr: "BlockCount");
12212 json.BeginObject(singleLine: true);
12213 if(m_MinBlockCount > 0)
12214 {
12215 json.WriteString(pStr: "Min");
12216 json.WriteNumber(n: (uint64_t)m_MinBlockCount);
12217 }
12218 if(m_MaxBlockCount < SIZE_MAX)
12219 {
12220 json.WriteString(pStr: "Max");
12221 json.WriteNumber(n: (uint64_t)m_MaxBlockCount);
12222 }
12223 json.WriteString(pStr: "Cur");
12224 json.WriteNumber(n: (uint64_t)m_Blocks.size());
12225 json.EndObject();
12226
12227 if(m_FrameInUseCount > 0)
12228 {
12229 json.WriteString(pStr: "FrameInUseCount");
12230 json.WriteNumber(n: m_FrameInUseCount);
12231 }
12232
12233 if(m_Algorithm != 0)
12234 {
12235 json.WriteString(pStr: "Algorithm");
12236 json.WriteString(pStr: VmaAlgorithmToStr(algorithm: m_Algorithm));
12237 }
12238 }
12239 else
12240 {
12241 json.WriteString(pStr: "PreferredBlockSize");
12242 json.WriteNumber(n: m_PreferredBlockSize);
12243 }
12244
12245 json.WriteString(pStr: "Blocks");
12246 json.BeginObject();
12247 for(size_t i = 0; i < m_Blocks.size(); ++i)
12248 {
12249 json.BeginString();
12250 json.ContinueString(n: m_Blocks[i]->GetId());
12251 json.EndString();
12252
12253 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12254 }
12255 json.EndObject();
12256
12257 json.EndObject();
12258}
12259
12260#endif // #if VMA_STATS_STRING_ENABLED
12261
12262void VmaBlockVector::Defragment(
12263 class VmaBlockVectorDefragmentationContext* pCtx,
12264 VmaDefragmentationStats* pStats,
12265 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12266 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12267 VkCommandBuffer commandBuffer)
12268{
12269 pCtx->res = VK_SUCCESS;
12270
12271 const VkMemoryPropertyFlags memPropFlags =
12272 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12273 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12274 const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12275
12276 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12277 isHostVisible;
12278 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12279 (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12280
12281 // There are options to defragment this memory type.
12282 if(canDefragmentOnCpu || canDefragmentOnGpu)
12283 {
12284 bool defragmentOnGpu;
12285 // There is only one option to defragment this memory type.
12286 if(canDefragmentOnGpu != canDefragmentOnCpu)
12287 {
12288 defragmentOnGpu = canDefragmentOnGpu;
12289 }
12290 // Both options are available: Heuristics to choose the best one.
12291 else
12292 {
12293 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12294 m_hAllocator->IsIntegratedGpu();
12295 }
12296
12297 bool overlappingMoveSupported = !defragmentOnGpu;
12298
12299 if(m_hAllocator->m_UseMutex)
12300 {
12301 m_Mutex.LockWrite();
12302 pCtx->mutexLocked = true;
12303 }
12304
12305 pCtx->Begin(overlappingMoveSupported);
12306
12307 // Defragment.
12308
12309 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12310 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12311 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12312 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12313 pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12314
12315 // Accumulate statistics.
12316 if(pStats != VMA_NULL)
12317 {
12318 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12319 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12320 pStats->bytesMoved += bytesMoved;
12321 pStats->allocationsMoved += allocationsMoved;
12322 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12323 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12324 if(defragmentOnGpu)
12325 {
12326 maxGpuBytesToMove -= bytesMoved;
12327 maxGpuAllocationsToMove -= allocationsMoved;
12328 }
12329 else
12330 {
12331 maxCpuBytesToMove -= bytesMoved;
12332 maxCpuAllocationsToMove -= allocationsMoved;
12333 }
12334 }
12335
12336 if(pCtx->res >= VK_SUCCESS)
12337 {
12338 if(defragmentOnGpu)
12339 {
12340 ApplyDefragmentationMovesGpu(pDefragCtx: pCtx, moves, commandBuffer);
12341 }
12342 else
12343 {
12344 ApplyDefragmentationMovesCpu(pDefragCtx: pCtx, moves);
12345 }
12346 }
12347 }
12348}
12349
12350void VmaBlockVector::DefragmentationEnd(
12351 class VmaBlockVectorDefragmentationContext* pCtx,
12352 VmaDefragmentationStats* pStats)
12353{
12354 // Destroy buffers.
12355 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12356 {
12357 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12358 if(blockCtx.hBuffer)
12359 {
12360 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12361 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12362 }
12363 }
12364
12365 if(pCtx->res >= VK_SUCCESS)
12366 {
12367 FreeEmptyBlocks(pDefragmentationStats: pStats);
12368 }
12369
12370 if(pCtx->mutexLocked)
12371 {
12372 VMA_ASSERT(m_hAllocator->m_UseMutex);
12373 m_Mutex.UnlockWrite();
12374 }
12375}
12376
12377size_t VmaBlockVector::CalcAllocationCount() const
12378{
12379 size_t result = 0;
12380 for(size_t i = 0; i < m_Blocks.size(); ++i)
12381 {
12382 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12383 }
12384 return result;
12385}
12386
12387bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12388{
12389 if(m_BufferImageGranularity == 1)
12390 {
12391 return false;
12392 }
12393 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12394 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12395 {
12396 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12397 VMA_ASSERT(m_Algorithm == 0);
12398 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12399 if(pMetadata->IsBufferImageGranularityConflictPossible(bufferImageGranularity: m_BufferImageGranularity, inOutPrevSuballocType&: lastSuballocType))
12400 {
12401 return true;
12402 }
12403 }
12404 return false;
12405}
12406
12407void VmaBlockVector::MakePoolAllocationsLost(
12408 uint32_t currentFrameIndex,
12409 size_t* pLostAllocationCount)
12410{
12411 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12412 size_t lostAllocationCount = 0;
12413 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12414 {
12415 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12416 VMA_ASSERT(pBlock);
12417 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, frameInUseCount: m_FrameInUseCount);
12418 }
12419 if(pLostAllocationCount != VMA_NULL)
12420 {
12421 *pLostAllocationCount = lostAllocationCount;
12422 }
12423}
12424
12425VkResult VmaBlockVector::CheckCorruption()
12426{
12427 if(!IsCorruptionDetectionEnabled())
12428 {
12429 return VK_ERROR_FEATURE_NOT_PRESENT;
12430 }
12431
12432 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12433 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12434 {
12435 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12436 VMA_ASSERT(pBlock);
12437 VkResult res = pBlock->CheckCorruption(hAllocator: m_hAllocator);
12438 if(res != VK_SUCCESS)
12439 {
12440 return res;
12441 }
12442 }
12443 return VK_SUCCESS;
12444}
12445
12446void VmaBlockVector::AddStats(VmaStats* pStats)
12447{
12448 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12449 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12450
12451 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12452
12453 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12454 {
12455 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12456 VMA_ASSERT(pBlock);
12457 VMA_HEAVY_ASSERT(pBlock->Validate());
12458 VmaStatInfo allocationStatInfo;
12459 pBlock->m_pMetadata->CalcAllocationStatInfo(outInfo&: allocationStatInfo);
12460 VmaAddStatInfo(inoutInfo&: pStats->total, srcInfo: allocationStatInfo);
12461 VmaAddStatInfo(inoutInfo&: pStats->memoryType[memTypeIndex], srcInfo: allocationStatInfo);
12462 VmaAddStatInfo(inoutInfo&: pStats->memoryHeap[memHeapIndex], srcInfo: allocationStatInfo);
12463 }
12464}
12465
12466////////////////////////////////////////////////////////////////////////////////
12467// VmaDefragmentationAlgorithm_Generic members definition
12468
12469VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12470 VmaAllocator hAllocator,
12471 VmaBlockVector* pBlockVector,
12472 uint32_t currentFrameIndex,
12473 bool /*overlappingMoveSupported*/) :
12474 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12475 m_AllocationCount(0),
12476 m_AllAllocations(false),
12477 m_BytesMoved(0),
12478 m_AllocationsMoved(0),
12479 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12480{
12481 // Create block info for each block.
12482 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12483 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12484 {
12485 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12486 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12487 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12488 m_Blocks.push_back(src: pBlockInfo);
12489 }
12490
12491 // Sort them by m_pBlock pointer value.
12492 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12493}
12494
12495VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12496{
12497 for(size_t i = m_Blocks.size(); i--; )
12498 {
12499 vma_delete(hAllocator: m_hAllocator, ptr: m_Blocks[i]);
12500 }
12501}
12502
12503void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12504{
12505 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12506 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12507 {
12508 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12509 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(beg: m_Blocks.begin(), end: m_Blocks.end(), key: pBlock, cmp: BlockPointerLess());
12510 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12511 {
12512 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12513 (*it)->m_Allocations.push_back(src: allocInfo);
12514 }
12515 else
12516 {
12517 VMA_ASSERT(0);
12518 }
12519
12520 ++m_AllocationCount;
12521 }
12522}
12523
12524VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12525 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12526 VkDeviceSize maxBytesToMove,
12527 uint32_t maxAllocationsToMove)
12528{
12529 if(m_Blocks.empty())
12530 {
12531 return VK_SUCCESS;
12532 }
12533
12534 // This is a choice based on research.
12535 // Option 1:
12536 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12537 // Option 2:
12538 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12539 // Option 3:
12540 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12541
12542 size_t srcBlockMinIndex = 0;
12543 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12544 /*
12545 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12546 {
12547 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12548 if(blocksWithNonMovableCount > 0)
12549 {
12550 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12551 }
12552 }
12553 */
12554
12555 size_t srcBlockIndex = m_Blocks.size() - 1;
12556 size_t srcAllocIndex = SIZE_MAX;
12557 for(;;)
12558 {
12559 // 1. Find next allocation to move.
12560 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12561 // 1.2. Then start from last to first m_Allocations.
12562 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12563 {
12564 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12565 {
12566 // Finished: no more allocations to process.
12567 if(srcBlockIndex == srcBlockMinIndex)
12568 {
12569 return VK_SUCCESS;
12570 }
12571 else
12572 {
12573 --srcBlockIndex;
12574 srcAllocIndex = SIZE_MAX;
12575 }
12576 }
12577 else
12578 {
12579 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12580 }
12581 }
12582
12583 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12584 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12585
12586 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12587 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12588 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12589 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12590
12591 // 2. Try to find new place for this allocation in preceding or current block.
12592 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12593 {
12594 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12595 VmaAllocationRequest dstAllocRequest;
12596 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12597 currentFrameIndex: m_CurrentFrameIndex,
12598 frameInUseCount: m_pBlockVector->GetFrameInUseCount(),
12599 bufferImageGranularity: m_pBlockVector->GetBufferImageGranularity(),
12600 allocSize: size,
12601 allocAlignment: alignment,
12602 upperAddress: false, // upperAddress
12603 allocType: suballocType,
12604 canMakeOtherLost: false, // canMakeOtherLost
12605 strategy,
12606 pAllocationRequest: &dstAllocRequest) &&
12607 MoveMakesSense(
12608 dstBlockIndex, dstOffset: dstAllocRequest.offset, srcBlockIndex, srcOffset))
12609 {
12610 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12611
12612 // Reached limit on number of allocations or bytes to move.
12613 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12614 (m_BytesMoved + size > maxBytesToMove))
12615 {
12616 return VK_SUCCESS;
12617 }
12618
12619 VmaDefragmentationMove move;
12620 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12621 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12622 move.srcOffset = srcOffset;
12623 move.dstOffset = dstAllocRequest.offset;
12624 move.size = size;
12625 moves.push_back(src: move);
12626
12627 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12628 request: dstAllocRequest,
12629 type: suballocType,
12630 allocSize: size,
12631 upperAddress: false, // upperAddress
12632 hAllocation: allocInfo.m_hAllocation);
12633 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(offset: srcOffset);
12634
12635 allocInfo.m_hAllocation->ChangeBlockAllocation(hAllocator: m_hAllocator, block: pDstBlockInfo->m_pBlock, offset: dstAllocRequest.offset);
12636
12637 if(allocInfo.m_pChanged != VMA_NULL)
12638 {
12639 *allocInfo.m_pChanged = VK_TRUE;
12640 }
12641
12642 ++m_AllocationsMoved;
12643 m_BytesMoved += size;
12644
12645 VmaVectorRemove(vec&: pSrcBlockInfo->m_Allocations, index: srcAllocIndex);
12646
12647 break;
12648 }
12649 }
12650
12651 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12652
12653 if(srcAllocIndex > 0)
12654 {
12655 --srcAllocIndex;
12656 }
12657 else
12658 {
12659 if(srcBlockIndex > 0)
12660 {
12661 --srcBlockIndex;
12662 srcAllocIndex = SIZE_MAX;
12663 }
12664 else
12665 {
12666 return VK_SUCCESS;
12667 }
12668 }
12669 }
12670}
12671
12672size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12673{
12674 size_t result = 0;
12675 for(size_t i = 0; i < m_Blocks.size(); ++i)
12676 {
12677 if(m_Blocks[i]->m_HasNonMovableAllocations)
12678 {
12679 ++result;
12680 }
12681 }
12682 return result;
12683}
12684
12685VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12686 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12687 VkDeviceSize maxBytesToMove,
12688 uint32_t maxAllocationsToMove)
12689{
12690 if(!m_AllAllocations && m_AllocationCount == 0)
12691 {
12692 return VK_SUCCESS;
12693 }
12694
12695 const size_t blockCount = m_Blocks.size();
12696 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12697 {
12698 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12699
12700 if(m_AllAllocations)
12701 {
12702 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12703 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12704 it != pMetadata->m_Suballocations.end();
12705 ++it)
12706 {
12707 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12708 {
12709 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12710 pBlockInfo->m_Allocations.push_back(src: allocInfo);
12711 }
12712 }
12713 }
12714
12715 pBlockInfo->CalcHasNonMovableAllocations();
12716
12717 // This is a choice based on research.
12718 // Option 1:
12719 pBlockInfo->SortAllocationsByOffsetDescending();
12720 // Option 2:
12721 //pBlockInfo->SortAllocationsBySizeDescending();
12722 }
12723
12724 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12725 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12726
12727 // This is a choice based on research.
12728 const uint32_t roundCount = 2;
12729
12730 // Execute defragmentation rounds (the main part).
12731 VkResult result = VK_SUCCESS;
12732 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12733 {
12734 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12735 }
12736
12737 return result;
12738}
12739
12740bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12741 size_t dstBlockIndex, VkDeviceSize dstOffset,
12742 size_t srcBlockIndex, VkDeviceSize srcOffset)
12743{
12744 if(dstBlockIndex < srcBlockIndex)
12745 {
12746 return true;
12747 }
12748 if(dstBlockIndex > srcBlockIndex)
12749 {
12750 return false;
12751 }
12752 if(dstOffset < srcOffset)
12753 {
12754 return true;
12755 }
12756 return false;
12757}
12758
12759////////////////////////////////////////////////////////////////////////////////
12760// VmaDefragmentationAlgorithm_Fast
12761
12762VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12763 VmaAllocator hAllocator,
12764 VmaBlockVector* pBlockVector,
12765 uint32_t currentFrameIndex,
12766 bool overlappingMoveSupported) :
12767 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12768 m_OverlappingMoveSupported(overlappingMoveSupported),
12769 m_AllocationCount(0),
12770 m_AllAllocations(false),
12771 m_BytesMoved(0),
12772 m_AllocationsMoved(0),
12773 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12774{
12775 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12776
12777}
12778
12779VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12780{
12781}
12782
12783VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12784 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12785 VkDeviceSize maxBytesToMove,
12786 uint32_t maxAllocationsToMove)
12787{
12788 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12789
12790 const size_t blockCount = m_pBlockVector->GetBlockCount();
12791 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12792 {
12793 return VK_SUCCESS;
12794 }
12795
12796 PreprocessMetadata();
12797
12798 // Sort blocks in order from most destination.
12799
12800 m_BlockInfos.resize(newCount: blockCount);
12801 for(size_t i = 0; i < blockCount; ++i)
12802 {
12803 m_BlockInfos[i].origBlockIndex = i;
12804 }
12805
12806 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12807 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12808 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12809 });
12810
12811 // THE MAIN ALGORITHM
12812
12813 FreeSpaceDatabase freeSpaceDb;
12814
12815 size_t dstBlockInfoIndex = 0;
12816 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12817 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(index: dstOrigBlockIndex);
12818 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12819 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12820 VkDeviceSize dstOffset = 0;
12821
12822 bool end = false;
12823 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12824 {
12825 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12826 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(index: srcOrigBlockIndex);
12827 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12828 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12829 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12830 {
12831 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12832 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12833 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12834 if(m_AllocationsMoved == maxAllocationsToMove ||
12835 m_BytesMoved + srcAllocSize > maxBytesToMove)
12836 {
12837 end = true;
12838 break;
12839 }
12840 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12841
12842 // Try to place it in one of free spaces from the database.
12843 size_t freeSpaceInfoIndex;
12844 VkDeviceSize dstAllocOffset;
12845 if(freeSpaceDb.Fetch(alignment: srcAllocAlignment, size: srcAllocSize,
12846 outBlockInfoIndex&: freeSpaceInfoIndex, outDstOffset&: dstAllocOffset))
12847 {
12848 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12849 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(index: freeSpaceOrigBlockIndex);
12850 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12851 /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/
12852
12853 // Same block
12854 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12855 {
12856 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12857
12858 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12859
12860 VmaSuballocation suballoc = *srcSuballocIt;
12861 suballoc.offset = dstAllocOffset;
12862 suballoc.hAllocation->ChangeOffset(newOffset: dstAllocOffset);
12863 m_BytesMoved += srcAllocSize;
12864 ++m_AllocationsMoved;
12865
12866 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12867 ++nextSuballocIt;
12868 pSrcMetadata->m_Suballocations.erase(it: srcSuballocIt);
12869 srcSuballocIt = nextSuballocIt;
12870
12871 InsertSuballoc(pMetadata: pFreeSpaceMetadata, suballoc);
12872
12873 VmaDefragmentationMove move = {
12874 .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: freeSpaceOrigBlockIndex,
12875 .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset,
12876 .size: srcAllocSize };
12877 moves.push_back(src: move);
12878 }
12879 // Different block
12880 else
12881 {
12882 // MOVE OPTION 2: Move the allocation to a different block.
12883
12884 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12885
12886 VmaSuballocation suballoc = *srcSuballocIt;
12887 suballoc.offset = dstAllocOffset;
12888 suballoc.hAllocation->ChangeBlockAllocation(hAllocator: m_hAllocator, block: pFreeSpaceBlock, offset: dstAllocOffset);
12889 m_BytesMoved += srcAllocSize;
12890 ++m_AllocationsMoved;
12891
12892 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12893 ++nextSuballocIt;
12894 pSrcMetadata->m_Suballocations.erase(it: srcSuballocIt);
12895 srcSuballocIt = nextSuballocIt;
12896
12897 InsertSuballoc(pMetadata: pFreeSpaceMetadata, suballoc);
12898
12899 VmaDefragmentationMove move = {
12900 .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: freeSpaceOrigBlockIndex,
12901 .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset,
12902 .size: srcAllocSize };
12903 moves.push_back(src: move);
12904 }
12905 }
12906 else
12907 {
12908 dstAllocOffset = VmaAlignUp(val: dstOffset, align: srcAllocAlignment);
12909
12910 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12911 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12912 dstAllocOffset + srcAllocSize > dstBlockSize)
12913 {
12914 // But before that, register remaining free space at the end of dst block.
12915 freeSpaceDb.Register(blockInfoIndex: dstBlockInfoIndex, offset: dstOffset, size: dstBlockSize - dstOffset);
12916
12917 ++dstBlockInfoIndex;
12918 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12919 pDstBlock = m_pBlockVector->GetBlock(index: dstOrigBlockIndex);
12920 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12921 dstBlockSize = pDstMetadata->GetSize();
12922 dstOffset = 0;
12923 dstAllocOffset = 0;
12924 }
12925
12926 // Same block
12927 if(dstBlockInfoIndex == srcBlockInfoIndex)
12928 {
12929 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12930
12931 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12932
12933 bool skipOver = overlap;
12934 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12935 {
12936 // If destination and source place overlap, skip if it would move it
12937 // by only < 1/64 of its size.
12938 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12939 }
12940
12941 if(skipOver)
12942 {
12943 freeSpaceDb.Register(blockInfoIndex: dstBlockInfoIndex, offset: dstOffset, size: srcAllocOffset - dstOffset);
12944
12945 dstOffset = srcAllocOffset + srcAllocSize;
12946 ++srcSuballocIt;
12947 }
12948 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12949 else
12950 {
12951 srcSuballocIt->offset = dstAllocOffset;
12952 srcSuballocIt->hAllocation->ChangeOffset(newOffset: dstAllocOffset);
12953 dstOffset = dstAllocOffset + srcAllocSize;
12954 m_BytesMoved += srcAllocSize;
12955 ++m_AllocationsMoved;
12956 ++srcSuballocIt;
12957 VmaDefragmentationMove move = {
12958 .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: dstOrigBlockIndex,
12959 .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset,
12960 .size: srcAllocSize };
12961 moves.push_back(src: move);
12962 }
12963 }
12964 // Different block
12965 else
12966 {
12967 // MOVE OPTION 2: Move the allocation to a different block.
12968
12969 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12970 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12971
12972 VmaSuballocation suballoc = *srcSuballocIt;
12973 suballoc.offset = dstAllocOffset;
12974 suballoc.hAllocation->ChangeBlockAllocation(hAllocator: m_hAllocator, block: pDstBlock, offset: dstAllocOffset);
12975 dstOffset = dstAllocOffset + srcAllocSize;
12976 m_BytesMoved += srcAllocSize;
12977 ++m_AllocationsMoved;
12978
12979 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12980 ++nextSuballocIt;
12981 pSrcMetadata->m_Suballocations.erase(it: srcSuballocIt);
12982 srcSuballocIt = nextSuballocIt;
12983
12984 pDstMetadata->m_Suballocations.push_back(value: suballoc);
12985
12986 VmaDefragmentationMove move = {
12987 .srcBlockIndex: srcOrigBlockIndex, .dstBlockIndex: dstOrigBlockIndex,
12988 .srcOffset: srcAllocOffset, .dstOffset: dstAllocOffset,
12989 .size: srcAllocSize };
12990 moves.push_back(src: move);
12991 }
12992 }
12993 }
12994 }
12995
12996 m_BlockInfos.clear();
12997
12998 PostprocessMetadata();
12999
13000 return VK_SUCCESS;
13001}
13002
13003void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13004{
13005 const size_t blockCount = m_pBlockVector->GetBlockCount();
13006 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13007 {
13008 VmaBlockMetadata_Generic* const pMetadata =
13009 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(index: blockIndex)->m_pMetadata;
13010 pMetadata->m_FreeCount = 0;
13011 pMetadata->m_SumFreeSize = pMetadata->GetSize();
13012 pMetadata->m_FreeSuballocationsBySize.clear();
13013 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13014 it != pMetadata->m_Suballocations.end(); )
13015 {
13016 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13017 {
13018 VmaSuballocationList::iterator nextIt = it;
13019 ++nextIt;
13020 pMetadata->m_Suballocations.erase(it);
13021 it = nextIt;
13022 }
13023 else
13024 {
13025 ++it;
13026 }
13027 }
13028 }
13029}
13030
13031void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13032{
13033 const size_t blockCount = m_pBlockVector->GetBlockCount();
13034 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13035 {
13036 VmaBlockMetadata_Generic* const pMetadata =
13037 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(index: blockIndex)->m_pMetadata;
13038 const VkDeviceSize blockSize = pMetadata->GetSize();
13039
13040 // No allocations in this block - entire area is free.
13041 if(pMetadata->m_Suballocations.empty())
13042 {
13043 pMetadata->m_FreeCount = 1;
13044 //pMetadata->m_SumFreeSize is already set to blockSize.
13045 VmaSuballocation suballoc = {
13046 .offset: 0, // offset
13047 .size: blockSize, // size
13048 VMA_NULL, // hAllocation
13049 .type: VMA_SUBALLOCATION_TYPE_FREE };
13050 pMetadata->m_Suballocations.push_back(value: suballoc);
13051 pMetadata->RegisterFreeSuballocation(item: pMetadata->m_Suballocations.begin());
13052 }
13053 // There are some allocations in this block.
13054 else
13055 {
13056 VkDeviceSize offset = 0;
13057 VmaSuballocationList::iterator it;
13058 for(it = pMetadata->m_Suballocations.begin();
13059 it != pMetadata->m_Suballocations.end();
13060 ++it)
13061 {
13062 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13063 VMA_ASSERT(it->offset >= offset);
13064
13065 // Need to insert preceding free space.
13066 if(it->offset > offset)
13067 {
13068 ++pMetadata->m_FreeCount;
13069 const VkDeviceSize freeSize = it->offset - offset;
13070 VmaSuballocation suballoc = {
13071 .offset: offset, // offset
13072 .size: freeSize, // size
13073 VMA_NULL, // hAllocation
13074 .type: VMA_SUBALLOCATION_TYPE_FREE };
13075 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, value: suballoc);
13076 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13077 {
13078 pMetadata->m_FreeSuballocationsBySize.push_back(src: precedingFreeIt);
13079 }
13080 }
13081
13082 pMetadata->m_SumFreeSize -= it->size;
13083 offset = it->offset + it->size;
13084 }
13085
13086 // Need to insert trailing free space.
13087 if(offset < blockSize)
13088 {
13089 ++pMetadata->m_FreeCount;
13090 const VkDeviceSize freeSize = blockSize - offset;
13091 VmaSuballocation suballoc = {
13092 .offset: offset, // offset
13093 .size: freeSize, // size
13094 VMA_NULL, // hAllocation
13095 .type: VMA_SUBALLOCATION_TYPE_FREE };
13096 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13097 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, value: suballoc);
13098 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13099 {
13100 pMetadata->m_FreeSuballocationsBySize.push_back(src: trailingFreeIt);
13101 }
13102 }
13103
13104 VMA_SORT(
13105 pMetadata->m_FreeSuballocationsBySize.begin(),
13106 pMetadata->m_FreeSuballocationsBySize.end(),
13107 VmaSuballocationItemSizeLess());
13108 }
13109
13110 VMA_HEAVY_ASSERT(pMetadata->Validate());
13111 }
13112}
13113
13114void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13115{
13116 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13117 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13118 while(it != pMetadata->m_Suballocations.end())
13119 {
13120 if(it->offset < suballoc.offset)
13121 {
13122 ++it;
13123 }
13124 }
13125 pMetadata->m_Suballocations.insert(it, value: suballoc);
13126}
13127
13128////////////////////////////////////////////////////////////////////////////////
13129// VmaBlockVectorDefragmentationContext
13130
13131VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13132 VmaAllocator hAllocator,
13133 VmaPool hCustomPool,
13134 VmaBlockVector* pBlockVector,
13135 uint32_t currFrameIndex,
13136 uint32_t /*algorithmFlags*/) :
13137 res(VK_SUCCESS),
13138 mutexLocked(false),
13139 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13140 m_hAllocator(hAllocator),
13141 m_hCustomPool(hCustomPool),
13142 m_pBlockVector(pBlockVector),
13143 m_CurrFrameIndex(currFrameIndex),
13144 /*m_AlgorithmFlags(algorithmFlags),*/
13145 m_pAlgorithm(VMA_NULL),
13146 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13147 m_AllAllocations(false)
13148{
13149}
13150
13151VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13152{
13153 vma_delete(hAllocator: m_hAllocator, ptr: m_pAlgorithm);
13154}
13155
13156void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13157{
13158 AllocInfo info = { .hAlloc: hAlloc, .pChanged: pChanged };
13159 m_Allocations.push_back(src: info);
13160}
13161
13162void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13163{
13164 const bool allAllocations = m_AllAllocations ||
13165 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13166
13167 /********************************
13168 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13169 ********************************/
13170
13171 /*
13172 Fast algorithm is supported only when certain criteria are met:
13173 - VMA_DEBUG_MARGIN is 0.
13174 - All allocations in this block vector are moveable.
13175 - There is no possibility of image/buffer granularity conflict.
13176 */
13177 if(VMA_DEBUG_MARGIN == 0 &&
13178 allAllocations &&
13179 !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13180 {
13181 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13182 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13183 }
13184 else
13185 {
13186 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13187 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13188 }
13189
13190 if(allAllocations)
13191 {
13192 m_pAlgorithm->AddAll();
13193 }
13194 else
13195 {
13196 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13197 {
13198 m_pAlgorithm->AddAllocation(hAlloc: m_Allocations[i].hAlloc, pChanged: m_Allocations[i].pChanged);
13199 }
13200 }
13201}
13202
13203////////////////////////////////////////////////////////////////////////////////
13204// VmaDefragmentationContext
13205
13206VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13207 VmaAllocator hAllocator,
13208 uint32_t currFrameIndex,
13209 uint32_t flags,
13210 VmaDefragmentationStats* pStats) :
13211 m_hAllocator(hAllocator),
13212 m_CurrFrameIndex(currFrameIndex),
13213 m_Flags(flags),
13214 m_pStats(pStats),
13215 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13216{
13217 memset(s: m_DefaultPoolContexts, c: 0, n: sizeof(m_DefaultPoolContexts));
13218}
13219
13220VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13221{
13222 for(size_t i = m_CustomPoolContexts.size(); i--; )
13223 {
13224 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13225 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pCtx: pBlockVectorCtx, pStats: m_pStats);
13226 vma_delete(hAllocator: m_hAllocator, ptr: pBlockVectorCtx);
13227 }
13228 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13229 {
13230 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13231 if(pBlockVectorCtx)
13232 {
13233 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pCtx: pBlockVectorCtx, pStats: m_pStats);
13234 vma_delete(hAllocator: m_hAllocator, ptr: pBlockVectorCtx);
13235 }
13236 }
13237}
13238
13239void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13240{
13241 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13242 {
13243 VmaPool pool = pPools[poolIndex];
13244 VMA_ASSERT(pool);
13245 // Pools with algorithm other than default are not defragmented.
13246 if(pool->m_BlockVector.GetAlgorithm() == 0)
13247 {
13248 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13249
13250 for(size_t i = m_CustomPoolContexts.size(); i--; )
13251 {
13252 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13253 {
13254 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13255 break;
13256 }
13257 }
13258
13259 if(!pBlockVectorDefragCtx)
13260 {
13261 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13262 m_hAllocator,
13263 pool,
13264 &pool->m_BlockVector,
13265 m_CurrFrameIndex,
13266 m_Flags);
13267 m_CustomPoolContexts.push_back(src: pBlockVectorDefragCtx);
13268 }
13269
13270 pBlockVectorDefragCtx->AddAll();
13271 }
13272 }
13273}
13274
13275void VmaDefragmentationContext_T::AddAllocations(
13276 uint32_t allocationCount,
13277 VmaAllocation* pAllocations,
13278 VkBool32* pAllocationsChanged)
13279{
13280 // Dispatch pAllocations among defragmentators. Create them when necessary.
13281 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13282 {
13283 const VmaAllocation hAlloc = pAllocations[allocIndex];
13284 VMA_ASSERT(hAlloc);
13285 // DedicatedAlloc cannot be defragmented.
13286 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13287 // Lost allocation cannot be defragmented.
13288 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13289 {
13290 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13291
13292 const VmaPool hAllocPool = hAlloc->GetPool();
13293 // This allocation belongs to custom pool.
13294 if(hAllocPool != VK_NULL_HANDLE)
13295 {
13296 // Pools with algorithm other than default are not defragmented.
13297 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13298 {
13299 for(size_t i = m_CustomPoolContexts.size(); i--; )
13300 {
13301 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13302 {
13303 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13304 break;
13305 }
13306 }
13307 if(!pBlockVectorDefragCtx)
13308 {
13309 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13310 m_hAllocator,
13311 hAllocPool,
13312 &hAllocPool->m_BlockVector,
13313 m_CurrFrameIndex,
13314 m_Flags);
13315 m_CustomPoolContexts.push_back(src: pBlockVectorDefragCtx);
13316 }
13317 }
13318 }
13319 // This allocation belongs to default pool.
13320 else
13321 {
13322 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13323 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13324 if(!pBlockVectorDefragCtx)
13325 {
13326 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13327 m_hAllocator,
13328 VMA_NULL, // hCustomPool
13329 m_hAllocator->m_pBlockVectors[memTypeIndex],
13330 m_CurrFrameIndex,
13331 m_Flags);
13332 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13333 }
13334 }
13335
13336 if(pBlockVectorDefragCtx)
13337 {
13338 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13339 &pAllocationsChanged[allocIndex] : VMA_NULL;
13340 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13341 }
13342 }
13343 }
13344}
13345
13346VkResult VmaDefragmentationContext_T::Defragment(
13347 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13348 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13349 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13350{
13351 if(pStats)
13352 {
13353 memset(s: pStats, c: 0, n: sizeof(VmaDefragmentationStats));
13354 }
13355
13356 if(commandBuffer == VK_NULL_HANDLE)
13357 {
13358 maxGpuBytesToMove = 0;
13359 maxGpuAllocationsToMove = 0;
13360 }
13361
13362 VkResult res = VK_SUCCESS;
13363
13364 // Process default pools.
13365 for(uint32_t memTypeIndex = 0;
13366 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13367 ++memTypeIndex)
13368 {
13369 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13370 if(pBlockVectorCtx)
13371 {
13372 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13373 pBlockVectorCtx->GetBlockVector()->Defragment(
13374 pCtx: pBlockVectorCtx,
13375 pStats,
13376 maxCpuBytesToMove, maxCpuAllocationsToMove,
13377 maxGpuBytesToMove, maxGpuAllocationsToMove,
13378 commandBuffer);
13379 if(pBlockVectorCtx->res != VK_SUCCESS)
13380 {
13381 res = pBlockVectorCtx->res;
13382 }
13383 }
13384 }
13385
13386 // Process custom pools.
13387 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13388 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13389 ++customCtxIndex)
13390 {
13391 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13392 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13393 pBlockVectorCtx->GetBlockVector()->Defragment(
13394 pCtx: pBlockVectorCtx,
13395 pStats,
13396 maxCpuBytesToMove, maxCpuAllocationsToMove,
13397 maxGpuBytesToMove, maxGpuAllocationsToMove,
13398 commandBuffer);
13399 if(pBlockVectorCtx->res != VK_SUCCESS)
13400 {
13401 res = pBlockVectorCtx->res;
13402 }
13403 }
13404
13405 return res;
13406}
13407
13408////////////////////////////////////////////////////////////////////////////////
13409// VmaRecorder
13410
13411#if VMA_RECORDING_ENABLED
13412
13413VmaRecorder::VmaRecorder() :
13414 m_UseMutex(true),
13415 m_Flags(0),
13416 m_File(VMA_NULL),
13417 m_Freq(INT64_MAX),
13418 m_StartCounter(INT64_MAX)
13419{
13420}
13421
13422VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13423{
13424 m_UseMutex = useMutex;
13425 m_Flags = settings.flags;
13426
13427 QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13428 QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13429
13430 // Open file for writing.
13431 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13432 if(err != 0)
13433 {
13434 return VK_ERROR_INITIALIZATION_FAILED;
13435 }
13436
13437 // Write header.
13438 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13439 fprintf(m_File, "%s\n", "1,5");
13440
13441 return VK_SUCCESS;
13442}
13443
13444VmaRecorder::~VmaRecorder()
13445{
13446 if(m_File != VMA_NULL)
13447 {
13448 fclose(m_File);
13449 }
13450}
13451
13452void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13453{
13454 CallParams callParams;
13455 GetBasicParams(callParams);
13456
13457 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13458 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13459 Flush();
13460}
13461
13462void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13463{
13464 CallParams callParams;
13465 GetBasicParams(callParams);
13466
13467 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13468 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13469 Flush();
13470}
13471
13472void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13473{
13474 CallParams callParams;
13475 GetBasicParams(callParams);
13476
13477 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13478 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13479 createInfo.memoryTypeIndex,
13480 createInfo.flags,
13481 createInfo.blockSize,
13482 (uint64_t)createInfo.minBlockCount,
13483 (uint64_t)createInfo.maxBlockCount,
13484 createInfo.frameInUseCount,
13485 pool);
13486 Flush();
13487}
13488
13489void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13490{
13491 CallParams callParams;
13492 GetBasicParams(callParams);
13493
13494 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13495 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13496 pool);
13497 Flush();
13498}
13499
13500void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13501 const VkMemoryRequirements& vkMemReq,
13502 const VmaAllocationCreateInfo& createInfo,
13503 VmaAllocation allocation)
13504{
13505 CallParams callParams;
13506 GetBasicParams(callParams);
13507
13508 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13509 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13510 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13511 vkMemReq.size,
13512 vkMemReq.alignment,
13513 vkMemReq.memoryTypeBits,
13514 createInfo.flags,
13515 createInfo.usage,
13516 createInfo.requiredFlags,
13517 createInfo.preferredFlags,
13518 createInfo.memoryTypeBits,
13519 createInfo.pool,
13520 allocation,
13521 userDataStr.GetString());
13522 Flush();
13523}
13524
13525void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13526 const VkMemoryRequirements& vkMemReq,
13527 const VmaAllocationCreateInfo& createInfo,
13528 uint64_t allocationCount,
13529 const VmaAllocation* pAllocations)
13530{
13531 CallParams callParams;
13532 GetBasicParams(callParams);
13533
13534 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13535 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13536 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13537 vkMemReq.size,
13538 vkMemReq.alignment,
13539 vkMemReq.memoryTypeBits,
13540 createInfo.flags,
13541 createInfo.usage,
13542 createInfo.requiredFlags,
13543 createInfo.preferredFlags,
13544 createInfo.memoryTypeBits,
13545 createInfo.pool);
13546 PrintPointerList(allocationCount, pAllocations);
13547 fprintf(m_File, ",%s\n", userDataStr.GetString());
13548 Flush();
13549}
13550
13551void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13552 const VkMemoryRequirements& vkMemReq,
13553 bool requiresDedicatedAllocation,
13554 bool prefersDedicatedAllocation,
13555 const VmaAllocationCreateInfo& createInfo,
13556 VmaAllocation allocation)
13557{
13558 CallParams callParams;
13559 GetBasicParams(callParams);
13560
13561 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13562 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13563 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,
13564 vkMemReq.size,
13565 vkMemReq.alignment,
13566 vkMemReq.memoryTypeBits,
13567 requiresDedicatedAllocation ? 1 : 0,
13568 prefersDedicatedAllocation ? 1 : 0,
13569 createInfo.flags,
13570 createInfo.usage,
13571 createInfo.requiredFlags,
13572 createInfo.preferredFlags,
13573 createInfo.memoryTypeBits,
13574 createInfo.pool,
13575 allocation,
13576 userDataStr.GetString());
13577 Flush();
13578}
13579
13580void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13581 const VkMemoryRequirements& vkMemReq,
13582 bool requiresDedicatedAllocation,
13583 bool prefersDedicatedAllocation,
13584 const VmaAllocationCreateInfo& createInfo,
13585 VmaAllocation allocation)
13586{
13587 CallParams callParams;
13588 GetBasicParams(callParams);
13589
13590 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13591 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13592 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,
13593 vkMemReq.size,
13594 vkMemReq.alignment,
13595 vkMemReq.memoryTypeBits,
13596 requiresDedicatedAllocation ? 1 : 0,
13597 prefersDedicatedAllocation ? 1 : 0,
13598 createInfo.flags,
13599 createInfo.usage,
13600 createInfo.requiredFlags,
13601 createInfo.preferredFlags,
13602 createInfo.memoryTypeBits,
13603 createInfo.pool,
13604 allocation,
13605 userDataStr.GetString());
13606 Flush();
13607}
13608
13609void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13610 VmaAllocation allocation)
13611{
13612 CallParams callParams;
13613 GetBasicParams(callParams);
13614
13615 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13616 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13617 allocation);
13618 Flush();
13619}
13620
13621void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13622 uint64_t allocationCount,
13623 const VmaAllocation* pAllocations)
13624{
13625 CallParams callParams;
13626 GetBasicParams(callParams);
13627
13628 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13629 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13630 PrintPointerList(allocationCount, pAllocations);
13631 fprintf(m_File, "\n");
13632 Flush();
13633}
13634
13635void VmaRecorder::RecordResizeAllocation(
13636 uint32_t frameIndex,
13637 VmaAllocation allocation,
13638 VkDeviceSize newSize)
13639{
13640 CallParams callParams;
13641 GetBasicParams(callParams);
13642
13643 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13644 fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13645 allocation, newSize);
13646 Flush();
13647}
13648
13649void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13650 VmaAllocation allocation,
13651 const void* pUserData)
13652{
13653 CallParams callParams;
13654 GetBasicParams(callParams);
13655
13656 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13657 UserDataString userDataStr(
13658 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13659 pUserData);
13660 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13661 allocation,
13662 userDataStr.GetString());
13663 Flush();
13664}
13665
13666void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13667 VmaAllocation allocation)
13668{
13669 CallParams callParams;
13670 GetBasicParams(callParams);
13671
13672 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13673 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13674 allocation);
13675 Flush();
13676}
13677
13678void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13679 VmaAllocation allocation)
13680{
13681 CallParams callParams;
13682 GetBasicParams(callParams);
13683
13684 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13685 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13686 allocation);
13687 Flush();
13688}
13689
13690void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13691 VmaAllocation allocation)
13692{
13693 CallParams callParams;
13694 GetBasicParams(callParams);
13695
13696 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13697 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13698 allocation);
13699 Flush();
13700}
13701
13702void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13703 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13704{
13705 CallParams callParams;
13706 GetBasicParams(callParams);
13707
13708 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13709 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13710 allocation,
13711 offset,
13712 size);
13713 Flush();
13714}
13715
13716void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13717 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13718{
13719 CallParams callParams;
13720 GetBasicParams(callParams);
13721
13722 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13723 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13724 allocation,
13725 offset,
13726 size);
13727 Flush();
13728}
13729
13730void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13731 const VkBufferCreateInfo& bufCreateInfo,
13732 const VmaAllocationCreateInfo& allocCreateInfo,
13733 VmaAllocation allocation)
13734{
13735 CallParams callParams;
13736 GetBasicParams(callParams);
13737
13738 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13739 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13740 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,
13741 bufCreateInfo.flags,
13742 bufCreateInfo.size,
13743 bufCreateInfo.usage,
13744 bufCreateInfo.sharingMode,
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::RecordCreateImage(uint32_t frameIndex,
13757 const VkImageCreateInfo& imageCreateInfo,
13758 const VmaAllocationCreateInfo& allocCreateInfo,
13759 VmaAllocation allocation)
13760{
13761 CallParams callParams;
13762 GetBasicParams(callParams);
13763
13764 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13765 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13766 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,
13767 imageCreateInfo.flags,
13768 imageCreateInfo.imageType,
13769 imageCreateInfo.format,
13770 imageCreateInfo.extent.width,
13771 imageCreateInfo.extent.height,
13772 imageCreateInfo.extent.depth,
13773 imageCreateInfo.mipLevels,
13774 imageCreateInfo.arrayLayers,
13775 imageCreateInfo.samples,
13776 imageCreateInfo.tiling,
13777 imageCreateInfo.usage,
13778 imageCreateInfo.sharingMode,
13779 imageCreateInfo.initialLayout,
13780 allocCreateInfo.flags,
13781 allocCreateInfo.usage,
13782 allocCreateInfo.requiredFlags,
13783 allocCreateInfo.preferredFlags,
13784 allocCreateInfo.memoryTypeBits,
13785 allocCreateInfo.pool,
13786 allocation,
13787 userDataStr.GetString());
13788 Flush();
13789}
13790
13791void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13792 VmaAllocation allocation)
13793{
13794 CallParams callParams;
13795 GetBasicParams(callParams);
13796
13797 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13798 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13799 allocation);
13800 Flush();
13801}
13802
13803void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13804 VmaAllocation allocation)
13805{
13806 CallParams callParams;
13807 GetBasicParams(callParams);
13808
13809 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13810 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13811 allocation);
13812 Flush();
13813}
13814
13815void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13816 VmaAllocation allocation)
13817{
13818 CallParams callParams;
13819 GetBasicParams(callParams);
13820
13821 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13822 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13823 allocation);
13824 Flush();
13825}
13826
13827void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13828 VmaAllocation allocation)
13829{
13830 CallParams callParams;
13831 GetBasicParams(callParams);
13832
13833 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13834 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13835 allocation);
13836 Flush();
13837}
13838
13839void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13840 VmaPool pool)
13841{
13842 CallParams callParams;
13843 GetBasicParams(callParams);
13844
13845 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13846 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13847 pool);
13848 Flush();
13849}
13850
13851void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13852 const VmaDefragmentationInfo2& info,
13853 VmaDefragmentationContext ctx)
13854{
13855 CallParams callParams;
13856 GetBasicParams(callParams);
13857
13858 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13859 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13860 info.flags);
13861 PrintPointerList(info.allocationCount, info.pAllocations);
13862 fprintf(m_File, ",");
13863 PrintPointerList(info.poolCount, info.pPools);
13864 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13865 info.maxCpuBytesToMove,
13866 info.maxCpuAllocationsToMove,
13867 info.maxGpuBytesToMove,
13868 info.maxGpuAllocationsToMove,
13869 info.commandBuffer,
13870 ctx);
13871 Flush();
13872}
13873
13874void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13875 VmaDefragmentationContext ctx)
13876{
13877 CallParams callParams;
13878 GetBasicParams(callParams);
13879
13880 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13881 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13882 ctx);
13883 Flush();
13884}
13885
13886VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13887{
13888 if(pUserData != VMA_NULL)
13889 {
13890 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13891 {
13892 m_Str = (const char*)pUserData;
13893 }
13894 else
13895 {
13896 sprintf_s(m_PtrStr, "%p", pUserData);
13897 m_Str = m_PtrStr;
13898 }
13899 }
13900 else
13901 {
13902 m_Str = "";
13903 }
13904}
13905
13906void VmaRecorder::WriteConfiguration(
13907 const VkPhysicalDeviceProperties& devProps,
13908 const VkPhysicalDeviceMemoryProperties& memProps,
13909 bool dedicatedAllocationExtensionEnabled)
13910{
13911 fprintf(m_File, "Config,Begin\n");
13912
13913 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13914 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13915 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13916 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13917 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13918 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13919
13920 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13921 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13922 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13923
13924 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13925 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13926 {
13927 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13928 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13929 }
13930 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13931 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13932 {
13933 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13934 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13935 }
13936
13937 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13938
13939 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13940 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13941 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13942 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13943 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13944 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13945 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13946 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13947 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13948
13949 fprintf(m_File, "Config,End\n");
13950}
13951
13952void VmaRecorder::GetBasicParams(CallParams& outParams)
13953{
13954 outParams.threadId = GetCurrentThreadId();
13955
13956 LARGE_INTEGER counter;
13957 QueryPerformanceCounter(&counter);
13958 outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13959}
13960
13961void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13962{
13963 if(count)
13964 {
13965 fprintf(m_File, "%p", pItems[0]);
13966 for(uint64_t i = 1; i < count; ++i)
13967 {
13968 fprintf(m_File, " %p", pItems[i]);
13969 }
13970 }
13971}
13972
13973void VmaRecorder::Flush()
13974{
13975 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13976 {
13977 fflush(m_File);
13978 }
13979}
13980
13981#endif // #if VMA_RECORDING_ENABLED
13982
13983////////////////////////////////////////////////////////////////////////////////
13984// VmaAllocator_T
13985
13986VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13987 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13988 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13989 m_hDevice(pCreateInfo->device),
13990 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13991 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13992 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13993 m_PreferredLargeHeapBlockSize(0),
13994 m_PhysicalDevice(pCreateInfo->physicalDevice),
13995 m_CurrentFrameIndex(0),
13996 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13997 m_NextPoolId(0)
13998#if VMA_RECORDING_ENABLED
13999 ,m_pRecorder(VMA_NULL)
14000#endif
14001{
14002 if(VMA_DEBUG_DETECT_CORRUPTION)
14003 {
14004 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14005 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14006 }
14007
14008 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
14009
14010#if !(VMA_DEDICATED_ALLOCATION)
14011 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14012 {
14013 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14014 }
14015#endif
14016
14017 memset(s: &m_DeviceMemoryCallbacks, c: 0 ,n: sizeof(m_DeviceMemoryCallbacks));
14018 memset(s: &m_PhysicalDeviceProperties, c: 0, n: sizeof(m_PhysicalDeviceProperties));
14019 memset(s: &m_MemProps, c: 0, n: sizeof(m_MemProps));
14020
14021 memset(s: &m_pBlockVectors, c: 0, n: sizeof(m_pBlockVectors));
14022 memset(s: &m_pDedicatedAllocations, c: 0, n: sizeof(m_pDedicatedAllocations));
14023
14024 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14025 {
14026 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14027 }
14028
14029 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14030 {
14031 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14032 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14033 }
14034
14035 ImportVulkanFunctions(pVulkanFunctions: pCreateInfo->pVulkanFunctions);
14036
14037 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14038 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14039
14040 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14041 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14042 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14043 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14044
14045 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14046 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14047
14048 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14049 {
14050 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14051 {
14052 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14053 if(limit != VK_WHOLE_SIZE)
14054 {
14055 m_HeapSizeLimit[heapIndex] = limit;
14056 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14057 {
14058 m_MemProps.memoryHeaps[heapIndex].size = limit;
14059 }
14060 }
14061 }
14062 }
14063
14064 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14065 {
14066 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14067
14068 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14069 this,
14070 memTypeIndex,
14071 preferredBlockSize,
14072 0,
14073 SIZE_MAX,
14074 GetBufferImageGranularity(),
14075 pCreateInfo->frameInUseCount,
14076 false, // isCustomPool
14077 false, // explicitBlockSize
14078 false); // linearAlgorithm
14079 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14080 // becase minBlockCount is 0.
14081 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14082
14083 }
14084}
14085
14086VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14087{
14088 VkResult res = VK_SUCCESS;
14089
14090 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14091 !VmaStrIsEmpty(pStr: pCreateInfo->pRecordSettings->pFilePath))
14092 {
14093#if VMA_RECORDING_ENABLED
14094 m_pRecorder = vma_new(this, VmaRecorder)();
14095 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14096 if(res != VK_SUCCESS)
14097 {
14098 return res;
14099 }
14100 m_pRecorder->WriteConfiguration(
14101 m_PhysicalDeviceProperties,
14102 m_MemProps,
14103 m_UseKhrDedicatedAllocation);
14104 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14105#else
14106 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14107 return VK_ERROR_FEATURE_NOT_PRESENT;
14108#endif
14109 }
14110
14111 return res;
14112}
14113
14114VmaAllocator_T::~VmaAllocator_T()
14115{
14116#if VMA_RECORDING_ENABLED
14117 if(m_pRecorder != VMA_NULL)
14118 {
14119 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14120 vma_delete(this, m_pRecorder);
14121 }
14122#endif
14123
14124 VMA_ASSERT(m_Pools.empty());
14125
14126 for(size_t i = GetMemoryTypeCount(); i--; )
14127 {
14128 vma_delete(hAllocator: this, ptr: m_pDedicatedAllocations[i]);
14129 vma_delete(hAllocator: this, ptr: m_pBlockVectors[i]);
14130 }
14131}
14132
14133void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14134{
14135#if VMA_STATIC_VULKAN_FUNCTIONS == 1
14136 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14137 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14138 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14139 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14140 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14141 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14142 m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14143 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14144 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14145 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14146 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14147 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14148 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14149 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14150 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14151 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14152 m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14153#if VMA_DEDICATED_ALLOCATION
14154 if(m_UseKhrDedicatedAllocation)
14155 {
14156 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14157 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14158 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14159 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14160 }
14161#endif // #if VMA_DEDICATED_ALLOCATION
14162#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14163
14164#define VMA_COPY_IF_NOT_NULL(funcName) \
14165 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14166
14167 if(pVulkanFunctions != VMA_NULL)
14168 {
14169 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14170 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14171 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14172 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14173 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14174 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14175 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14176 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14177 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14178 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14179 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14180 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14181 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14182 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14183 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14184 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14185 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14186#if VMA_DEDICATED_ALLOCATION
14187 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14188 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14189#endif
14190 }
14191
14192#undef VMA_COPY_IF_NOT_NULL
14193
14194 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14195 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14196 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14197 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14198 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14199 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14200 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14201 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14202 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14203 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14204 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14205 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14206 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14207 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14208 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14209 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14210 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14211 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14212 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14213#if VMA_DEDICATED_ALLOCATION
14214 if(m_UseKhrDedicatedAllocation)
14215 {
14216 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14217 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14218 }
14219#endif
14220}
14221
14222VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14223{
14224 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14225 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14226 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14227 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14228}
14229
14230VkResult VmaAllocator_T::AllocateMemoryOfType(
14231 VkDeviceSize size,
14232 VkDeviceSize alignment,
14233 bool dedicatedAllocation,
14234 VkBuffer dedicatedBuffer,
14235 VkImage dedicatedImage,
14236 const VmaAllocationCreateInfo& createInfo,
14237 uint32_t memTypeIndex,
14238 VmaSuballocationType suballocType,
14239 size_t allocationCount,
14240 VmaAllocation* pAllocations)
14241{
14242 VMA_ASSERT(pAllocations != VMA_NULL);
14243 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14244
14245 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14246
14247 // If memory type is not HOST_VISIBLE, disable MAPPED.
14248 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14249 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14250 {
14251 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14252 }
14253
14254 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14255 VMA_ASSERT(blockVector);
14256
14257 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14258 bool preferDedicatedMemory =
14259 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14260 dedicatedAllocation ||
14261 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14262 size > preferredBlockSize / 2;
14263
14264 if(preferDedicatedMemory &&
14265 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14266 finalCreateInfo.pool == VK_NULL_HANDLE)
14267 {
14268 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14269 }
14270
14271 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14272 {
14273 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14274 {
14275 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14276 }
14277 else
14278 {
14279 return AllocateDedicatedMemory(
14280 size,
14281 suballocType,
14282 memTypeIndex,
14283 map: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14284 isUserDataString: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14285 pUserData: finalCreateInfo.pUserData,
14286 dedicatedBuffer,
14287 dedicatedImage,
14288 allocationCount,
14289 pAllocations);
14290 }
14291 }
14292 else
14293 {
14294 VkResult res = blockVector->Allocate(
14295 VK_NULL_HANDLE, // hCurrentPool
14296 currentFrameIndex: m_CurrentFrameIndex.load(),
14297 size,
14298 alignment,
14299 createInfo: finalCreateInfo,
14300 suballocType,
14301 allocationCount,
14302 pAllocations);
14303 if(res == VK_SUCCESS)
14304 {
14305 return res;
14306 }
14307
14308 // 5. Try dedicated memory.
14309 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14310 {
14311 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14312 }
14313 else
14314 {
14315 res = AllocateDedicatedMemory(
14316 size,
14317 suballocType,
14318 memTypeIndex,
14319 map: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14320 isUserDataString: (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14321 pUserData: finalCreateInfo.pUserData,
14322 dedicatedBuffer,
14323 dedicatedImage,
14324 allocationCount,
14325 pAllocations);
14326 if(res == VK_SUCCESS)
14327 {
14328 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14329 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14330 return VK_SUCCESS;
14331 }
14332 else
14333 {
14334 // Everything failed: Return error code.
14335 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14336 return res;
14337 }
14338 }
14339 }
14340}
14341
14342VkResult VmaAllocator_T::AllocateDedicatedMemory(
14343 VkDeviceSize size,
14344 VmaSuballocationType suballocType,
14345 uint32_t memTypeIndex,
14346 bool map,
14347 bool isUserDataString,
14348 void* pUserData,
14349 VkBuffer /*dedicatedBuffer*/,
14350 VkImage /*dedicatedImage*/,
14351 size_t allocationCount,
14352 VmaAllocation* pAllocations)
14353{
14354 VMA_ASSERT(allocationCount > 0 && pAllocations);
14355
14356 VkMemoryAllocateInfo allocInfo = {};
14357 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
14358 allocInfo.memoryTypeIndex = memTypeIndex;
14359 allocInfo.allocationSize = size;
14360
14361#if VMA_DEDICATED_ALLOCATION
14362 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {};
14363 dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
14364 if(m_UseKhrDedicatedAllocation)
14365 {
14366 if(dedicatedBuffer != VK_NULL_HANDLE)
14367 {
14368 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14369 dedicatedAllocInfo.buffer = dedicatedBuffer;
14370 allocInfo.pNext = &dedicatedAllocInfo;
14371 }
14372 else if(dedicatedImage != VK_NULL_HANDLE)
14373 {
14374 dedicatedAllocInfo.image = dedicatedImage;
14375 allocInfo.pNext = &dedicatedAllocInfo;
14376 }
14377 }
14378#endif // #if VMA_DEDICATED_ALLOCATION
14379
14380 size_t allocIndex;
14381 VkResult res = VK_SUCCESS;
14382 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14383 {
14384 res = AllocateDedicatedMemoryPage(
14385 size,
14386 suballocType,
14387 memTypeIndex,
14388 allocInfo,
14389 map,
14390 isUserDataString,
14391 pUserData,
14392 pAllocation: pAllocations + allocIndex);
14393 if(res != VK_SUCCESS)
14394 {
14395 break;
14396 }
14397 }
14398
14399 if(res == VK_SUCCESS)
14400 {
14401 // Register them in m_pDedicatedAllocations.
14402 {
14403 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14404 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14405 VMA_ASSERT(pDedicatedAllocations);
14406 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14407 {
14408 VmaVectorInsertSorted<VmaPointerLess>(vector&: *pDedicatedAllocations, value: pAllocations[allocIndex]);
14409 }
14410 }
14411
14412 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14413 }
14414 else
14415 {
14416 // Free all already created allocations.
14417 while(allocIndex--)
14418 {
14419 VmaAllocation currAlloc = pAllocations[allocIndex];
14420 VkDeviceMemory hMemory = currAlloc->GetMemory();
14421
14422 /*
14423 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14424 before vkFreeMemory.
14425
14426 if(currAlloc->GetMappedData() != VMA_NULL)
14427 {
14428 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14429 }
14430 */
14431
14432 FreeVulkanMemory(memoryType: memTypeIndex, size: currAlloc->GetSize(), hMemory);
14433
14434 currAlloc->SetUserData(hAllocator: this, VMA_NULL);
14435 vma_delete(hAllocator: this, ptr: currAlloc);
14436 }
14437
14438 memset(s: pAllocations, c: 0, n: sizeof(VmaAllocation) * allocationCount);
14439 }
14440
14441 return res;
14442}
14443
14444VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14445 VkDeviceSize size,
14446 VmaSuballocationType suballocType,
14447 uint32_t memTypeIndex,
14448 const VkMemoryAllocateInfo& allocInfo,
14449 bool map,
14450 bool isUserDataString,
14451 void* pUserData,
14452 VmaAllocation* pAllocation)
14453{
14454 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14455 VkResult res = AllocateVulkanMemory(pAllocateInfo: &allocInfo, pMemory: &hMemory);
14456 if(res < 0)
14457 {
14458 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14459 return res;
14460 }
14461
14462 void* pMappedData = VMA_NULL;
14463 if(map)
14464 {
14465 res = (*m_VulkanFunctions.vkMapMemory)(
14466 m_hDevice,
14467 hMemory,
14468 0,
14469 VK_WHOLE_SIZE,
14470 0,
14471 &pMappedData);
14472 if(res < 0)
14473 {
14474 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14475 FreeVulkanMemory(memoryType: memTypeIndex, size, hMemory);
14476 return res;
14477 }
14478 }
14479
14480 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14481 (*pAllocation)->InitDedicatedAllocation(memoryTypeIndex: memTypeIndex, hMemory, suballocationType: suballocType, pMappedData, size);
14482 (*pAllocation)->SetUserData(hAllocator: this, pUserData);
14483 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14484 {
14485 FillAllocation(hAllocation: *pAllocation, pattern: VMA_ALLOCATION_FILL_PATTERN_CREATED);
14486 }
14487
14488 return VK_SUCCESS;
14489}
14490
14491void VmaAllocator_T::GetBufferMemoryRequirements(
14492 VkBuffer hBuffer,
14493 VkMemoryRequirements& memReq,
14494 bool& requiresDedicatedAllocation,
14495 bool& prefersDedicatedAllocation) const
14496{
14497#if VMA_DEDICATED_ALLOCATION
14498 if(m_UseKhrDedicatedAllocation)
14499 {
14500 VkBufferMemoryRequirementsInfo2KHR memReqInfo = {};
14501 memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR;
14502 memReqInfo.buffer = hBuffer;
14503
14504 VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
14505 memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
14506
14507 VkMemoryRequirements2KHR memReq2 = {};
14508 memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
14509 memReq2.pNext = &memDedicatedReq;
14510
14511 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14512
14513 memReq = memReq2.memoryRequirements;
14514 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14515 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14516 }
14517 else
14518#endif // #if VMA_DEDICATED_ALLOCATION
14519 {
14520 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14521 requiresDedicatedAllocation = false;
14522 prefersDedicatedAllocation = false;
14523 }
14524}
14525
14526void VmaAllocator_T::GetImageMemoryRequirements(
14527 VkImage hImage,
14528 VkMemoryRequirements& memReq,
14529 bool& requiresDedicatedAllocation,
14530 bool& prefersDedicatedAllocation) const
14531{
14532#if VMA_DEDICATED_ALLOCATION
14533 if(m_UseKhrDedicatedAllocation)
14534 {
14535 VkImageMemoryRequirementsInfo2KHR memReqInfo = {};
14536 memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR;
14537 memReqInfo.image = hImage;
14538
14539 VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
14540 memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
14541
14542 VkMemoryRequirements2KHR memReq2 = {};
14543 memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
14544 memReq2.pNext = &memDedicatedReq;
14545
14546 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14547
14548 memReq = memReq2.memoryRequirements;
14549 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14550 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14551 }
14552 else
14553#endif // #if VMA_DEDICATED_ALLOCATION
14554 {
14555 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14556 requiresDedicatedAllocation = false;
14557 prefersDedicatedAllocation = false;
14558 }
14559}
14560
14561VkResult VmaAllocator_T::AllocateMemory(
14562 const VkMemoryRequirements& vkMemReq,
14563 bool requiresDedicatedAllocation,
14564 bool prefersDedicatedAllocation,
14565 VkBuffer dedicatedBuffer,
14566 VkImage dedicatedImage,
14567 const VmaAllocationCreateInfo& createInfo,
14568 VmaSuballocationType suballocType,
14569 size_t allocationCount,
14570 VmaAllocation* pAllocations)
14571{
14572 memset(s: pAllocations, c: 0, n: sizeof(VmaAllocation) * allocationCount);
14573
14574 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14575
14576 if(vkMemReq.size == 0)
14577 {
14578 return VK_ERROR_VALIDATION_FAILED_EXT;
14579 }
14580 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14581 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14582 {
14583 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14584 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14585 }
14586 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14587 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14588 {
14589 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14590 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14591 }
14592 if(requiresDedicatedAllocation)
14593 {
14594 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14595 {
14596 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14597 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14598 }
14599 if(createInfo.pool != VK_NULL_HANDLE)
14600 {
14601 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14602 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14603 }
14604 }
14605 if((createInfo.pool != VK_NULL_HANDLE) &&
14606 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14607 {
14608 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14609 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14610 }
14611
14612 if(createInfo.pool != VK_NULL_HANDLE)
14613 {
14614 const VkDeviceSize alignmentForPool = VMA_MAX(
14615 vkMemReq.alignment,
14616 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14617 return createInfo.pool->m_BlockVector.Allocate(
14618 hCurrentPool: createInfo.pool,
14619 currentFrameIndex: m_CurrentFrameIndex.load(),
14620 size: vkMemReq.size,
14621 alignment: alignmentForPool,
14622 createInfo,
14623 suballocType,
14624 allocationCount,
14625 pAllocations);
14626 }
14627 else
14628 {
14629 // Bit mask of memory Vulkan types acceptable for this allocation.
14630 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14631 uint32_t memTypeIndex = UINT32_MAX;
14632 VkResult res = vmaFindMemoryTypeIndex(allocator: this, memoryTypeBits, pAllocationCreateInfo: &createInfo, pMemoryTypeIndex: &memTypeIndex);
14633 if(res == VK_SUCCESS)
14634 {
14635 VkDeviceSize alignmentForMemType = VMA_MAX(
14636 vkMemReq.alignment,
14637 GetMemoryTypeMinAlignment(memTypeIndex));
14638
14639 res = AllocateMemoryOfType(
14640 size: vkMemReq.size,
14641 alignment: alignmentForMemType,
14642 dedicatedAllocation: requiresDedicatedAllocation || prefersDedicatedAllocation,
14643 dedicatedBuffer,
14644 dedicatedImage,
14645 createInfo,
14646 memTypeIndex,
14647 suballocType,
14648 allocationCount,
14649 pAllocations);
14650 // Succeeded on first try.
14651 if(res == VK_SUCCESS)
14652 {
14653 return res;
14654 }
14655 // Allocation from this memory type failed. Try other compatible memory types.
14656 else
14657 {
14658 for(;;)
14659 {
14660 // Remove old memTypeIndex from list of possibilities.
14661 memoryTypeBits &= ~(1u << memTypeIndex);
14662 // Find alternative memTypeIndex.
14663 res = vmaFindMemoryTypeIndex(allocator: this, memoryTypeBits, pAllocationCreateInfo: &createInfo, pMemoryTypeIndex: &memTypeIndex);
14664 if(res == VK_SUCCESS)
14665 {
14666 alignmentForMemType = VMA_MAX(
14667 vkMemReq.alignment,
14668 GetMemoryTypeMinAlignment(memTypeIndex));
14669
14670 res = AllocateMemoryOfType(
14671 size: vkMemReq.size,
14672 alignment: alignmentForMemType,
14673 dedicatedAllocation: requiresDedicatedAllocation || prefersDedicatedAllocation,
14674 dedicatedBuffer,
14675 dedicatedImage,
14676 createInfo,
14677 memTypeIndex,
14678 suballocType,
14679 allocationCount,
14680 pAllocations);
14681 // Allocation from this alternative memory type succeeded.
14682 if(res == VK_SUCCESS)
14683 {
14684 return res;
14685 }
14686 // else: Allocation from this memory type failed. Try next one - next loop iteration.
14687 }
14688 // No other matching memory type index could be found.
14689 else
14690 {
14691 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14692 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14693 }
14694 }
14695 }
14696 }
14697 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14698 else
14699 return res;
14700 }
14701}
14702
14703void VmaAllocator_T::FreeMemory(
14704 size_t allocationCount,
14705 const VmaAllocation* pAllocations)
14706{
14707 VMA_ASSERT(pAllocations);
14708
14709 for(size_t allocIndex = allocationCount; allocIndex--; )
14710 {
14711 VmaAllocation allocation = pAllocations[allocIndex];
14712
14713 if(allocation != VK_NULL_HANDLE)
14714 {
14715 if(TouchAllocation(hAllocation: allocation))
14716 {
14717 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14718 {
14719 FillAllocation(hAllocation: allocation, pattern: VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14720 }
14721
14722 switch(allocation->GetType())
14723 {
14724 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14725 {
14726 VmaBlockVector* pBlockVector = VMA_NULL;
14727 VmaPool hPool = allocation->GetPool();
14728 if(hPool != VK_NULL_HANDLE)
14729 {
14730 pBlockVector = &hPool->m_BlockVector;
14731 }
14732 else
14733 {
14734 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14735 pBlockVector = m_pBlockVectors[memTypeIndex];
14736 }
14737 pBlockVector->Free(hAllocation: allocation);
14738 }
14739 break;
14740 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14741 FreeDedicatedMemory(allocation);
14742 break;
14743 default:
14744 VMA_ASSERT(0);
14745 }
14746 }
14747
14748 allocation->SetUserData(hAllocator: this, VMA_NULL);
14749 vma_delete(hAllocator: this, ptr: allocation);
14750 }
14751 }
14752}
14753
14754VkResult VmaAllocator_T::ResizeAllocation(
14755 const VmaAllocation alloc,
14756 VkDeviceSize newSize)
14757{
14758 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14759 {
14760 return VK_ERROR_VALIDATION_FAILED_EXT;
14761 }
14762 if(newSize == alloc->GetSize())
14763 {
14764 return VK_SUCCESS;
14765 }
14766
14767 switch(alloc->GetType())
14768 {
14769 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14770 return VK_ERROR_FEATURE_NOT_PRESENT;
14771 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14772 if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14773 {
14774 alloc->ChangeSize(newSize);
14775 VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14776 return VK_SUCCESS;
14777 }
14778 else
14779 {
14780 return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY
14781 }
14782 default:
14783 VMA_ASSERT(0);
14784 return VK_ERROR_VALIDATION_FAILED_EXT;
14785 }
14786}
14787
14788void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14789{
14790 // Initialize.
14791 InitStatInfo(outInfo&: pStats->total);
14792 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14793 InitStatInfo(outInfo&: pStats->memoryType[i]);
14794 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14795 InitStatInfo(outInfo&: pStats->memoryHeap[i]);
14796
14797 // Process default pools.
14798 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14799 {
14800 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14801 VMA_ASSERT(pBlockVector);
14802 pBlockVector->AddStats(pStats);
14803 }
14804
14805 // Process custom pools.
14806 {
14807 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14808 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14809 {
14810 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14811 }
14812 }
14813
14814 // Process dedicated allocations.
14815 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14816 {
14817 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14818 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14819 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14820 VMA_ASSERT(pDedicatedAllocVector);
14821 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14822 {
14823 VmaStatInfo allocationStatInfo;
14824 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(outInfo&: allocationStatInfo);
14825 VmaAddStatInfo(inoutInfo&: pStats->total, srcInfo: allocationStatInfo);
14826 VmaAddStatInfo(inoutInfo&: pStats->memoryType[memTypeIndex], srcInfo: allocationStatInfo);
14827 VmaAddStatInfo(inoutInfo&: pStats->memoryHeap[memHeapIndex], srcInfo: allocationStatInfo);
14828 }
14829 }
14830
14831 // Postprocess.
14832 VmaPostprocessCalcStatInfo(inoutInfo&: pStats->total);
14833 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14834 VmaPostprocessCalcStatInfo(inoutInfo&: pStats->memoryType[i]);
14835 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14836 VmaPostprocessCalcStatInfo(inoutInfo&: pStats->memoryHeap[i]);
14837}
14838
14839static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14840
14841VkResult VmaAllocator_T::DefragmentationBegin(
14842 const VmaDefragmentationInfo2& info,
14843 VmaDefragmentationStats* pStats,
14844 VmaDefragmentationContext* pContext)
14845{
14846 if(info.pAllocationsChanged != VMA_NULL)
14847 {
14848 memset(s: info.pAllocationsChanged, c: 0, n: info.allocationCount * sizeof(VkBool32));
14849 }
14850
14851 *pContext = vma_new(this, VmaDefragmentationContext_T)(
14852 this, m_CurrentFrameIndex.load(), info.flags, pStats);
14853
14854 (*pContext)->AddPools(poolCount: info.poolCount, pPools: info.pPools);
14855 (*pContext)->AddAllocations(
14856 allocationCount: info.allocationCount, pAllocations: info.pAllocations, pAllocationsChanged: info.pAllocationsChanged);
14857
14858 VkResult res = (*pContext)->Defragment(
14859 maxCpuBytesToMove: info.maxCpuBytesToMove, maxCpuAllocationsToMove: info.maxCpuAllocationsToMove,
14860 maxGpuBytesToMove: info.maxGpuBytesToMove, maxGpuAllocationsToMove: info.maxGpuAllocationsToMove,
14861 commandBuffer: info.commandBuffer, pStats);
14862
14863 if(res != VK_NOT_READY)
14864 {
14865 vma_delete(hAllocator: this, ptr: *pContext);
14866 *pContext = VMA_NULL;
14867 }
14868
14869 return res;
14870}
14871
14872VkResult VmaAllocator_T::DefragmentationEnd(
14873 VmaDefragmentationContext context)
14874{
14875 vma_delete(hAllocator: this, ptr: context);
14876 return VK_SUCCESS;
14877}
14878
14879void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14880{
14881 if(hAllocation->CanBecomeLost())
14882 {
14883 /*
14884 Warning: This is a carefully designed algorithm.
14885 Do not modify unless you really know what you're doing :)
14886 */
14887 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14888 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14889 for(;;)
14890 {
14891 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14892 {
14893 pAllocationInfo->memoryType = UINT32_MAX;
14894 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14895 pAllocationInfo->offset = 0;
14896 pAllocationInfo->size = hAllocation->GetSize();
14897 pAllocationInfo->pMappedData = VMA_NULL;
14898 pAllocationInfo->pUserData = hAllocation->GetUserData();
14899 return;
14900 }
14901 else if(localLastUseFrameIndex == localCurrFrameIndex)
14902 {
14903 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14904 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14905 pAllocationInfo->offset = hAllocation->GetOffset();
14906 pAllocationInfo->size = hAllocation->GetSize();
14907 pAllocationInfo->pMappedData = VMA_NULL;
14908 pAllocationInfo->pUserData = hAllocation->GetUserData();
14909 return;
14910 }
14911 else // Last use time earlier than current time.
14912 {
14913 if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex))
14914 {
14915 localLastUseFrameIndex = localCurrFrameIndex;
14916 }
14917 }
14918 }
14919 }
14920 else
14921 {
14922#if VMA_STATS_STRING_ENABLED
14923 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14924 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14925 for(;;)
14926 {
14927 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14928 if(localLastUseFrameIndex == localCurrFrameIndex)
14929 {
14930 break;
14931 }
14932 else // Last use time earlier than current time.
14933 {
14934 if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex))
14935 {
14936 localLastUseFrameIndex = localCurrFrameIndex;
14937 }
14938 }
14939 }
14940#endif
14941
14942 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14943 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14944 pAllocationInfo->offset = hAllocation->GetOffset();
14945 pAllocationInfo->size = hAllocation->GetSize();
14946 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14947 pAllocationInfo->pUserData = hAllocation->GetUserData();
14948 }
14949}
14950
14951bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14952{
14953 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14954 if(hAllocation->CanBecomeLost())
14955 {
14956 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14957 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14958 for(;;)
14959 {
14960 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14961 {
14962 return false;
14963 }
14964 else if(localLastUseFrameIndex == localCurrFrameIndex)
14965 {
14966 return true;
14967 }
14968 else // Last use time earlier than current time.
14969 {
14970 if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex))
14971 {
14972 localLastUseFrameIndex = localCurrFrameIndex;
14973 }
14974 }
14975 }
14976 }
14977 else
14978 {
14979#if VMA_STATS_STRING_ENABLED
14980 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14981 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14982 for(;;)
14983 {
14984 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14985 if(localLastUseFrameIndex == localCurrFrameIndex)
14986 {
14987 break;
14988 }
14989 else // Last use time earlier than current time.
14990 {
14991 if(hAllocation->CompareExchangeLastUseFrameIndex(expected&: localLastUseFrameIndex, desired: localCurrFrameIndex))
14992 {
14993 localLastUseFrameIndex = localCurrFrameIndex;
14994 }
14995 }
14996 }
14997#endif
14998
14999 return true;
15000 }
15001}
15002
15003VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15004{
15005 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15006
15007 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15008
15009 if(newCreateInfo.maxBlockCount == 0)
15010 {
15011 newCreateInfo.maxBlockCount = SIZE_MAX;
15012 }
15013 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15014 {
15015 return VK_ERROR_INITIALIZATION_FAILED;
15016 }
15017
15018 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex: newCreateInfo.memoryTypeIndex);
15019
15020 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15021
15022 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15023 if(res != VK_SUCCESS)
15024 {
15025 vma_delete(hAllocator: this, ptr: *pPool);
15026 *pPool = VMA_NULL;
15027 return res;
15028 }
15029
15030 // Add to m_Pools.
15031 {
15032 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15033 (*pPool)->SetId(m_NextPoolId++);
15034 VmaVectorInsertSorted<VmaPointerLess>(vector&: m_Pools, value: *pPool);
15035 }
15036
15037 return VK_SUCCESS;
15038}
15039
15040void VmaAllocator_T::DestroyPool(VmaPool pool)
15041{
15042 // Remove from m_Pools.
15043 {
15044 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15045 bool success = VmaVectorRemoveSorted<VmaPointerLess>(vector&: m_Pools, value: pool);
15046 (void) success;
15047 VMA_ASSERT(success && "Pool not found in Allocator.");
15048 }
15049
15050 vma_delete(hAllocator: this, ptr: pool);
15051}
15052
15053void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15054{
15055 pool->m_BlockVector.GetPoolStats(pStats: pPoolStats);
15056}
15057
15058void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15059{
15060 m_CurrentFrameIndex.store(i: frameIndex);
15061}
15062
15063void VmaAllocator_T::MakePoolAllocationsLost(
15064 VmaPool hPool,
15065 size_t* pLostAllocationCount)
15066{
15067 hPool->m_BlockVector.MakePoolAllocationsLost(
15068 currentFrameIndex: m_CurrentFrameIndex.load(),
15069 pLostAllocationCount);
15070}
15071
15072VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15073{
15074 return hPool->m_BlockVector.CheckCorruption();
15075}
15076
15077VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15078{
15079 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15080
15081 // Process default pools.
15082 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15083 {
15084 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15085 {
15086 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15087 VMA_ASSERT(pBlockVector);
15088 VkResult localRes = pBlockVector->CheckCorruption();
15089 switch(localRes)
15090 {
15091 case VK_ERROR_FEATURE_NOT_PRESENT:
15092 break;
15093 case VK_SUCCESS:
15094 finalRes = VK_SUCCESS;
15095 break;
15096 default:
15097 return localRes;
15098 }
15099 }
15100 }
15101
15102 // Process custom pools.
15103 {
15104 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15105 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15106 {
15107 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15108 {
15109 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15110 switch(localRes)
15111 {
15112 case VK_ERROR_FEATURE_NOT_PRESENT:
15113 break;
15114 case VK_SUCCESS:
15115 finalRes = VK_SUCCESS;
15116 break;
15117 default:
15118 return localRes;
15119 }
15120 }
15121 }
15122 }
15123
15124 return finalRes;
15125}
15126
15127void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15128{
15129 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15130 (*pAllocation)->InitLost();
15131}
15132
15133VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15134{
15135 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex: pAllocateInfo->memoryTypeIndex);
15136
15137 VkResult res;
15138 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15139 {
15140 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15141 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15142 {
15143 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15144 if(res == VK_SUCCESS)
15145 {
15146 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15147 }
15148 }
15149 else
15150 {
15151 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15152 }
15153 }
15154 else
15155 {
15156 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15157 }
15158
15159 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15160 {
15161 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15162 }
15163
15164 return res;
15165}
15166
15167void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15168{
15169 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15170 {
15171 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15172 }
15173
15174 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15175
15176 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex: memoryType);
15177 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15178 {
15179 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15180 m_HeapSizeLimit[heapIndex] += size;
15181 }
15182}
15183
15184VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15185{
15186 if(hAllocation->CanBecomeLost())
15187 {
15188 return VK_ERROR_MEMORY_MAP_FAILED;
15189 }
15190
15191 switch(hAllocation->GetType())
15192 {
15193 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15194 {
15195 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15196 char *pBytes = VMA_NULL;
15197 VkResult res = pBlock->Map(hAllocator: this, count: 1, ppData: (void**)&pBytes);
15198 if(res == VK_SUCCESS)
15199 {
15200 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15201 hAllocation->BlockAllocMap();
15202 }
15203 return res;
15204 }
15205 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15206 return hAllocation->DedicatedAllocMap(hAllocator: this, ppData);
15207 default:
15208 VMA_ASSERT(0);
15209 return VK_ERROR_MEMORY_MAP_FAILED;
15210 }
15211}
15212
15213void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15214{
15215 switch(hAllocation->GetType())
15216 {
15217 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15218 {
15219 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15220 hAllocation->BlockAllocUnmap();
15221 pBlock->Unmap(hAllocator: this, count: 1);
15222 }
15223 break;
15224 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15225 hAllocation->DedicatedAllocUnmap(hAllocator: this);
15226 break;
15227 default:
15228 VMA_ASSERT(0);
15229 }
15230}
15231
15232VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15233{
15234 VkResult res = VK_SUCCESS;
15235 switch(hAllocation->GetType())
15236 {
15237 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15238 res = GetVulkanFunctions().vkBindBufferMemory(
15239 m_hDevice,
15240 hBuffer,
15241 hAllocation->GetMemory(),
15242 0); //memoryOffset
15243 break;
15244 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15245 {
15246 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15247 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15248 res = pBlock->BindBufferMemory(hAllocator: this, hAllocation, hBuffer);
15249 break;
15250 }
15251 default:
15252 VMA_ASSERT(0);
15253 }
15254 return res;
15255}
15256
15257VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15258{
15259 VkResult res = VK_SUCCESS;
15260 switch(hAllocation->GetType())
15261 {
15262 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15263 res = GetVulkanFunctions().vkBindImageMemory(
15264 m_hDevice,
15265 hImage,
15266 hAllocation->GetMemory(),
15267 0); //memoryOffset
15268 break;
15269 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15270 {
15271 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15272 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15273 res = pBlock->BindImageMemory(hAllocator: this, hAllocation, hImage);
15274 break;
15275 }
15276 default:
15277 VMA_ASSERT(0);
15278 }
15279 return res;
15280}
15281
15282void VmaAllocator_T::FlushOrInvalidateAllocation(
15283 VmaAllocation hAllocation,
15284 VkDeviceSize offset, VkDeviceSize size,
15285 VMA_CACHE_OPERATION op)
15286{
15287 const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15288 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15289 {
15290 const VkDeviceSize allocationSize = hAllocation->GetSize();
15291 VMA_ASSERT(offset <= allocationSize);
15292
15293 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15294
15295 VkMappedMemoryRange memRange = {};
15296 memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
15297 memRange.memory = hAllocation->GetMemory();
15298
15299 switch(hAllocation->GetType())
15300 {
15301 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15302 memRange.offset = VmaAlignDown(val: offset, align: nonCoherentAtomSize);
15303 if(size == VK_WHOLE_SIZE)
15304 {
15305 memRange.size = allocationSize - memRange.offset;
15306 }
15307 else
15308 {
15309 VMA_ASSERT(offset + size <= allocationSize);
15310 memRange.size = VMA_MIN(
15311 VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15312 allocationSize - memRange.offset);
15313 }
15314 break;
15315
15316 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15317 {
15318 // 1. Still within this allocation.
15319 memRange.offset = VmaAlignDown(val: offset, align: nonCoherentAtomSize);
15320 if(size == VK_WHOLE_SIZE)
15321 {
15322 size = allocationSize - offset;
15323 }
15324 else
15325 {
15326 VMA_ASSERT(offset + size <= allocationSize);
15327 }
15328 memRange.size = VmaAlignUp(val: size + (offset - memRange.offset), align: nonCoherentAtomSize);
15329
15330 // 2. Adjust to whole block.
15331 const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15332 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15333 const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15334 memRange.offset += allocationOffset;
15335 memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15336
15337 break;
15338 }
15339
15340 default:
15341 VMA_ASSERT(0);
15342 }
15343
15344 switch(op)
15345 {
15346 case VMA_CACHE_FLUSH:
15347 (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15348 break;
15349 case VMA_CACHE_INVALIDATE:
15350 (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15351 break;
15352 default:
15353 VMA_ASSERT(0);
15354 }
15355 }
15356 // else: Just ignore this call.
15357}
15358
15359void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15360{
15361 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15362
15363 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15364 {
15365 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15366 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15367 VMA_ASSERT(pDedicatedAllocations);
15368 bool success = VmaVectorRemoveSorted<VmaPointerLess>(vector&: *pDedicatedAllocations, value: allocation);
15369 (void) success;
15370 VMA_ASSERT(success);
15371 }
15372
15373 VkDeviceMemory hMemory = allocation->GetMemory();
15374
15375 /*
15376 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15377 before vkFreeMemory.
15378
15379 if(allocation->GetMappedData() != VMA_NULL)
15380 {
15381 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15382 }
15383 */
15384
15385 FreeVulkanMemory(memoryType: memTypeIndex, size: allocation->GetSize(), hMemory);
15386
15387 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15388}
15389
15390void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15391{
15392 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15393 !hAllocation->CanBecomeLost() &&
15394 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15395 {
15396 void* pData = VMA_NULL;
15397 VkResult res = Map(hAllocation, ppData: &pData);
15398 if(res == VK_SUCCESS)
15399 {
15400 memset(s: pData, c: (int)pattern, n: (size_t)hAllocation->GetSize());
15401 FlushOrInvalidateAllocation(hAllocation, offset: 0, VK_WHOLE_SIZE, op: VMA_CACHE_FLUSH);
15402 Unmap(hAllocation);
15403 }
15404 else
15405 {
15406 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15407 }
15408 }
15409}
15410
15411#if VMA_STATS_STRING_ENABLED
15412
15413void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15414{
15415 bool dedicatedAllocationsStarted = false;
15416 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15417 {
15418 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15419 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15420 VMA_ASSERT(pDedicatedAllocVector);
15421 if(pDedicatedAllocVector->empty() == false)
15422 {
15423 if(dedicatedAllocationsStarted == false)
15424 {
15425 dedicatedAllocationsStarted = true;
15426 json.WriteString(pStr: "DedicatedAllocations");
15427 json.BeginObject();
15428 }
15429
15430 json.BeginString(pStr: "Type ");
15431 json.ContinueString(n: memTypeIndex);
15432 json.EndString();
15433
15434 json.BeginArray();
15435
15436 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15437 {
15438 json.BeginObject(singleLine: true);
15439 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15440 hAlloc->PrintParameters(json);
15441 json.EndObject();
15442 }
15443
15444 json.EndArray();
15445 }
15446 }
15447 if(dedicatedAllocationsStarted)
15448 {
15449 json.EndObject();
15450 }
15451
15452 {
15453 bool allocationsStarted = false;
15454 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15455 {
15456 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15457 {
15458 if(allocationsStarted == false)
15459 {
15460 allocationsStarted = true;
15461 json.WriteString(pStr: "DefaultPools");
15462 json.BeginObject();
15463 }
15464
15465 json.BeginString(pStr: "Type ");
15466 json.ContinueString(n: memTypeIndex);
15467 json.EndString();
15468
15469 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15470 }
15471 }
15472 if(allocationsStarted)
15473 {
15474 json.EndObject();
15475 }
15476 }
15477
15478 // Custom pools
15479 {
15480 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15481 const size_t poolCount = m_Pools.size();
15482 if(poolCount > 0)
15483 {
15484 json.WriteString(pStr: "Pools");
15485 json.BeginObject();
15486 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15487 {
15488 json.BeginString();
15489 json.ContinueString(n: m_Pools[poolIndex]->GetId());
15490 json.EndString();
15491
15492 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15493 }
15494 json.EndObject();
15495 }
15496 }
15497}
15498
15499#endif // #if VMA_STATS_STRING_ENABLED
15500
15501////////////////////////////////////////////////////////////////////////////////
15502// Public interface
15503
15504VkResult vmaCreateAllocator(
15505 const VmaAllocatorCreateInfo* pCreateInfo,
15506 VmaAllocator* pAllocator)
15507{
15508 VMA_ASSERT(pCreateInfo && pAllocator);
15509 VMA_DEBUG_LOG("vmaCreateAllocator");
15510 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15511 return (*pAllocator)->Init(pCreateInfo);
15512}
15513
15514void vmaDestroyAllocator(
15515 VmaAllocator allocator)
15516{
15517 if(allocator != VK_NULL_HANDLE)
15518 {
15519 VMA_DEBUG_LOG("vmaDestroyAllocator");
15520 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15521 vma_delete(pAllocationCallbacks: &allocationCallbacks, ptr: allocator);
15522 }
15523}
15524
15525void vmaGetPhysicalDeviceProperties(
15526 VmaAllocator allocator,
15527 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15528{
15529 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15530 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15531}
15532
15533void vmaGetMemoryProperties(
15534 VmaAllocator allocator,
15535 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15536{
15537 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15538 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15539}
15540
15541void vmaGetMemoryTypeProperties(
15542 VmaAllocator allocator,
15543 uint32_t memoryTypeIndex,
15544 VkMemoryPropertyFlags* pFlags)
15545{
15546 VMA_ASSERT(allocator && pFlags);
15547 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15548 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15549}
15550
15551void vmaSetCurrentFrameIndex(
15552 VmaAllocator allocator,
15553 uint32_t frameIndex)
15554{
15555 VMA_ASSERT(allocator);
15556 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15557
15558 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15559
15560 allocator->SetCurrentFrameIndex(frameIndex);
15561}
15562
15563void vmaCalculateStats(
15564 VmaAllocator allocator,
15565 VmaStats* pStats)
15566{
15567 VMA_ASSERT(allocator && pStats);
15568 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15569 allocator->CalculateStats(pStats);
15570}
15571
15572#if VMA_STATS_STRING_ENABLED
15573
15574void vmaBuildStatsString(
15575 VmaAllocator allocator,
15576 char** ppStatsString,
15577 VkBool32 detailedMap)
15578{
15579 VMA_ASSERT(allocator && ppStatsString);
15580 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15581
15582 VmaStringBuilder sb(allocator);
15583 {
15584 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15585 json.BeginObject();
15586
15587 VmaStats stats;
15588 allocator->CalculateStats(pStats: &stats);
15589
15590 json.WriteString(pStr: "Total");
15591 VmaPrintStatInfo(json, stat: stats.total);
15592
15593 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15594 {
15595 json.BeginString(pStr: "Heap ");
15596 json.ContinueString(n: heapIndex);
15597 json.EndString();
15598 json.BeginObject();
15599
15600 json.WriteString(pStr: "Size");
15601 json.WriteNumber(n: allocator->m_MemProps.memoryHeaps[heapIndex].size);
15602
15603 json.WriteString(pStr: "Flags");
15604 json.BeginArray(singleLine: true);
15605 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15606 {
15607 json.WriteString(pStr: "DEVICE_LOCAL");
15608 }
15609 json.EndArray();
15610
15611 if(stats.memoryHeap[heapIndex].blockCount > 0)
15612 {
15613 json.WriteString(pStr: "Stats");
15614 VmaPrintStatInfo(json, stat: stats.memoryHeap[heapIndex]);
15615 }
15616
15617 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15618 {
15619 if(allocator->MemoryTypeIndexToHeapIndex(memTypeIndex: typeIndex) == heapIndex)
15620 {
15621 json.BeginString(pStr: "Type ");
15622 json.ContinueString(n: typeIndex);
15623 json.EndString();
15624
15625 json.BeginObject();
15626
15627 json.WriteString(pStr: "Flags");
15628 json.BeginArray(singleLine: true);
15629 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15630 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15631 {
15632 json.WriteString(pStr: "DEVICE_LOCAL");
15633 }
15634 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15635 {
15636 json.WriteString(pStr: "HOST_VISIBLE");
15637 }
15638 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15639 {
15640 json.WriteString(pStr: "HOST_COHERENT");
15641 }
15642 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15643 {
15644 json.WriteString(pStr: "HOST_CACHED");
15645 }
15646 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15647 {
15648 json.WriteString(pStr: "LAZILY_ALLOCATED");
15649 }
15650 json.EndArray();
15651
15652 if(stats.memoryType[typeIndex].blockCount > 0)
15653 {
15654 json.WriteString(pStr: "Stats");
15655 VmaPrintStatInfo(json, stat: stats.memoryType[typeIndex]);
15656 }
15657
15658 json.EndObject();
15659 }
15660 }
15661
15662 json.EndObject();
15663 }
15664 if(detailedMap == VK_TRUE)
15665 {
15666 allocator->PrintDetailedMap(json);
15667 }
15668
15669 json.EndObject();
15670 }
15671
15672 const size_t len = sb.GetLength();
15673 char* const pChars = vma_new_array(allocator, char, len + 1);
15674 if(len > 0)
15675 {
15676 memcpy(dest: pChars, src: sb.GetData(), n: len);
15677 }
15678 pChars[len] = '\0';
15679 *ppStatsString = pChars;
15680}
15681
15682void vmaFreeStatsString(
15683 VmaAllocator allocator,
15684 char* pStatsString)
15685{
15686 if(pStatsString != VMA_NULL)
15687 {
15688 VMA_ASSERT(allocator);
15689 size_t len = strlen(s: pStatsString);
15690 vma_delete_array(hAllocator: allocator, ptr: pStatsString, count: len + 1);
15691 }
15692}
15693
15694#endif // #if VMA_STATS_STRING_ENABLED
15695
15696/*
15697This function is not protected by any mutex because it just reads immutable data.
15698*/
15699VkResult vmaFindMemoryTypeIndex(
15700 VmaAllocator allocator,
15701 uint32_t memoryTypeBits,
15702 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15703 uint32_t* pMemoryTypeIndex)
15704{
15705 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15706 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15707 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15708
15709 if(pAllocationCreateInfo->memoryTypeBits != 0)
15710 {
15711 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15712 }
15713
15714 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15715 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15716
15717 const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15718 if(mapped)
15719 {
15720 preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15721 }
15722
15723 // Convert usage to requiredFlags and preferredFlags.
15724 switch(pAllocationCreateInfo->usage)
15725 {
15726 case VMA_MEMORY_USAGE_UNKNOWN:
15727 break;
15728 case VMA_MEMORY_USAGE_GPU_ONLY:
15729 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15730 {
15731 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15732 }
15733 break;
15734 case VMA_MEMORY_USAGE_CPU_ONLY:
15735 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15736 break;
15737 case VMA_MEMORY_USAGE_CPU_TO_GPU:
15738 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15739 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15740 {
15741 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15742 }
15743 break;
15744 case VMA_MEMORY_USAGE_GPU_TO_CPU:
15745 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15746 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15747 break;
15748 default:
15749 break;
15750 }
15751
15752 *pMemoryTypeIndex = UINT32_MAX;
15753 uint32_t minCost = UINT32_MAX;
15754 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15755 memTypeIndex < allocator->GetMemoryTypeCount();
15756 ++memTypeIndex, memTypeBit <<= 1)
15757 {
15758 // This memory type is acceptable according to memoryTypeBits bitmask.
15759 if((memTypeBit & memoryTypeBits) != 0)
15760 {
15761 const VkMemoryPropertyFlags currFlags =
15762 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15763 // This memory type contains requiredFlags.
15764 if((requiredFlags & ~currFlags) == 0)
15765 {
15766 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15767 uint32_t currCost = VmaCountBitsSet(v: preferredFlags & ~currFlags);
15768 // Remember memory type with lowest cost.
15769 if(currCost < minCost)
15770 {
15771 *pMemoryTypeIndex = memTypeIndex;
15772 if(currCost == 0)
15773 {
15774 return VK_SUCCESS;
15775 }
15776 minCost = currCost;
15777 }
15778 }
15779 }
15780 }
15781 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15782}
15783
15784VkResult vmaFindMemoryTypeIndexForBufferInfo(
15785 VmaAllocator allocator,
15786 const VkBufferCreateInfo* pBufferCreateInfo,
15787 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15788 uint32_t* pMemoryTypeIndex)
15789{
15790 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15791 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15792 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15793 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15794
15795 const VkDevice hDev = allocator->m_hDevice;
15796 VkBuffer hBuffer = VK_NULL_HANDLE;
15797 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15798 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15799 if(res == VK_SUCCESS)
15800 {
15801 VkMemoryRequirements memReq = {};
15802 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15803 hDev, hBuffer, &memReq);
15804
15805 res = vmaFindMemoryTypeIndex(
15806 allocator,
15807 memoryTypeBits: memReq.memoryTypeBits,
15808 pAllocationCreateInfo,
15809 pMemoryTypeIndex);
15810
15811 allocator->GetVulkanFunctions().vkDestroyBuffer(
15812 hDev, hBuffer, allocator->GetAllocationCallbacks());
15813 }
15814 return res;
15815}
15816
15817VkResult vmaFindMemoryTypeIndexForImageInfo(
15818 VmaAllocator allocator,
15819 const VkImageCreateInfo* pImageCreateInfo,
15820 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15821 uint32_t* pMemoryTypeIndex)
15822{
15823 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15824 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15825 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15826 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15827
15828 const VkDevice hDev = allocator->m_hDevice;
15829 VkImage hImage = VK_NULL_HANDLE;
15830 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15831 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15832 if(res == VK_SUCCESS)
15833 {
15834 VkMemoryRequirements memReq = {};
15835 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15836 hDev, hImage, &memReq);
15837
15838 res = vmaFindMemoryTypeIndex(
15839 allocator,
15840 memoryTypeBits: memReq.memoryTypeBits,
15841 pAllocationCreateInfo,
15842 pMemoryTypeIndex);
15843
15844 allocator->GetVulkanFunctions().vkDestroyImage(
15845 hDev, hImage, allocator->GetAllocationCallbacks());
15846 }
15847 return res;
15848}
15849
15850VkResult vmaCreatePool(
15851 VmaAllocator allocator,
15852 const VmaPoolCreateInfo* pCreateInfo,
15853 VmaPool* pPool)
15854{
15855 VMA_ASSERT(allocator && pCreateInfo && pPool);
15856
15857 VMA_DEBUG_LOG("vmaCreatePool");
15858
15859 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15860
15861 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15862
15863#if VMA_RECORDING_ENABLED
15864 if(allocator->GetRecorder() != VMA_NULL)
15865 {
15866 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15867 }
15868#endif
15869
15870 return res;
15871}
15872
15873void vmaDestroyPool(
15874 VmaAllocator allocator,
15875 VmaPool pool)
15876{
15877 VMA_ASSERT(allocator);
15878
15879 if(pool == VK_NULL_HANDLE)
15880 {
15881 return;
15882 }
15883
15884 VMA_DEBUG_LOG("vmaDestroyPool");
15885
15886 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15887
15888#if VMA_RECORDING_ENABLED
15889 if(allocator->GetRecorder() != VMA_NULL)
15890 {
15891 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15892 }
15893#endif
15894
15895 allocator->DestroyPool(pool);
15896}
15897
15898void vmaGetPoolStats(
15899 VmaAllocator allocator,
15900 VmaPool pool,
15901 VmaPoolStats* pPoolStats)
15902{
15903 VMA_ASSERT(allocator && pool && pPoolStats);
15904
15905 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15906
15907 allocator->GetPoolStats(pool, pPoolStats);
15908}
15909
15910void vmaMakePoolAllocationsLost(
15911 VmaAllocator allocator,
15912 VmaPool pool,
15913 size_t* pLostAllocationCount)
15914{
15915 VMA_ASSERT(allocator && pool);
15916
15917 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15918
15919#if VMA_RECORDING_ENABLED
15920 if(allocator->GetRecorder() != VMA_NULL)
15921 {
15922 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15923 }
15924#endif
15925
15926 allocator->MakePoolAllocationsLost(hPool: pool, pLostAllocationCount);
15927}
15928
15929VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15930{
15931 VMA_ASSERT(allocator && pool);
15932
15933 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15934
15935 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15936
15937 return allocator->CheckPoolCorruption(hPool: pool);
15938}
15939
15940VkResult vmaAllocateMemory(
15941 VmaAllocator allocator,
15942 const VkMemoryRequirements* pVkMemoryRequirements,
15943 const VmaAllocationCreateInfo* pCreateInfo,
15944 VmaAllocation* pAllocation,
15945 VmaAllocationInfo* pAllocationInfo)
15946{
15947 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15948
15949 VMA_DEBUG_LOG("vmaAllocateMemory");
15950
15951 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15952
15953 VkResult result = allocator->AllocateMemory(
15954 vkMemReq: *pVkMemoryRequirements,
15955 requiresDedicatedAllocation: false, // requiresDedicatedAllocation
15956 prefersDedicatedAllocation: false, // prefersDedicatedAllocation
15957 VK_NULL_HANDLE, // dedicatedBuffer
15958 VK_NULL_HANDLE, // dedicatedImage
15959 createInfo: *pCreateInfo,
15960 suballocType: VMA_SUBALLOCATION_TYPE_UNKNOWN,
15961 allocationCount: 1, // allocationCount
15962 pAllocations: pAllocation);
15963
15964#if VMA_RECORDING_ENABLED
15965 if(allocator->GetRecorder() != VMA_NULL)
15966 {
15967 allocator->GetRecorder()->RecordAllocateMemory(
15968 allocator->GetCurrentFrameIndex(),
15969 *pVkMemoryRequirements,
15970 *pCreateInfo,
15971 *pAllocation);
15972 }
15973#endif
15974
15975 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15976 {
15977 allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo);
15978 }
15979
15980 return result;
15981}
15982
15983VkResult vmaAllocateMemoryPages(
15984 VmaAllocator allocator,
15985 const VkMemoryRequirements* pVkMemoryRequirements,
15986 const VmaAllocationCreateInfo* pCreateInfo,
15987 size_t allocationCount,
15988 VmaAllocation* pAllocations,
15989 VmaAllocationInfo* pAllocationInfo)
15990{
15991 if(allocationCount == 0)
15992 {
15993 return VK_SUCCESS;
15994 }
15995
15996 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15997
15998 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15999
16000 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16001
16002 VkResult result = allocator->AllocateMemory(
16003 vkMemReq: *pVkMemoryRequirements,
16004 requiresDedicatedAllocation: false, // requiresDedicatedAllocation
16005 prefersDedicatedAllocation: false, // prefersDedicatedAllocation
16006 VK_NULL_HANDLE, // dedicatedBuffer
16007 VK_NULL_HANDLE, // dedicatedImage
16008 createInfo: *pCreateInfo,
16009 suballocType: VMA_SUBALLOCATION_TYPE_UNKNOWN,
16010 allocationCount,
16011 pAllocations);
16012
16013#if VMA_RECORDING_ENABLED
16014 if(allocator->GetRecorder() != VMA_NULL)
16015 {
16016 allocator->GetRecorder()->RecordAllocateMemoryPages(
16017 allocator->GetCurrentFrameIndex(),
16018 *pVkMemoryRequirements,
16019 *pCreateInfo,
16020 (uint64_t)allocationCount,
16021 pAllocations);
16022 }
16023#endif
16024
16025 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16026 {
16027 for(size_t i = 0; i < allocationCount; ++i)
16028 {
16029 allocator->GetAllocationInfo(hAllocation: pAllocations[i], pAllocationInfo: pAllocationInfo + i);
16030 }
16031 }
16032
16033 return result;
16034}
16035
16036VkResult vmaAllocateMemoryForBuffer(
16037 VmaAllocator allocator,
16038 VkBuffer buffer,
16039 const VmaAllocationCreateInfo* pCreateInfo,
16040 VmaAllocation* pAllocation,
16041 VmaAllocationInfo* pAllocationInfo)
16042{
16043 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16044
16045 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16046
16047 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16048
16049 VkMemoryRequirements vkMemReq = {};
16050 bool requiresDedicatedAllocation = false;
16051 bool prefersDedicatedAllocation = false;
16052 allocator->GetBufferMemoryRequirements(hBuffer: buffer, memReq&: vkMemReq,
16053 requiresDedicatedAllocation,
16054 prefersDedicatedAllocation);
16055
16056 VkResult result = allocator->AllocateMemory(
16057 vkMemReq,
16058 requiresDedicatedAllocation,
16059 prefersDedicatedAllocation,
16060 dedicatedBuffer: buffer, // dedicatedBuffer
16061 VK_NULL_HANDLE, // dedicatedImage
16062 createInfo: *pCreateInfo,
16063 suballocType: VMA_SUBALLOCATION_TYPE_BUFFER,
16064 allocationCount: 1, // allocationCount
16065 pAllocations: pAllocation);
16066
16067#if VMA_RECORDING_ENABLED
16068 if(allocator->GetRecorder() != VMA_NULL)
16069 {
16070 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16071 allocator->GetCurrentFrameIndex(),
16072 vkMemReq,
16073 requiresDedicatedAllocation,
16074 prefersDedicatedAllocation,
16075 *pCreateInfo,
16076 *pAllocation);
16077 }
16078#endif
16079
16080 if(pAllocationInfo && result == VK_SUCCESS)
16081 {
16082 allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo);
16083 }
16084
16085 return result;
16086}
16087
16088VkResult vmaAllocateMemoryForImage(
16089 VmaAllocator allocator,
16090 VkImage image,
16091 const VmaAllocationCreateInfo* pCreateInfo,
16092 VmaAllocation* pAllocation,
16093 VmaAllocationInfo* pAllocationInfo)
16094{
16095 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16096
16097 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16098
16099 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16100
16101 VkMemoryRequirements vkMemReq = {};
16102 bool requiresDedicatedAllocation = false;
16103 bool prefersDedicatedAllocation = false;
16104 allocator->GetImageMemoryRequirements(hImage: image, memReq&: vkMemReq,
16105 requiresDedicatedAllocation, prefersDedicatedAllocation);
16106
16107 VkResult result = allocator->AllocateMemory(
16108 vkMemReq,
16109 requiresDedicatedAllocation,
16110 prefersDedicatedAllocation,
16111 VK_NULL_HANDLE, // dedicatedBuffer
16112 dedicatedImage: image, // dedicatedImage
16113 createInfo: *pCreateInfo,
16114 suballocType: VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16115 allocationCount: 1, // allocationCount
16116 pAllocations: pAllocation);
16117
16118#if VMA_RECORDING_ENABLED
16119 if(allocator->GetRecorder() != VMA_NULL)
16120 {
16121 allocator->GetRecorder()->RecordAllocateMemoryForImage(
16122 allocator->GetCurrentFrameIndex(),
16123 vkMemReq,
16124 requiresDedicatedAllocation,
16125 prefersDedicatedAllocation,
16126 *pCreateInfo,
16127 *pAllocation);
16128 }
16129#endif
16130
16131 if(pAllocationInfo && result == VK_SUCCESS)
16132 {
16133 allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo);
16134 }
16135
16136 return result;
16137}
16138
16139void vmaFreeMemory(
16140 VmaAllocator allocator,
16141 VmaAllocation allocation)
16142{
16143 VMA_ASSERT(allocator);
16144
16145 if(allocation == VK_NULL_HANDLE)
16146 {
16147 return;
16148 }
16149
16150 VMA_DEBUG_LOG("vmaFreeMemory");
16151
16152 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16153
16154#if VMA_RECORDING_ENABLED
16155 if(allocator->GetRecorder() != VMA_NULL)
16156 {
16157 allocator->GetRecorder()->RecordFreeMemory(
16158 allocator->GetCurrentFrameIndex(),
16159 allocation);
16160 }
16161#endif
16162
16163 allocator->FreeMemory(
16164 allocationCount: 1, // allocationCount
16165 pAllocations: &allocation);
16166}
16167
16168void vmaFreeMemoryPages(
16169 VmaAllocator allocator,
16170 size_t allocationCount,
16171 VmaAllocation* pAllocations)
16172{
16173 if(allocationCount == 0)
16174 {
16175 return;
16176 }
16177
16178 VMA_ASSERT(allocator);
16179
16180 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16181
16182 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16183
16184#if VMA_RECORDING_ENABLED
16185 if(allocator->GetRecorder() != VMA_NULL)
16186 {
16187 allocator->GetRecorder()->RecordFreeMemoryPages(
16188 allocator->GetCurrentFrameIndex(),
16189 (uint64_t)allocationCount,
16190 pAllocations);
16191 }
16192#endif
16193
16194 allocator->FreeMemory(allocationCount, pAllocations);
16195}
16196
16197VkResult vmaResizeAllocation(
16198 VmaAllocator allocator,
16199 VmaAllocation allocation,
16200 VkDeviceSize newSize)
16201{
16202 VMA_ASSERT(allocator && allocation);
16203
16204 VMA_DEBUG_LOG("vmaResizeAllocation");
16205
16206 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16207
16208#if VMA_RECORDING_ENABLED
16209 if(allocator->GetRecorder() != VMA_NULL)
16210 {
16211 allocator->GetRecorder()->RecordResizeAllocation(
16212 allocator->GetCurrentFrameIndex(),
16213 allocation,
16214 newSize);
16215 }
16216#endif
16217
16218 return allocator->ResizeAllocation(alloc: allocation, newSize);
16219}
16220
16221void vmaGetAllocationInfo(
16222 VmaAllocator allocator,
16223 VmaAllocation allocation,
16224 VmaAllocationInfo* pAllocationInfo)
16225{
16226 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16227
16228 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16229
16230#if VMA_RECORDING_ENABLED
16231 if(allocator->GetRecorder() != VMA_NULL)
16232 {
16233 allocator->GetRecorder()->RecordGetAllocationInfo(
16234 allocator->GetCurrentFrameIndex(),
16235 allocation);
16236 }
16237#endif
16238
16239 allocator->GetAllocationInfo(hAllocation: allocation, pAllocationInfo);
16240}
16241
16242VkBool32 vmaTouchAllocation(
16243 VmaAllocator allocator,
16244 VmaAllocation allocation)
16245{
16246 VMA_ASSERT(allocator && allocation);
16247
16248 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16249
16250#if VMA_RECORDING_ENABLED
16251 if(allocator->GetRecorder() != VMA_NULL)
16252 {
16253 allocator->GetRecorder()->RecordTouchAllocation(
16254 allocator->GetCurrentFrameIndex(),
16255 allocation);
16256 }
16257#endif
16258
16259 return allocator->TouchAllocation(hAllocation: allocation);
16260}
16261
16262void vmaSetAllocationUserData(
16263 VmaAllocator allocator,
16264 VmaAllocation allocation,
16265 void* pUserData)
16266{
16267 VMA_ASSERT(allocator && allocation);
16268
16269 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16270
16271 allocation->SetUserData(hAllocator: allocator, pUserData);
16272
16273#if VMA_RECORDING_ENABLED
16274 if(allocator->GetRecorder() != VMA_NULL)
16275 {
16276 allocator->GetRecorder()->RecordSetAllocationUserData(
16277 allocator->GetCurrentFrameIndex(),
16278 allocation,
16279 pUserData);
16280 }
16281#endif
16282}
16283
16284void vmaCreateLostAllocation(
16285 VmaAllocator allocator,
16286 VmaAllocation* pAllocation)
16287{
16288 VMA_ASSERT(allocator && pAllocation);
16289
16290 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16291
16292 allocator->CreateLostAllocation(pAllocation);
16293
16294#if VMA_RECORDING_ENABLED
16295 if(allocator->GetRecorder() != VMA_NULL)
16296 {
16297 allocator->GetRecorder()->RecordCreateLostAllocation(
16298 allocator->GetCurrentFrameIndex(),
16299 *pAllocation);
16300 }
16301#endif
16302}
16303
16304VkResult vmaMapMemory(
16305 VmaAllocator allocator,
16306 VmaAllocation allocation,
16307 void** ppData)
16308{
16309 VMA_ASSERT(allocator && allocation && ppData);
16310
16311 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16312
16313 VkResult res = allocator->Map(hAllocation: allocation, ppData);
16314
16315#if VMA_RECORDING_ENABLED
16316 if(allocator->GetRecorder() != VMA_NULL)
16317 {
16318 allocator->GetRecorder()->RecordMapMemory(
16319 allocator->GetCurrentFrameIndex(),
16320 allocation);
16321 }
16322#endif
16323
16324 return res;
16325}
16326
16327void vmaUnmapMemory(
16328 VmaAllocator allocator,
16329 VmaAllocation allocation)
16330{
16331 VMA_ASSERT(allocator && allocation);
16332
16333 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16334
16335#if VMA_RECORDING_ENABLED
16336 if(allocator->GetRecorder() != VMA_NULL)
16337 {
16338 allocator->GetRecorder()->RecordUnmapMemory(
16339 allocator->GetCurrentFrameIndex(),
16340 allocation);
16341 }
16342#endif
16343
16344 allocator->Unmap(hAllocation: allocation);
16345}
16346
16347void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16348{
16349 VMA_ASSERT(allocator && allocation);
16350
16351 VMA_DEBUG_LOG("vmaFlushAllocation");
16352
16353 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16354
16355 allocator->FlushOrInvalidateAllocation(hAllocation: allocation, offset, size, op: VMA_CACHE_FLUSH);
16356
16357#if VMA_RECORDING_ENABLED
16358 if(allocator->GetRecorder() != VMA_NULL)
16359 {
16360 allocator->GetRecorder()->RecordFlushAllocation(
16361 allocator->GetCurrentFrameIndex(),
16362 allocation, offset, size);
16363 }
16364#endif
16365}
16366
16367void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16368{
16369 VMA_ASSERT(allocator && allocation);
16370
16371 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16372
16373 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16374
16375 allocator->FlushOrInvalidateAllocation(hAllocation: allocation, offset, size, op: VMA_CACHE_INVALIDATE);
16376
16377#if VMA_RECORDING_ENABLED
16378 if(allocator->GetRecorder() != VMA_NULL)
16379 {
16380 allocator->GetRecorder()->RecordInvalidateAllocation(
16381 allocator->GetCurrentFrameIndex(),
16382 allocation, offset, size);
16383 }
16384#endif
16385}
16386
16387VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16388{
16389 VMA_ASSERT(allocator);
16390
16391 VMA_DEBUG_LOG("vmaCheckCorruption");
16392
16393 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16394
16395 return allocator->CheckCorruption(memoryTypeBits);
16396}
16397
16398VkResult vmaDefragment(
16399 VmaAllocator allocator,
16400 VmaAllocation* pAllocations,
16401 size_t allocationCount,
16402 VkBool32* pAllocationsChanged,
16403 const VmaDefragmentationInfo *pDefragmentationInfo,
16404 VmaDefragmentationStats* pDefragmentationStats)
16405{
16406 // Deprecated interface, reimplemented using new one.
16407
16408 VmaDefragmentationInfo2 info2 = {};
16409 info2.allocationCount = (uint32_t)allocationCount;
16410 info2.pAllocations = pAllocations;
16411 info2.pAllocationsChanged = pAllocationsChanged;
16412 if(pDefragmentationInfo != VMA_NULL)
16413 {
16414 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16415 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16416 }
16417 else
16418 {
16419 info2.maxCpuAllocationsToMove = UINT32_MAX;
16420 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16421 }
16422 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16423
16424 VmaDefragmentationContext ctx;
16425 VkResult res = vmaDefragmentationBegin(allocator, pInfo: &info2, pStats: pDefragmentationStats, pContext: &ctx);
16426 if(res == VK_NOT_READY)
16427 {
16428 res = vmaDefragmentationEnd( allocator, context: ctx);
16429 }
16430 return res;
16431}
16432
16433VkResult vmaDefragmentationBegin(
16434 VmaAllocator allocator,
16435 const VmaDefragmentationInfo2* pInfo,
16436 VmaDefragmentationStats* pStats,
16437 VmaDefragmentationContext *pContext)
16438{
16439 VMA_ASSERT(allocator && pInfo && pContext);
16440
16441 // Degenerate case: Nothing to defragment.
16442 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16443 {
16444 return VK_SUCCESS;
16445 }
16446
16447 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16448 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16449 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16450 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16451
16452 VMA_DEBUG_LOG("vmaDefragmentationBegin");
16453
16454 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16455
16456 VkResult res = allocator->DefragmentationBegin(info: *pInfo, pStats, pContext);
16457
16458#if VMA_RECORDING_ENABLED
16459 if(allocator->GetRecorder() != VMA_NULL)
16460 {
16461 allocator->GetRecorder()->RecordDefragmentationBegin(
16462 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16463 }
16464#endif
16465
16466 return res;
16467}
16468
16469VkResult vmaDefragmentationEnd(
16470 VmaAllocator allocator,
16471 VmaDefragmentationContext context)
16472{
16473 VMA_ASSERT(allocator);
16474
16475 VMA_DEBUG_LOG("vmaDefragmentationEnd");
16476
16477 if(context != VK_NULL_HANDLE)
16478 {
16479 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16480
16481#if VMA_RECORDING_ENABLED
16482 if(allocator->GetRecorder() != VMA_NULL)
16483 {
16484 allocator->GetRecorder()->RecordDefragmentationEnd(
16485 allocator->GetCurrentFrameIndex(), context);
16486 }
16487#endif
16488
16489 return allocator->DefragmentationEnd(context);
16490 }
16491 else
16492 {
16493 return VK_SUCCESS;
16494 }
16495}
16496
16497VkResult vmaBindBufferMemory(
16498 VmaAllocator allocator,
16499 VmaAllocation allocation,
16500 VkBuffer buffer)
16501{
16502 VMA_ASSERT(allocator && allocation && buffer);
16503
16504 VMA_DEBUG_LOG("vmaBindBufferMemory");
16505
16506 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16507
16508 return allocator->BindBufferMemory(hAllocation: allocation, hBuffer: buffer);
16509}
16510
16511VkResult vmaBindImageMemory(
16512 VmaAllocator allocator,
16513 VmaAllocation allocation,
16514 VkImage image)
16515{
16516 VMA_ASSERT(allocator && allocation && image);
16517
16518 VMA_DEBUG_LOG("vmaBindImageMemory");
16519
16520 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16521
16522 return allocator->BindImageMemory(hAllocation: allocation, hImage: image);
16523}
16524
16525VkResult vmaCreateBuffer(
16526 VmaAllocator allocator,
16527 const VkBufferCreateInfo* pBufferCreateInfo,
16528 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16529 VkBuffer* pBuffer,
16530 VmaAllocation* pAllocation,
16531 VmaAllocationInfo* pAllocationInfo)
16532{
16533 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16534
16535 if(pBufferCreateInfo->size == 0)
16536 {
16537 return VK_ERROR_VALIDATION_FAILED_EXT;
16538 }
16539
16540 VMA_DEBUG_LOG("vmaCreateBuffer");
16541
16542 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16543
16544 *pBuffer = VK_NULL_HANDLE;
16545 *pAllocation = VK_NULL_HANDLE;
16546
16547 // 1. Create VkBuffer.
16548 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16549 allocator->m_hDevice,
16550 pBufferCreateInfo,
16551 allocator->GetAllocationCallbacks(),
16552 pBuffer);
16553 if(res >= 0)
16554 {
16555 // 2. vkGetBufferMemoryRequirements.
16556 VkMemoryRequirements vkMemReq = {};
16557 bool requiresDedicatedAllocation = false;
16558 bool prefersDedicatedAllocation = false;
16559 allocator->GetBufferMemoryRequirements(hBuffer: *pBuffer, memReq&: vkMemReq,
16560 requiresDedicatedAllocation, prefersDedicatedAllocation);
16561
16562 // Make sure alignment requirements for specific buffer usages reported
16563 // in Physical Device Properties are included in alignment reported by memory requirements.
16564 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16565 {
16566 VMA_ASSERT(vkMemReq.alignment %
16567 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16568 }
16569 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16570 {
16571 VMA_ASSERT(vkMemReq.alignment %
16572 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16573 }
16574 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16575 {
16576 VMA_ASSERT(vkMemReq.alignment %
16577 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16578 }
16579
16580 // 3. Allocate memory using allocator.
16581 res = allocator->AllocateMemory(
16582 vkMemReq,
16583 requiresDedicatedAllocation,
16584 prefersDedicatedAllocation,
16585 dedicatedBuffer: *pBuffer, // dedicatedBuffer
16586 VK_NULL_HANDLE, // dedicatedImage
16587 createInfo: *pAllocationCreateInfo,
16588 suballocType: VMA_SUBALLOCATION_TYPE_BUFFER,
16589 allocationCount: 1, // allocationCount
16590 pAllocations: pAllocation);
16591
16592#if VMA_RECORDING_ENABLED
16593 if(allocator->GetRecorder() != VMA_NULL)
16594 {
16595 allocator->GetRecorder()->RecordCreateBuffer(
16596 allocator->GetCurrentFrameIndex(),
16597 *pBufferCreateInfo,
16598 *pAllocationCreateInfo,
16599 *pAllocation);
16600 }
16601#endif
16602
16603 if(res >= 0)
16604 {
16605 // 3. Bind buffer with memory.
16606 res = allocator->BindBufferMemory(hAllocation: *pAllocation, hBuffer: *pBuffer);
16607 if(res >= 0)
16608 {
16609 // All steps succeeded.
16610 #if VMA_STATS_STRING_ENABLED
16611 (*pAllocation)->InitBufferImageUsage(bufferImageUsage: pBufferCreateInfo->usage);
16612 #endif
16613 if(pAllocationInfo != VMA_NULL)
16614 {
16615 allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo);
16616 }
16617
16618 return VK_SUCCESS;
16619 }
16620 allocator->FreeMemory(
16621 allocationCount: 1, // allocationCount
16622 pAllocations: pAllocation);
16623 *pAllocation = VK_NULL_HANDLE;
16624 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16625 *pBuffer = VK_NULL_HANDLE;
16626 return res;
16627 }
16628 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16629 *pBuffer = VK_NULL_HANDLE;
16630 return res;
16631 }
16632 return res;
16633}
16634
16635void vmaDestroyBuffer(
16636 VmaAllocator allocator,
16637 VkBuffer buffer,
16638 VmaAllocation allocation)
16639{
16640 VMA_ASSERT(allocator);
16641
16642 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16643 {
16644 return;
16645 }
16646
16647 VMA_DEBUG_LOG("vmaDestroyBuffer");
16648
16649 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16650
16651#if VMA_RECORDING_ENABLED
16652 if(allocator->GetRecorder() != VMA_NULL)
16653 {
16654 allocator->GetRecorder()->RecordDestroyBuffer(
16655 allocator->GetCurrentFrameIndex(),
16656 allocation);
16657 }
16658#endif
16659
16660 if(buffer != VK_NULL_HANDLE)
16661 {
16662 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16663 }
16664
16665 if(allocation != VK_NULL_HANDLE)
16666 {
16667 allocator->FreeMemory(
16668 allocationCount: 1, // allocationCount
16669 pAllocations: &allocation);
16670 }
16671}
16672
16673VkResult vmaCreateImage(
16674 VmaAllocator allocator,
16675 const VkImageCreateInfo* pImageCreateInfo,
16676 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16677 VkImage* pImage,
16678 VmaAllocation* pAllocation,
16679 VmaAllocationInfo* pAllocationInfo)
16680{
16681 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16682
16683 if(pImageCreateInfo->extent.width == 0 ||
16684 pImageCreateInfo->extent.height == 0 ||
16685 pImageCreateInfo->extent.depth == 0 ||
16686 pImageCreateInfo->mipLevels == 0 ||
16687 pImageCreateInfo->arrayLayers == 0)
16688 {
16689 return VK_ERROR_VALIDATION_FAILED_EXT;
16690 }
16691
16692 VMA_DEBUG_LOG("vmaCreateImage");
16693
16694 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16695
16696 *pImage = VK_NULL_HANDLE;
16697 *pAllocation = VK_NULL_HANDLE;
16698
16699 // 1. Create VkImage.
16700 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16701 allocator->m_hDevice,
16702 pImageCreateInfo,
16703 allocator->GetAllocationCallbacks(),
16704 pImage);
16705 if(res >= 0)
16706 {
16707 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16708 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16709 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16710
16711 // 2. Allocate memory using allocator.
16712 VkMemoryRequirements vkMemReq = {};
16713 bool requiresDedicatedAllocation = false;
16714 bool prefersDedicatedAllocation = false;
16715 allocator->GetImageMemoryRequirements(hImage: *pImage, memReq&: vkMemReq,
16716 requiresDedicatedAllocation, prefersDedicatedAllocation);
16717
16718 res = allocator->AllocateMemory(
16719 vkMemReq,
16720 requiresDedicatedAllocation,
16721 prefersDedicatedAllocation,
16722 VK_NULL_HANDLE, // dedicatedBuffer
16723 dedicatedImage: *pImage, // dedicatedImage
16724 createInfo: *pAllocationCreateInfo,
16725 suballocType,
16726 allocationCount: 1, // allocationCount
16727 pAllocations: pAllocation);
16728
16729#if VMA_RECORDING_ENABLED
16730 if(allocator->GetRecorder() != VMA_NULL)
16731 {
16732 allocator->GetRecorder()->RecordCreateImage(
16733 allocator->GetCurrentFrameIndex(),
16734 *pImageCreateInfo,
16735 *pAllocationCreateInfo,
16736 *pAllocation);
16737 }
16738#endif
16739
16740 if(res >= 0)
16741 {
16742 // 3. Bind image with memory.
16743 res = allocator->BindImageMemory(hAllocation: *pAllocation, hImage: *pImage);
16744 if(res >= 0)
16745 {
16746 // All steps succeeded.
16747 #if VMA_STATS_STRING_ENABLED
16748 (*pAllocation)->InitBufferImageUsage(bufferImageUsage: pImageCreateInfo->usage);
16749 #endif
16750 if(pAllocationInfo != VMA_NULL)
16751 {
16752 allocator->GetAllocationInfo(hAllocation: *pAllocation, pAllocationInfo);
16753 }
16754
16755 return VK_SUCCESS;
16756 }
16757 allocator->FreeMemory(
16758 allocationCount: 1, // allocationCount
16759 pAllocations: pAllocation);
16760 *pAllocation = VK_NULL_HANDLE;
16761 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16762 *pImage = VK_NULL_HANDLE;
16763 return res;
16764 }
16765 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16766 *pImage = VK_NULL_HANDLE;
16767 return res;
16768 }
16769 return res;
16770}
16771
16772void vmaDestroyImage(
16773 VmaAllocator allocator,
16774 VkImage image,
16775 VmaAllocation allocation)
16776{
16777 VMA_ASSERT(allocator);
16778
16779 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16780 {
16781 return;
16782 }
16783
16784 VMA_DEBUG_LOG("vmaDestroyImage");
16785
16786 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16787
16788#if VMA_RECORDING_ENABLED
16789 if(allocator->GetRecorder() != VMA_NULL)
16790 {
16791 allocator->GetRecorder()->RecordDestroyImage(
16792 allocator->GetCurrentFrameIndex(),
16793 allocation);
16794 }
16795#endif
16796
16797 if(image != VK_NULL_HANDLE)
16798 {
16799 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16800 }
16801 if(allocation != VK_NULL_HANDLE)
16802 {
16803 allocator->FreeMemory(
16804 allocationCount: 1, // allocationCount
16805 pAllocations: &allocation);
16806 }
16807}
16808
16809#endif // #ifdef VMA_IMPLEMENTATION
16810

source code of qtbase/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h