blob: 1e1fce1b2bf15d0c14f1f9301f447410d06e95dd [file] [log] [blame]
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06001//
2// Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21//
22
Tony-LunarG0e564722019-03-19 16:09:14 -060023// clang-format off
Tony-LunarG390319b2019-03-18 15:54:16 -060024//
25// Source: https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
26// THIS FILE HAS BEEN CHANGED FROM THE ORIGINAL VERSION
27//
28// Change Log:
29// 3/27/19 - Make changes to suppress warnings from GCC
Mike Schuchardte48dc142019-04-18 09:12:03 -070030// 4/18/19 - Make changes to suppress warnings from clang
Tony-LunarG390319b2019-03-18 15:54:16 -060031//
32
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060033#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
34#define AMD_VULKAN_MEMORY_ALLOCATOR_H
35
36#ifdef __cplusplus
37extern "C" {
38#endif
39
40/** \mainpage Vulkan Memory Allocator
41
42<b>Version 2.2.0</b> (2018-12-13)
43
44Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
45License: MIT
46
47Documentation of all members: vk_mem_alloc.h
48
49\section main_table_of_contents Table of contents
50
51- <b>User guide</b>
52 - \subpage quick_start
53 - [Project setup](@ref quick_start_project_setup)
54 - [Initialization](@ref quick_start_initialization)
55 - [Resource allocation](@ref quick_start_resource_allocation)
56 - \subpage choosing_memory_type
57 - [Usage](@ref choosing_memory_type_usage)
58 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
59 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
60 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
61 - \subpage memory_mapping
62 - [Mapping functions](@ref memory_mapping_mapping_functions)
63 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
64 - [Cache control](@ref memory_mapping_cache_control)
65 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
66 - \subpage custom_memory_pools
67 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
68 - [Linear allocation algorithm](@ref linear_algorithm)
69 - [Free-at-once](@ref linear_algorithm_free_at_once)
70 - [Stack](@ref linear_algorithm_stack)
71 - [Double stack](@ref linear_algorithm_double_stack)
72 - [Ring buffer](@ref linear_algorithm_ring_buffer)
73 - [Buddy allocation algorithm](@ref buddy_algorithm)
74 - \subpage defragmentation
75 - [Defragmenting CPU memory](@ref defragmentation_cpu)
76 - [Defragmenting GPU memory](@ref defragmentation_gpu)
77 - [Additional notes](@ref defragmentation_additional_notes)
78 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
79 - \subpage lost_allocations
80 - \subpage statistics
81 - [Numeric statistics](@ref statistics_numeric_statistics)
82 - [JSON dump](@ref statistics_json_dump)
83 - \subpage allocation_annotation
84 - [Allocation user data](@ref allocation_user_data)
85 - [Allocation names](@ref allocation_names)
86 - \subpage debugging_memory_usage
87 - [Memory initialization](@ref debugging_memory_usage_initialization)
88 - [Margins](@ref debugging_memory_usage_margins)
89 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
90 - \subpage record_and_replay
91- \subpage usage_patterns
92 - [Simple patterns](@ref usage_patterns_simple)
93 - [Advanced patterns](@ref usage_patterns_advanced)
94- \subpage configuration
95 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
96 - [Custom host memory allocator](@ref custom_memory_allocator)
97 - [Device memory allocation callbacks](@ref allocation_callbacks)
98 - [Device heap memory limit](@ref heap_memory_limit)
99 - \subpage vk_khr_dedicated_allocation
100- \subpage general_considerations
101 - [Thread safety](@ref general_considerations_thread_safety)
102 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
103 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
104 - [Features not supported](@ref general_considerations_features_not_supported)
105
106\section main_see_also See also
107
108- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
109- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
110
111
112
113
114\page quick_start Quick start
115
116\section quick_start_project_setup Project setup
117
118Vulkan Memory Allocator comes in form of a single header file.
119You don't need to build it as a separate library project.
120You can add this file directly to your project and submit it to code repository next to your other source files.
121
122"Single header" doesn't mean that everything is contained in C/C++ declarations,
123like it tends to be in case of inline functions or C++ templates.
124It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
125If you don't do it properly, you will get linker errors.
126
127To do it properly:
128
129-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
130 This includes declarations of all members of the library.
131-# In exacly one CPP file define following macro before this include.
132 It enables also internal definitions.
133
134\code
135#define VMA_IMPLEMENTATION
136#include "vk_mem_alloc.h"
137\endcode
138
139It may be a good idea to create dedicated CPP file just for this purpose.
140
141Note on language: This library is written in C++, but has C-compatible interface.
142Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
143implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
144
145Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
146includes `<windows.h>` on Windows. If you need some specific macros defined
147before including these headers (like `WIN32_LEAN_AND_MEAN` or
148`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
149them before every `#include` of this library.
150
151
152\section quick_start_initialization Initialization
153
154At program startup:
155
156-# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
157-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
158 calling vmaCreateAllocator().
159
160\code
161VmaAllocatorCreateInfo allocatorInfo = {};
162allocatorInfo.physicalDevice = physicalDevice;
163allocatorInfo.device = device;
164
165VmaAllocator allocator;
166vmaCreateAllocator(&allocatorInfo, &allocator);
167\endcode
168
169\section quick_start_resource_allocation Resource allocation
170
171When you want to create a buffer or image:
172
173-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
174-# Fill VmaAllocationCreateInfo structure.
175-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
176 already allocated and bound to it.
177
178\code
179VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
180bufferInfo.size = 65536;
181bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
182
183VmaAllocationCreateInfo allocInfo = {};
184allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
185
186VkBuffer buffer;
187VmaAllocation allocation;
188vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
189\endcode
190
191Don't forget to destroy your objects when no longer needed:
192
193\code
194vmaDestroyBuffer(allocator, buffer, allocation);
195vmaDestroyAllocator(allocator);
196\endcode
197
198
199\page choosing_memory_type Choosing memory type
200
201Physical devices in Vulkan support various combinations of memory heaps and
202types. Help with choosing correct and optimal memory type for your specific
203resource is one of the key features of this library. You can use it by filling
204appropriate members of VmaAllocationCreateInfo structure, as described below.
205You can also combine multiple methods.
206
207-# If you just want to find memory type index that meets your requirements, you
208 can use function vmaFindMemoryTypeIndex().
209-# If you want to allocate a region of device memory without association with any
210 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
211 this function is not recommended and usually not needed.
212-# If you already have a buffer or an image created, you want to allocate memory
213 for it and then you will bind it yourself, you can use function
214 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
215 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
216-# If you want to create a buffer or an image, allocate memory for it and bind
217 them together, all in one call, you can use function vmaCreateBuffer(),
218 vmaCreateImage(). This is the recommended way to use this library.
219
220When using 3. or 4., the library internally queries Vulkan for memory types
221supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
222and uses only one of these types.
223
224If no memory type can be found that meets all the requirements, these functions
225return `VK_ERROR_FEATURE_NOT_PRESENT`.
226
227You can leave VmaAllocationCreateInfo structure completely filled with zeros.
228It means no requirements are specified for memory type.
229It is valid, although not very useful.
230
231\section choosing_memory_type_usage Usage
232
233The easiest way to specify memory requirements is to fill member
234VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
235It defines high level, common usage types.
236For more details, see description of this enum.
237
238For example, if you want to create a uniform buffer that will be filled using
239transfer only once or infrequently and used for rendering every frame, you can
240do it using following code:
241
242\code
243VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
244bufferInfo.size = 65536;
245bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
246
247VmaAllocationCreateInfo allocInfo = {};
248allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
249
250VkBuffer buffer;
251VmaAllocation allocation;
252vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
253\endcode
254
255\section choosing_memory_type_required_preferred_flags Required and preferred flags
256
257You can specify more detailed requirements by filling members
258VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
259with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
260if you want to create a buffer that will be persistently mapped on host (so it
261must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
262use following code:
263
264\code
265VmaAllocationCreateInfo allocInfo = {};
266allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
267allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
268allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
269
270VkBuffer buffer;
271VmaAllocation allocation;
272vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
273\endcode
274
275A memory type is chosen that has all the required flags and as many preferred
276flags set as possible.
277
278If you use VmaAllocationCreateInfo::usage, it is just internally converted to
279a set of required and preferred flags.
280
281\section choosing_memory_type_explicit_memory_types Explicit memory types
282
283If you inspected memory types available on the physical device and you have
284a preference for memory types that you want to use, you can fill member
285VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
286means that a memory type with that index is allowed to be used for the
287allocation. Special value 0, just like `UINT32_MAX`, means there are no
288restrictions to memory type index.
289
290Please note that this member is NOT just a memory type index.
291Still you can use it to choose just one, specific memory type.
292For example, if you already determined that your buffer should be created in
293memory type 2, use following code:
294
295\code
296uint32_t memoryTypeIndex = 2;
297
298VmaAllocationCreateInfo allocInfo = {};
299allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
300
301VkBuffer buffer;
302VmaAllocation allocation;
303vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
304\endcode
305
306\section choosing_memory_type_custom_memory_pools Custom memory pools
307
308If you allocate from custom memory pool, all the ways of specifying memory
309requirements described above are not applicable and the aforementioned members
310of VmaAllocationCreateInfo structure are ignored. Memory type is selected
311explicitly when creating the pool and then used to make all the allocations from
312that pool. For further details, see \ref custom_memory_pools.
313
314
315\page memory_mapping Memory mapping
316
317To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
318to be able to read from it or write to it in CPU code.
319Mapping is possible only of memory allocated from a memory type that has
320`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
321Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
322You can use them directly with memory allocated by this library,
323but it is not recommended because of following issue:
324Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
325This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
326Because of this, Vulkan Memory Allocator provides following facilities:
327
328\section memory_mapping_mapping_functions Mapping functions
329
330The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
331They are safer and more convenient to use than standard Vulkan functions.
332You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
333You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
334The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
335For further details, see description of vmaMapMemory() function.
336Example:
337
338\code
339// Having these objects initialized:
340
341struct ConstantBuffer
342{
343 ...
344};
345ConstantBuffer constantBufferData;
346
347VmaAllocator allocator;
348VkBuffer constantBuffer;
349VmaAllocation constantBufferAllocation;
350
351// You can map and fill your buffer using following code:
352
353void* mappedData;
354vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
355memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
356vmaUnmapMemory(allocator, constantBufferAllocation);
357\endcode
358
359When mapping, you may see a warning from Vulkan validation layer similar to this one:
360
361<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>
362
363It happens because the library maps entire `VkDeviceMemory` block, where different
364types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
365You can safely ignore it if you are sure you access only memory of the intended
366object that you wanted to map.
367
368
369\section memory_mapping_persistently_mapped_memory Persistently mapped memory
370
371Kepping your memory persistently mapped is generally OK in Vulkan.
372You don't need to unmap it before using its data on the GPU.
373The library provides a special feature designed for that:
374Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
375VmaAllocationCreateInfo::flags stay mapped all the time,
376so you can just access CPU pointer to it any time
377without a need to call any "map" or "unmap" function.
378Example:
379
380\code
381VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
382bufCreateInfo.size = sizeof(ConstantBuffer);
383bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
384
385VmaAllocationCreateInfo allocCreateInfo = {};
386allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
387allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
388
389VkBuffer buf;
390VmaAllocation alloc;
391VmaAllocationInfo allocInfo;
392vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
393
394// Buffer is already mapped. You can access its memory.
395memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
396\endcode
397
398There are some exceptions though, when you should consider mapping memory only for a short period of time:
399
400- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
401 device is discrete AMD GPU,
402 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
403 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
404 then whenever a memory block allocated from this memory type stays mapped
405 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
406 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
407 matter if that particular memory block is actually used by the command buffer
408 being submitted.
409- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
410 which requires unmapping before GPU can see updated texture.
411- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
412
413\section memory_mapping_cache_control Cache control
414
415Memory in Vulkan doesn't need to be unmapped before using it on GPU,
416but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
417you need to manually invalidate cache before reading of mapped pointer
418and flush cache after writing to mapped pointer.
419Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
420`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
421functions that refer to given allocation object: vmaFlushAllocation(),
422vmaInvalidateAllocation().
423
424Regions of memory specified for flush/invalidate must be aligned to
425`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
426In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
427within blocks are aligned to this value, so their offsets are always multiply of
428`nonCoherentAtomSize` and two different allocations never share same "line" of this size.
429
430Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
431
432Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
433currently provide `HOST_COHERENT` flag on all memory types that are
434`HOST_VISIBLE`, so on this platform you may not need to bother.
435
436\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
437
438It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
439despite it wasn't explicitly requested.
440For example, application may work on integrated graphics with unified memory (like Intel) or
441allocation from video memory might have failed, so the library chose system memory as fallback.
442
443You can detect this case and map such allocation to access its memory on CPU directly,
444instead of launching a transfer operation.
445In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
446and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
447
448\code
449VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
450bufCreateInfo.size = sizeof(ConstantBuffer);
451bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
452
453VmaAllocationCreateInfo allocCreateInfo = {};
454allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
455allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
456
457VkBuffer buf;
458VmaAllocation alloc;
459VmaAllocationInfo allocInfo;
460vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
461
462VkMemoryPropertyFlags memFlags;
463vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
464if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
465{
466 // Allocation ended up in mappable memory. You can map it and access it directly.
467 void* mappedData;
468 vmaMapMemory(allocator, alloc, &mappedData);
469 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
470 vmaUnmapMemory(allocator, alloc);
471}
472else
473{
474 // Allocation ended up in non-mappable memory.
475 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
476}
477\endcode
478
479You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
480that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
481If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
482If not, the flag is just ignored.
483Example:
484
485\code
486VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
487bufCreateInfo.size = sizeof(ConstantBuffer);
488bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
489
490VmaAllocationCreateInfo allocCreateInfo = {};
491allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
492allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
493
494VkBuffer buf;
495VmaAllocation alloc;
496VmaAllocationInfo allocInfo;
497vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
498
499if(allocInfo.pUserData != nullptr)
500{
501 // Allocation ended up in mappable memory.
502 // It's persistently mapped. You can access it directly.
503 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
504}
505else
506{
507 // Allocation ended up in non-mappable memory.
508 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
509}
510\endcode
511
512
513\page custom_memory_pools Custom memory pools
514
515A memory pool contains a number of `VkDeviceMemory` blocks.
516The library automatically creates and manages default pool for each memory type available on the device.
517Default memory pool automatically grows in size.
518Size of allocated blocks is also variable and managed automatically.
519
520You can create custom pool and allocate memory out of it.
521It can be useful if you want to:
522
523- Keep certain kind of allocations separate from others.
524- Enforce particular, fixed size of Vulkan memory blocks.
525- Limit maximum amount of Vulkan memory allocated for that pool.
526- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
527
528To use custom memory pools:
529
530-# Fill VmaPoolCreateInfo structure.
531-# Call vmaCreatePool() to obtain #VmaPool handle.
532-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
533 You don't need to specify any other parameters of this structure, like `usage`.
534
535Example:
536
537\code
538// Create a pool that can have at most 2 blocks, 128 MiB each.
539VmaPoolCreateInfo poolCreateInfo = {};
540poolCreateInfo.memoryTypeIndex = ...
541poolCreateInfo.blockSize = 128ull * 1024 * 1024;
542poolCreateInfo.maxBlockCount = 2;
543
544VmaPool pool;
545vmaCreatePool(allocator, &poolCreateInfo, &pool);
546
547// Allocate a buffer out of it.
548VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
549bufCreateInfo.size = 1024;
550bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
551
552VmaAllocationCreateInfo allocCreateInfo = {};
553allocCreateInfo.pool = pool;
554
555VkBuffer buf;
556VmaAllocation alloc;
557VmaAllocationInfo allocInfo;
558vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
559\endcode
560
561You have to free all allocations made from this pool before destroying it.
562
563\code
564vmaDestroyBuffer(allocator, buf, alloc);
565vmaDestroyPool(allocator, pool);
566\endcode
567
568\section custom_memory_pools_MemTypeIndex Choosing memory type index
569
570When creating a pool, you must explicitly specify memory type index.
571To find the one suitable for your buffers or images, you can use helper functions
572vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
573You need to provide structures with example parameters of buffers or images
574that you are going to create in that pool.
575
576\code
577VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
578exampleBufCreateInfo.size = 1024; // Whatever.
579exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
580
581VmaAllocationCreateInfo allocCreateInfo = {};
582allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
583
584uint32_t memTypeIndex;
585vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
586
587VmaPoolCreateInfo poolCreateInfo = {};
588poolCreateInfo.memoryTypeIndex = memTypeIndex;
589// ...
590\endcode
591
592When creating buffers/images allocated in that pool, provide following parameters:
593
594- `VkBufferCreateInfo`: Prefer to pass same parameters as above.
595 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
596 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
597 or the other way around.
598- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
599 Other members are ignored anyway.
600
601\section linear_algorithm Linear allocation algorithm
602
603Each Vulkan memory block managed by this library has accompanying metadata that
604keeps track of used and unused regions. By default, the metadata structure and
605algorithm tries to find best place for new allocations among free regions to
606optimize memory usage. This way you can allocate and free objects in any order.
607
608![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
609
610Sometimes there is a need to use simpler, linear allocation algorithm. You can
611create custom pool that uses such algorithm by adding flag
612#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
613#VmaPool object. Then an alternative metadata management is used. It always
614creates new allocations after last one and doesn't reuse free regions after
615allocations freed in the middle. It results in better allocation performance and
616less memory consumed by metadata.
617
618![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
619
620With this one flag, you can create a custom pool that can be used in many ways:
621free-at-once, stack, double stack, and ring buffer. See below for details.
622
623\subsection linear_algorithm_free_at_once Free-at-once
624
625In a pool that uses linear algorithm, you still need to free all the allocations
626individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
627them in any order. New allocations are always made after last one - free space
628in the middle is not reused. However, when you release all the allocation and
629the pool becomes empty, allocation starts from the beginning again. This way you
630can use linear algorithm to speed up creation of allocations that you are going
631to release all at once.
632
633![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
634
635This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
636value that allows multiple memory blocks.
637
638\subsection linear_algorithm_stack Stack
639
640When you free an allocation that was created last, its space can be reused.
641Thanks to this, if you always release allocations in the order opposite to their
642creation (LIFO - Last In First Out), you can achieve behavior of a stack.
643
644![Stack](../gfx/Linear_allocator_4_stack.png)
645
646This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
647value that allows multiple memory blocks.
648
649\subsection linear_algorithm_double_stack Double stack
650
651The space reserved by a custom pool with linear algorithm may be used by two
652stacks:
653
654- First, default one, growing up from offset 0.
655- Second, "upper" one, growing down from the end towards lower offsets.
656
657To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
658to VmaAllocationCreateInfo::flags.
659
660![Double stack](../gfx/Linear_allocator_7_double_stack.png)
661
662Double stack is available only in pools with one memory block -
663VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
664
665When the two stacks' ends meet so there is not enough space between them for a
666new allocation, such allocation fails with usual
667`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
668
669\subsection linear_algorithm_ring_buffer Ring buffer
670
671When you free some allocations from the beginning and there is not enough free space
672for a new one at the end of a pool, allocator's "cursor" wraps around to the
673beginning and starts allocation there. Thanks to this, if you always release
674allocations in the same order as you created them (FIFO - First In First Out),
675you can achieve behavior of a ring buffer / queue.
676
677![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
678
679Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
680If there is not enough free space for a new allocation, but existing allocations
681from the front of the queue can become lost, they become lost and the allocation
682succeeds.
683
684![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
685
686Ring buffer is available only in pools with one memory block -
687VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
688
689\section buddy_algorithm Buddy allocation algorithm
690
691There is another allocation algorithm that can be used with custom pools, called
692"buddy". Its internal data structure is based on a tree of blocks, each having
693size that is a power of two and a half of its parent's size. When you want to
694allocate memory of certain size, a free node in the tree is located. If it's too
695large, it is recursively split into two halves (called "buddies"). However, if
696requested allocation size is not a power of two, the size of a tree node is
697aligned up to the nearest power of two and the remaining space is wasted. When
698two buddy nodes become free, they are merged back into one larger node.
699
700![Buddy allocator](../gfx/Buddy_allocator.png)
701
702The advantage of buddy allocation algorithm over default algorithm is faster
703allocation and deallocation, as well as smaller external fragmentation. The
704disadvantage is more wasted space (internal fragmentation).
705
706For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
707or other sources that describe this concept in general.
708
709To use buddy allocation algorithm with a custom pool, add flag
710#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
711#VmaPool object.
712
713Several limitations apply to pools that use buddy algorithm:
714
715- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
716 Otherwise, only largest power of two smaller than the size is used for
717 allocations. The remaining space always stays unused.
718- [Margins](@ref debugging_memory_usage_margins) and
719 [corruption detection](@ref debugging_memory_usage_corruption_detection)
720 don't work in such pools.
721- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
722 use them, but they never become lost. Support may be added in the future.
723- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
724 such pool.
725
726\page defragmentation Defragmentation
727
728Interleaved allocations and deallocations of many objects of varying size can
729cause fragmentation over time, which can lead to a situation where the library is unable
730to find a continuous range of free memory for a new allocation despite there is
731enough free space, just scattered across many small free ranges between existing
732allocations.
733
734To mitigate this problem, you can use defragmentation feature:
735structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
736Given set of allocations,
737this function can move them to compact used memory, ensure more continuous free
738space and possibly also free some `VkDeviceMemory` blocks.
739
740What the defragmentation does is:
741
742- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
743 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
744 VmaAllocationInfo::offset changes. You must query them again using
745 vmaGetAllocationInfo() if you need them.
746- Moves actual data in memory.
747
748What it doesn't do, so you need to do it yourself:
749
750- Recreate buffers and images that were bound to allocations that were defragmented and
751 bind them with their new places in memory.
752 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
753 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
754 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
755 destroy or create allocation objects!
756- Recreate views and update descriptors that point to these buffers and images.
757
758\section defragmentation_cpu Defragmenting CPU memory
759
760Following example demonstrates how you can run defragmentation on CPU.
761Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
762Others are ignored.
763
764The way it works is:
765
766- It temporarily maps entire memory blocks when necessary.
767- It moves data using `memmove()` function.
768
769\code
770// Given following variables already initialized:
771VkDevice device;
772VmaAllocator allocator;
773std::vector<VkBuffer> buffers;
774std::vector<VmaAllocation> allocations;
775
776
777const uint32_t allocCount = (uint32_t)allocations.size();
778std::vector<VkBool32> allocationsChanged(allocCount);
779
780VmaDefragmentationInfo2 defragInfo = {};
781defragInfo.allocationCount = allocCount;
782defragInfo.pAllocations = allocations.data();
783defragInfo.pAllocationsChanged = allocationsChanged.data();
784defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
785defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
786
787VmaDefragmentationContext defragCtx;
788vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
789vmaDefragmentationEnd(allocator, defragCtx);
790
791for(uint32_t i = 0; i < allocCount; ++i)
792{
793 if(allocationsChanged[i])
794 {
795 // Destroy buffer that is immutably bound to memory region which is no longer valid.
796 vkDestroyBuffer(device, buffers[i], nullptr);
797
798 // Create new buffer with same parameters.
799 VkBufferCreateInfo bufferInfo = ...;
800 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
801
802 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
803
804 // Bind new buffer to new memory region. Data contained in it is already moved.
805 VmaAllocationInfo allocInfo;
806 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
807 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
808 }
809}
810\endcode
811
812Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
813This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
814has been modified during defragmentation.
815You can pass null, but you then need to query every allocation passed to defragmentation
816for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
817
818If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
819you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
820instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
821to defragment all allocations in given pools.
822You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
823You can also combine both methods.
824
825\section defragmentation_gpu Defragmenting GPU memory
826
827It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
828To do that, you need to pass a command buffer that meets requirements as described in
829VmaDefragmentationInfo2::commandBuffer. The way it works is:
830
831- It creates temporary buffers and binds them to entire memory blocks when necessary.
832- It issues `vkCmdCopyBuffer()` to passed command buffer.
833
834Example:
835
836\code
837// Given following variables already initialized:
838VkDevice device;
839VmaAllocator allocator;
840VkCommandBuffer commandBuffer;
841std::vector<VkBuffer> buffers;
842std::vector<VmaAllocation> allocations;
843
844
845const uint32_t allocCount = (uint32_t)allocations.size();
846std::vector<VkBool32> allocationsChanged(allocCount);
847
848VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
849vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
850
851VmaDefragmentationInfo2 defragInfo = {};
852defragInfo.allocationCount = allocCount;
853defragInfo.pAllocations = allocations.data();
854defragInfo.pAllocationsChanged = allocationsChanged.data();
855defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
856defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
857defragInfo.commandBuffer = commandBuffer;
858
859VmaDefragmentationContext defragCtx;
860vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
861
862vkEndCommandBuffer(commandBuffer);
863
864// Submit commandBuffer.
865// Wait for a fence that ensures commandBuffer execution finished.
866
867vmaDefragmentationEnd(allocator, defragCtx);
868
869for(uint32_t i = 0; i < allocCount; ++i)
870{
871 if(allocationsChanged[i])
872 {
873 // Destroy buffer that is immutably bound to memory region which is no longer valid.
874 vkDestroyBuffer(device, buffers[i], nullptr);
875
876 // Create new buffer with same parameters.
877 VkBufferCreateInfo bufferInfo = ...;
878 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
879
880 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
881
882 // Bind new buffer to new memory region. Data contained in it is already moved.
883 VmaAllocationInfo allocInfo;
884 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
885 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
886 }
887}
888\endcode
889
890You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
891The library automatically chooses best method to defragment each memory pool.
892
893You may try not to block your entire program to wait until defragmentation finishes,
894but do it in the background, as long as you carefully fullfill requirements described
895in function vmaDefragmentationBegin().
896
897\section defragmentation_additional_notes Additional notes
898
899While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
900See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
901
902If you defragment allocations bound to images, these images should be created with
903`VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
904parameters and pointing to data copied to another memory region will interpret
905its contents consistently. Otherwise you may experience corrupted data on some
906implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
907
908If you defragment allocations bound to images, new images to be bound to new
909memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
910and then transitioned to their original layout from before defragmentation using
911an image memory barrier.
912
913Please don't expect memory to be fully compacted after defragmentation.
914Algorithms inside are based on some heuristics that try to maximize number of Vulkan
915memory blocks to make totally empty to release them, as well as to maximimze continuous
916empty space inside remaining blocks, while minimizing the number and size of allocations that
917need to be moved. Some fragmentation may still remain - this is normal.
918
919\section defragmentation_custom_algorithm Writing custom defragmentation algorithm
920
921If you want to implement your own, custom defragmentation algorithm,
922there is infrastructure prepared for that,
923but it is not exposed through the library API - you need to hack its source code.
924Here are steps needed to do this:
925
926-# Main thing you need to do is to define your own class derived from base abstract
927 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
928 See definition and comments of this class for details.
929-# Your code needs to interact with device memory block metadata.
930 If you need more access to its data than it's provided by its public interface,
931 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
932-# If you want to create a flag that would enable your algorithm or pass some additional
933 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
934 VmaDefragmentationInfo2::flags.
935-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
936 of your new class whenever needed.
937
938
939\page lost_allocations Lost allocations
940
941If your game oversubscribes video memory, if may work OK in previous-generation
942graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
943paged to system RAM. In Vulkan you can't do it because when you run out of
944memory, an allocation just fails. If you have more data (e.g. textures) that can
945fit into VRAM and you don't need it all at once, you may want to upload them to
946GPU on demand and "push out" ones that are not used for a long time to make room
947for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
948cache. Vulkan Memory Allocator can help you with that by supporting a concept of
949"lost allocations".
950
951To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
952flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
953such allocation in every new frame, you need to query it if it's not lost.
954To check it, call vmaTouchAllocation().
955If the allocation is lost, you should not use it or buffer/image bound to it.
956You mustn't forget to destroy this allocation and this buffer/image.
957vmaGetAllocationInfo() can also be used for checking status of the allocation.
958Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
959
960To create an allocation that can make some other allocations lost to make room
961for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
962usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
963#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
964
965Warning! Current implementation uses quite naive, brute force algorithm,
966which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
967flag quite slow. A new, more optimal algorithm and data structure to speed this
968up is planned for the future.
969
970<b>Q: When interleaving creation of new allocations with usage of existing ones,
971how do you make sure that an allocation won't become lost while it's used in the
972current frame?</b>
973
974It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
975status/parameters and checks whether it's not lost, but when it's not, it also
976atomically marks it as used in the current frame, which makes it impossible to
977become lost in that frame. It uses lockless algorithm, so it works fast and
978doesn't involve locking any internal mutex.
979
980<b>Q: What if my allocation may still be in use by the GPU when it's rendering a
981previous frame while I already submit new frame on the CPU?</b>
982
983You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
984become lost for a number of additional frames back from the current one by
985specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
986memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
987
988<b>Q: How do you inform the library when new frame starts?</b>
989
990You need to call function vmaSetCurrentFrameIndex().
991
992Example code:
993
994\code
995struct MyBuffer
996{
997 VkBuffer m_Buf = nullptr;
998 VmaAllocation m_Alloc = nullptr;
999
1000 // Called when the buffer is really needed in the current frame.
1001 void EnsureBuffer();
1002};
1003
1004void MyBuffer::EnsureBuffer()
1005{
1006 // Buffer has been created.
1007 if(m_Buf != VK_NULL_HANDLE)
1008 {
1009 // Check if its allocation is not lost + mark it as used in current frame.
1010 if(vmaTouchAllocation(allocator, m_Alloc))
1011 {
1012 // It's all OK - safe to use m_Buf.
1013 return;
1014 }
1015 }
1016
1017 // Buffer not yet exists or lost - destroy and recreate it.
1018
1019 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1020
1021 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1022 bufCreateInfo.size = 1024;
1023 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1024
1025 VmaAllocationCreateInfo allocCreateInfo = {};
1026 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1027 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1028 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1029
1030 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1031}
1032\endcode
1033
1034When using lost allocations, you may see some Vulkan validation layer warnings
1035about overlapping regions of memory bound to different kinds of buffers and
1036images. This is still valid as long as you implement proper handling of lost
1037allocations (like in the example above) and don't use them.
1038
1039You can create an allocation that is already in lost state from the beginning using function
1040vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1041
1042You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1043in a specified custom pool to lost state.
1044Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1045cannot become lost.
1046
1047<b>Q: Can I touch allocation that cannot become lost?</b>
1048
1049Yes, although it has no visible effect.
1050Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1051also for allocations that cannot become lost, but the only way to observe it is to dump
1052internal allocator state using vmaBuildStatsString().
1053You can use this feature for debugging purposes to explicitly mark allocations that you use
1054in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1055
1056
1057\page statistics Statistics
1058
1059This library contains functions that return information about its internal state,
1060especially the amount of memory allocated from Vulkan.
1061Please keep in mind that these functions need to traverse all internal data structures
1062to gather these information, so they may be quite time-consuming.
1063Don't call them too often.
1064
1065\section statistics_numeric_statistics Numeric statistics
1066
1067You can query for overall statistics of the allocator using function vmaCalculateStats().
1068Information are returned using structure #VmaStats.
1069It contains #VmaStatInfo - number of allocated blocks, number of allocations
1070(occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1071number of bytes used and unused (but still allocated from Vulkan) and other information.
1072They are summed across memory heaps, memory types and total for whole allocator.
1073
1074You can query for statistics of a custom pool using function vmaGetPoolStats().
1075Information are returned using structure #VmaPoolStats.
1076
1077You can query for information about specific allocation using function vmaGetAllocationInfo().
1078It fill structure #VmaAllocationInfo.
1079
1080\section statistics_json_dump JSON dump
1081
1082You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1083The result is guaranteed to be correct JSON.
1084It uses ANSI encoding.
1085Any strings provided by user (see [Allocation names](@ref allocation_names))
1086are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1087this JSON string can be treated as using this encoding.
1088It must be freed using function vmaFreeStatsString().
1089
1090The format of this JSON string is not part of official documentation of the library,
1091but it will not change in backward-incompatible way without increasing library major version number
1092and appropriate mention in changelog.
1093
1094The JSON string contains all the data that can be obtained using vmaCalculateStats().
1095It can also contain detailed map of allocated memory blocks and their regions -
1096free and occupied by allocations.
1097This allows e.g. to visualize the memory or assess fragmentation.
1098
1099
1100\page allocation_annotation Allocation names and user data
1101
1102\section allocation_user_data Allocation user data
1103
1104You can annotate allocations with your own information, e.g. for debugging purposes.
1105To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1106an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1107some handle, index, key, ordinal number or any other value that would associate
1108the allocation with your custom metadata.
1109
1110\code
1111VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1112// Fill bufferInfo...
1113
1114MyBufferMetadata* pMetadata = CreateBufferMetadata();
1115
1116VmaAllocationCreateInfo allocCreateInfo = {};
1117allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1118allocCreateInfo.pUserData = pMetadata;
1119
1120VkBuffer buffer;
1121VmaAllocation allocation;
1122vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1123\endcode
1124
1125The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1126
1127\code
1128VmaAllocationInfo allocInfo;
1129vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1130MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1131\endcode
1132
1133It can also be changed using function vmaSetAllocationUserData().
1134
1135Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1136vmaBuildStatsString(), in hexadecimal form.
1137
1138\section allocation_names Allocation names
1139
1140There is alternative mode available where `pUserData` pointer is used to point to
1141a null-terminated string, giving a name to the allocation. To use this mode,
1142set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1143Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1144vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1145The library creates internal copy of the string, so the pointer you pass doesn't need
1146to be valid for whole lifetime of the allocation. You can free it after the call.
1147
1148\code
1149VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1150// Fill imageInfo...
1151
1152std::string imageName = "Texture: ";
1153imageName += fileName;
1154
1155VmaAllocationCreateInfo allocCreateInfo = {};
1156allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1157allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1158allocCreateInfo.pUserData = imageName.c_str();
1159
1160VkImage image;
1161VmaAllocation allocation;
1162vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1163\endcode
1164
1165The value of `pUserData` pointer of the allocation will be different than the one
1166you passed when setting allocation's name - pointing to a buffer managed
1167internally that holds copy of the string.
1168
1169\code
1170VmaAllocationInfo allocInfo;
1171vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1172const char* imageName = (const char*)allocInfo.pUserData;
1173printf("Image name: %s\n", imageName);
1174\endcode
1175
1176That string is also printed in JSON report created by vmaBuildStatsString().
1177
1178
1179\page debugging_memory_usage Debugging incorrect memory usage
1180
1181If you suspect a bug with memory usage, like usage of uninitialized memory or
1182memory being overwritten out of bounds of an allocation,
1183you can use debug features of this library to verify this.
1184
1185\section debugging_memory_usage_initialization Memory initialization
1186
1187If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1188you can enable automatic memory initialization to verify this.
1189To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1190
1191\code
1192#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1193#include "vk_mem_alloc.h"
1194\endcode
1195
1196It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1197Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1198Memory is automatically mapped and unmapped if necessary.
1199
1200If you find these values while debugging your program, good chances are that you incorrectly
1201read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1202
1203Memory initialization works only with memory types that are `HOST_VISIBLE`.
1204It works also with dedicated allocations.
1205It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1206as they cannot be mapped.
1207
1208\section debugging_memory_usage_margins Margins
1209
1210By default, allocations are laid out in memory blocks next to each other if possible
1211(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1212
1213![Allocations without margin](../gfx/Margins_1.png)
1214
1215Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1216number of bytes as a margin before and after every allocation.
1217
1218\code
1219#define VMA_DEBUG_MARGIN 16
1220#include "vk_mem_alloc.h"
1221\endcode
1222
1223![Allocations with margin](../gfx/Margins_2.png)
1224
1225If your bug goes away after enabling margins, it means it may be caused by memory
1226being overwritten outside of allocation boundaries. It is not 100% certain though.
1227Change in application behavior may also be caused by different order and distribution
1228of allocations across memory blocks after margins are applied.
1229
1230The margin is applied also before first and after last allocation in a block.
1231It may occur only once between two adjacent allocations.
1232
1233Margins work with all types of memory.
1234
1235Margin is applied only to allocations made out of memory blocks and not to dedicated
1236allocations, which have their own memory block of specific size.
1237It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1238or those automatically decided to put into dedicated allocations, e.g. due to its
1239large size or recommended by VK_KHR_dedicated_allocation extension.
1240Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1241
1242Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1243
1244Note that enabling margins increases memory usage and fragmentation.
1245
1246\section debugging_memory_usage_corruption_detection Corruption detection
1247
1248You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1249of contents of the margins.
1250
1251\code
1252#define VMA_DEBUG_MARGIN 16
1253#define VMA_DEBUG_DETECT_CORRUPTION 1
1254#include "vk_mem_alloc.h"
1255\endcode
1256
1257When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1258(it must be multiply of 4) before and after every allocation is filled with a magic number.
1259This idea is also know as "canary".
1260Memory is automatically mapped and unmapped if necessary.
1261
1262This number is validated automatically when the allocation is destroyed.
1263If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1264It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1265which indicates a serious bug.
1266
1267You can also explicitly request checking margins of all allocations in all memory blocks
1268that belong to specified memory types by using function vmaCheckCorruption(),
1269or in memory blocks that belong to specified custom pool, by using function
1270vmaCheckPoolCorruption().
1271
1272Margin validation (corruption detection) works only for memory types that are
1273`HOST_VISIBLE` and `HOST_COHERENT`.
1274
1275
1276\page record_and_replay Record and replay
1277
1278\section record_and_replay_introduction Introduction
1279
1280While using the library, sequence of calls to its functions together with their
1281parameters can be recorded to a file and later replayed using standalone player
1282application. It can be useful to:
1283
1284- Test correctness - check if same sequence of calls will not cause crash or
1285 failures on a target platform.
1286- Gather statistics - see number of allocations, peak memory usage, number of
1287 calls etc.
1288- Benchmark performance - see how much time it takes to replay the whole
1289 sequence.
1290
1291\section record_and_replay_usage Usage
1292
1293<b>To record sequence of calls to a file:</b> Fill in
1294VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1295object. File is opened and written during whole lifetime of the allocator.
1296
1297<b>To replay file:</b> Use VmaReplay - standalone command-line program.
1298Precompiled binary can be found in "bin" directory.
1299Its source can be found in "src/VmaReplay" directory.
1300Its project is generated by Premake.
1301Command line syntax is printed when the program is launched without parameters.
1302Basic usage:
1303
1304 VmaReplay.exe MyRecording.csv
1305
1306<b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1307It's a human-readable, text file in CSV format (Comma Separated Values).
1308
1309\section record_and_replay_additional_considerations Additional considerations
1310
1311- Replaying file that was recorded on a different GPU (with different parameters
1312 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1313 set of memory heaps and types) may give different performance and memory usage
1314 results, as well as issue some warnings and errors.
1315- Current implementation of recording in VMA, as well as VmaReplay application, is
1316 coded and tested only on Windows. Inclusion of recording code is driven by
1317 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1318 add. Contributions are welcomed.
1319- Currently calls to vmaDefragment() function are not recorded.
1320
1321
1322\page usage_patterns Recommended usage patterns
1323
1324See also slides from talk:
1325[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)
1326
1327
1328\section usage_patterns_simple Simple patterns
1329
1330\subsection usage_patterns_simple_render_targets Render targets
1331
1332<b>When:</b>
1333Any resources that you frequently write and read on GPU,
1334e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1335images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1336
1337<b>What to do:</b>
1338Create them in video memory that is fastest to access from GPU using
1339#VMA_MEMORY_USAGE_GPU_ONLY.
1340
1341Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1342and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1343especially if they are large or if you plan to destroy and recreate them e.g. when
1344display resolution changes.
1345Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1346
1347\subsection usage_patterns_simple_immutable_resources Immutable resources
1348
1349<b>When:</b>
1350Any resources that you fill on CPU only once (aka "immutable") or infrequently
1351and then read frequently on GPU,
1352e.g. textures, vertex and index buffers, constant buffers that don't change often.
1353
1354<b>What to do:</b>
1355Create them in video memory that is fastest to access from GPU using
1356#VMA_MEMORY_USAGE_GPU_ONLY.
1357
1358To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1359in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1360and submit a transfer from it to the GPU resource.
1361You can keep the staging copy if you need it for another upload transfer in the future.
1362If you don't, you can destroy it or reuse this buffer for uploading different resource
1363after the transfer finishes.
1364
1365Prefer to create just buffers in system memory rather than images, even for uploading textures.
1366Use `vkCmdCopyBufferToImage()`.
1367Dont use images with `VK_IMAGE_TILING_LINEAR`.
1368
1369\subsection usage_patterns_dynamic_resources Dynamic resources
1370
1371<b>When:</b>
1372Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1373written on CPU, read on GPU.
1374
1375<b>What to do:</b>
1376Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1377You can map it and write to it directly on CPU, as well as read from it on GPU.
1378
1379This is a more complex situation. Different solutions are possible,
1380and the best one depends on specific GPU type, but you can use this simple approach for the start.
1381Prefer to write to such resource sequentially (e.g. using `memcpy`).
1382Don't perform random access or any reads from it on CPU, as it may be very slow.
1383
1384\subsection usage_patterns_readback Readback
1385
1386<b>When:</b>
1387Resources that contain data written by GPU that you want to read back on CPU,
1388e.g. results of some computations.
1389
1390<b>What to do:</b>
1391Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1392You can write to them directly on GPU, as well as map and read them on CPU.
1393
1394\section usage_patterns_advanced Advanced patterns
1395
1396\subsection usage_patterns_integrated_graphics Detecting integrated graphics
1397
1398You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1399by detecting it in Vulkan.
1400To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1401`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1402When you find it, you can assume that memory is unified and all memory types are comparably fast
1403to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1404
1405You can then sum up sizes of all available memory heaps and treat them as useful for
1406your GPU resources, instead of only `DEVICE_LOCAL` ones.
1407You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1408directly instead of submitting explicit transfer (see below).
1409
1410\subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1411
1412For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1413
1414-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1415 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1416-# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1417 read it directly on GPU.
1418-# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1419 read it directly on GPU.
1420
1421Which solution is the most efficient depends on your resource and especially on the GPU.
1422It is best to measure it and then make the decision.
1423Some general recommendations:
1424
1425- On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1426 related to using a second copy and making transfer.
1427- For small resources (e.g. constant buffers) use (2).
1428 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1429 Even if the resource ends up in system memory, its data may be cached on GPU after first
1430 fetch over PCIe bus.
1431- For larger resources (e.g. textures), decide between (1) and (2).
1432 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1433 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1434
1435Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1436solutions are possible:
1437
1438-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1439 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1440-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1441 map it and read it on CPU.
1442
1443You should take some measurements to decide which option is faster in case of your specific
1444resource.
1445
1446If you don't want to specialize your code for specific types of GPUs, you can still make
1447an simple optimization for cases when your resource ends up in mappable memory to use it
1448directly in this case instead of creating CPU-side staging copy.
1449For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1450
1451
1452\page configuration Configuration
1453
1454Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1455before each include of this file or change directly in this file to provide
1456your own implementation of basic facilities like assert, `min()` and `max()` functions,
1457mutex, atomic etc.
1458The library uses its own implementation of containers by default, but you can switch to using
1459STL containers instead.
1460
1461\section config_Vulkan_functions Pointers to Vulkan functions
1462
1463The library uses Vulkan functions straight from the `vulkan.h` header by default.
1464If you want to provide your own pointers to these functions, e.g. fetched using
1465`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1466
1467-# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1468-# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1469
1470\section custom_memory_allocator Custom host memory allocator
1471
1472If you use custom allocator for CPU memory rather than default operator `new`
1473and `delete` from C++, you can make this library using your allocator as well
1474by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1475functions will be passed to Vulkan, as well as used by the library itself to
1476make any CPU-side allocations.
1477
1478\section allocation_callbacks Device memory allocation callbacks
1479
1480The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1481You can setup callbacks to be informed about these calls, e.g. for the purpose
1482of gathering some statistics. To do it, fill optional member
1483VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1484
1485\section heap_memory_limit Device heap memory limit
1486
1487If you want to test how your program behaves with limited amount of Vulkan device
1488memory available without switching your graphics card to one that really has
1489smaller VRAM, you can use a feature of this library intended for this purpose.
1490To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1491
1492
1493
1494\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1495
1496VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1497performance on some GPUs. It augments Vulkan API with possibility to query
1498driver whether it prefers particular buffer or image to have its own, dedicated
1499allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1500to do some internal optimizations.
1501
1502The extension is supported by this library. It will be used automatically when
1503enabled. To enable it:
1504
15051 . When creating Vulkan device, check if following 2 device extensions are
1506supported (call `vkEnumerateDeviceExtensionProperties()`).
1507If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1508
1509- VK_KHR_get_memory_requirements2
1510- VK_KHR_dedicated_allocation
1511
1512If you enabled these extensions:
1513
15142 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1515your #VmaAllocator`to inform the library that you enabled required extensions
1516and you want the library to use them.
1517
1518\code
1519allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1520
1521vmaCreateAllocator(&allocatorInfo, &allocator);
1522\endcode
1523
1524That's all. The extension will be automatically used whenever you create a
1525buffer using vmaCreateBuffer() or image using vmaCreateImage().
1526
1527When using the extension together with Vulkan Validation Layer, you will receive
1528warnings like this:
1529
1530 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1531
1532It is OK, you should just ignore it. It happens because you use function
1533`vkGetBufferMemoryRequirements2KHR()` instead of standard
1534`vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1535unaware of it.
1536
1537To learn more about this extension, see:
1538
1539- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1540- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1541
1542
1543
1544\page general_considerations General considerations
1545
1546\section general_considerations_thread_safety Thread safety
1547
1548- The library has no global state, so separate #VmaAllocator objects can be used
1549 independently.
1550 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1551- By default, all calls to functions that take #VmaAllocator as first parameter
1552 are safe to call from multiple threads simultaneously because they are
1553 synchronized internally when needed.
1554- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1555 flag, calls to functions that take such #VmaAllocator object must be
1556 synchronized externally.
1557- Access to a #VmaAllocation object must be externally synchronized. For example,
1558 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1559 threads at the same time if you pass the same #VmaAllocation object to these
1560 functions.
1561
1562\section general_considerations_validation_layer_warnings Validation layer warnings
1563
1564When using this library, you can meet following types of warnings issued by
1565Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1566to just ignore them.
1567
1568- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1569 - It happens when VK_KHR_dedicated_allocation extension is enabled.
1570 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1571- *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.*
1572 - It happens when you map a buffer or image, because the library maps entire
1573 `VkDeviceMemory` block, where different types of images and buffers may end
1574 up together, especially on GPUs with unified memory like Intel.
1575- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1576 - It happens when you use lost allocations, and a new image or buffer is
1577 created in place of an existing object that bacame lost.
1578 - It may happen also when you use [defragmentation](@ref defragmentation).
1579
1580\section general_considerations_allocation_algorithm Allocation algorithm
1581
1582The library uses following algorithm for allocation, in order:
1583
1584-# Try to find free range of memory in existing blocks.
1585-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1586-# If failed, try to create such block with size/2, size/4, size/8.
1587-# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1588 specified, try to find space in existing blocks, possilby making some other
1589 allocations lost.
1590-# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1591 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1592-# If failed, choose other memory type that meets the requirements specified in
1593 VmaAllocationCreateInfo and go to point 1.
1594-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1595
1596\section general_considerations_features_not_supported Features not supported
1597
1598Features deliberately excluded from the scope of this library:
1599
1600- Data transfer. Uploading (straming) and downloading data of buffers and images
1601 between CPU and GPU memory and related synchronization is responsibility of the user.
1602- Allocations for imported/exported external memory. They tend to require
1603 explicit memory type index and dedicated allocation anyway, so they don't
1604 interact with main features of this library. Such special purpose allocations
1605 should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1606- Recreation of buffers and images. Although the library has functions for
1607 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1608 recreate these objects yourself after defragmentation. That's because the big
1609 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1610 #VmaAllocation object.
1611- Handling CPU memory allocation failures. When dynamically creating small C++
1612 objects in CPU memory (not Vulkan memory), allocation failures are not checked
1613 and handled gracefully, because that would complicate code significantly and
1614 is usually not needed in desktop PC applications anyway.
1615- Code free of any compiler warnings. Maintaining the library to compile and
1616 work correctly on so many different platforms is hard enough. Being free of
1617 any warnings, on any version of any compiler, is simply not feasible.
1618- This is a C++ library with C interface.
1619 Bindings or ports to any other programming languages are welcomed as external projects and
1620 are not going to be included into this repository.
1621
1622*/
1623
1624/*
1625Define this macro to 0/1 to disable/enable support for recording functionality,
1626available through VmaAllocatorCreateInfo::pRecordSettings.
1627*/
1628#ifndef VMA_RECORDING_ENABLED
1629 #ifdef _WIN32
1630 #define VMA_RECORDING_ENABLED 1
1631 #else
1632 #define VMA_RECORDING_ENABLED 0
1633 #endif
1634#endif
1635
1636#ifndef NOMINMAX
1637 #define NOMINMAX // For windows.h
1638#endif
1639
1640#ifndef VULKAN_H_
1641 #include <vulkan/vulkan.h>
1642#endif
1643
1644#if VMA_RECORDING_ENABLED
1645 #include <windows.h>
1646#endif
1647
1648#if !defined(VMA_DEDICATED_ALLOCATION)
1649 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1650 #define VMA_DEDICATED_ALLOCATION 1
1651 #else
1652 #define VMA_DEDICATED_ALLOCATION 0
1653 #endif
1654#endif
1655
1656/** \struct VmaAllocator
1657\brief Represents main object of this library initialized.
1658
1659Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1660Call function vmaDestroyAllocator() to destroy it.
1661
1662It is recommended to create just one object of this type per `VkDevice` object,
1663right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1664*/
1665VK_DEFINE_HANDLE(VmaAllocator)
1666
1667/// Callback function called after successful vkAllocateMemory.
1668typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1669 VmaAllocator allocator,
1670 uint32_t memoryType,
1671 VkDeviceMemory memory,
1672 VkDeviceSize size);
1673/// Callback function called before vkFreeMemory.
1674typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1675 VmaAllocator allocator,
1676 uint32_t memoryType,
1677 VkDeviceMemory memory,
1678 VkDeviceSize size);
1679
1680/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1681
1682Provided for informative purpose, e.g. to gather statistics about number of
1683allocations or total amount of memory allocated in Vulkan.
1684
1685Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1686*/
1687typedef struct VmaDeviceMemoryCallbacks {
1688 /// Optional, can be null.
1689 PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1690 /// Optional, can be null.
1691 PFN_vmaFreeDeviceMemoryFunction pfnFree;
1692} VmaDeviceMemoryCallbacks;
1693
1694/// Flags for created #VmaAllocator.
1695typedef enum VmaAllocatorCreateFlagBits {
1696 /** \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.
1697
1698 Using this flag may increase performance because internal mutexes are not used.
1699 */
1700 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1701 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1702
1703 Using this extenion will automatically allocate dedicated blocks of memory for
1704 some buffers and images instead of suballocating place for them out of bigger
1705 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1706 flag) when it is recommended by the driver. It may improve performance on some
1707 GPUs.
1708
1709 You may set this flag only if you found out that following device extensions are
1710 supported, you enabled them while creating Vulkan device passed as
1711 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1712 library:
1713
1714 - VK_KHR_get_memory_requirements2
1715 - VK_KHR_dedicated_allocation
1716
1717When this flag is set, you can experience following warnings reported by Vulkan
1718validation layer. You can ignore them.
1719
1720> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1721 */
1722 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1723
1724 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1725} VmaAllocatorCreateFlagBits;
1726typedef VkFlags VmaAllocatorCreateFlags;
1727
1728/** \brief Pointers to some Vulkan functions - a subset used by the library.
1729
1730Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1731*/
1732typedef struct VmaVulkanFunctions {
1733 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1734 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1735 PFN_vkAllocateMemory vkAllocateMemory;
1736 PFN_vkFreeMemory vkFreeMemory;
1737 PFN_vkMapMemory vkMapMemory;
1738 PFN_vkUnmapMemory vkUnmapMemory;
1739 PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1740 PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1741 PFN_vkBindBufferMemory vkBindBufferMemory;
1742 PFN_vkBindImageMemory vkBindImageMemory;
1743 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1744 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1745 PFN_vkCreateBuffer vkCreateBuffer;
1746 PFN_vkDestroyBuffer vkDestroyBuffer;
1747 PFN_vkCreateImage vkCreateImage;
1748 PFN_vkDestroyImage vkDestroyImage;
1749 PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1750#if VMA_DEDICATED_ALLOCATION
1751 PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1752 PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1753#endif
1754} VmaVulkanFunctions;
1755
1756/// Flags to be used in VmaRecordSettings::flags.
1757typedef enum VmaRecordFlagBits {
1758 /** \brief Enables flush after recording every function call.
1759
1760 Enable it if you expect your application to crash, which may leave recording file truncated.
1761 It may degrade performance though.
1762 */
1763 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1764
1765 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1766} VmaRecordFlagBits;
1767typedef VkFlags VmaRecordFlags;
1768
1769/// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1770typedef struct VmaRecordSettings
1771{
1772 /// Flags for recording. Use #VmaRecordFlagBits enum.
1773 VmaRecordFlags flags;
1774 /** \brief Path to the file that should be written by the recording.
1775
1776 Suggested extension: "csv".
1777 If the file already exists, it will be overwritten.
1778 It will be opened for the whole time #VmaAllocator object is alive.
1779 If opening this file fails, creation of the whole allocator object fails.
1780 */
1781 const char* pFilePath;
1782} VmaRecordSettings;
1783
1784/// Description of a Allocator to be created.
1785typedef struct VmaAllocatorCreateInfo
1786{
1787 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1788 VmaAllocatorCreateFlags flags;
1789 /// Vulkan physical device.
1790 /** It must be valid throughout whole lifetime of created allocator. */
1791 VkPhysicalDevice physicalDevice;
1792 /// Vulkan device.
1793 /** It must be valid throughout whole lifetime of created allocator. */
1794 VkDevice device;
1795 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1796 /** Set to 0 to use default, which is currently 256 MiB. */
1797 VkDeviceSize preferredLargeHeapBlockSize;
1798 /// Custom CPU memory allocation callbacks. Optional.
1799 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1800 const VkAllocationCallbacks* pAllocationCallbacks;
1801 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1802 /** Optional, can be null. */
1803 const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1804 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1805
1806 This value is used only when you make allocations with
1807 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1808 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1809
1810 For example, if you double-buffer your command buffers, so resources used for
1811 rendering in previous frame may still be in use by the GPU at the moment you
1812 allocate resources needed for the current frame, set this value to 1.
1813
1814 If you want to allow any allocations other than used in the current frame to
1815 become lost, set this value to 0.
1816 */
1817 uint32_t frameInUseCount;
1818 /** \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.
1819
1820 If not NULL, it must be a pointer to an array of
1821 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1822 maximum number of bytes that can be allocated out of particular Vulkan memory
1823 heap.
1824
1825 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1826 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1827
1828 If there is a limit defined for a heap:
1829
1830 - If user tries to allocate more memory from that heap using this allocator,
1831 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1832 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1833 value of this limit will be reported instead when using vmaGetMemoryProperties().
1834
1835 Warning! Using this feature may not be equivalent to installing a GPU with
1836 smaller amount of memory, because graphics driver doesn't necessary fail new
1837 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1838 exceeded. It may return success and just silently migrate some device memory
1839 blocks to system RAM. This driver behavior can also be controlled using
1840 VK_AMD_memory_overallocation_behavior extension.
1841 */
1842 const VkDeviceSize* pHeapSizeLimit;
1843 /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1844
1845 If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1846 you can pass null as this member, because the library will fetch pointers to
1847 Vulkan functions internally in a static way, like:
1848
1849 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1850
1851 Fill this member if you want to provide your own pointers to Vulkan functions,
1852 e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1853 */
1854 const VmaVulkanFunctions* pVulkanFunctions;
1855 /** \brief Parameters for recording of VMA calls. Can be null.
1856
1857 If not null, it enables recording of calls to VMA functions to a file.
1858 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1859 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1860 */
1861 const VmaRecordSettings* pRecordSettings;
1862} VmaAllocatorCreateInfo;
1863
1864/// Creates Allocator object.
1865VkResult vmaCreateAllocator(
1866 const VmaAllocatorCreateInfo* pCreateInfo,
1867 VmaAllocator* pAllocator);
1868
1869/// Destroys allocator object.
1870void vmaDestroyAllocator(
1871 VmaAllocator allocator);
1872
1873/**
1874PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1875You can access it here, without fetching it again on your own.
1876*/
1877void vmaGetPhysicalDeviceProperties(
1878 VmaAllocator allocator,
1879 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1880
1881/**
1882PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1883You can access it here, without fetching it again on your own.
1884*/
1885void vmaGetMemoryProperties(
1886 VmaAllocator allocator,
1887 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1888
1889/**
1890\brief Given Memory Type Index, returns Property Flags of this memory type.
1891
1892This is just a convenience function. Same information can be obtained using
1893vmaGetMemoryProperties().
1894*/
1895void vmaGetMemoryTypeProperties(
1896 VmaAllocator allocator,
1897 uint32_t memoryTypeIndex,
1898 VkMemoryPropertyFlags* pFlags);
1899
1900/** \brief Sets index of the current frame.
1901
1902This function must be used if you make allocations with
1903#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1904#VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1905when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1906become lost in the current frame.
1907*/
1908void vmaSetCurrentFrameIndex(
1909 VmaAllocator allocator,
1910 uint32_t frameIndex);
1911
1912/** \brief Calculated statistics of memory usage in entire allocator.
1913*/
1914typedef struct VmaStatInfo
1915{
1916 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1917 uint32_t blockCount;
1918 /// Number of #VmaAllocation allocation objects allocated.
1919 uint32_t allocationCount;
1920 /// Number of free ranges of memory between allocations.
1921 uint32_t unusedRangeCount;
1922 /// Total number of bytes occupied by all allocations.
1923 VkDeviceSize usedBytes;
1924 /// Total number of bytes occupied by unused ranges.
1925 VkDeviceSize unusedBytes;
1926 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1927 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1928} VmaStatInfo;
1929
1930/// General statistics from current state of Allocator.
1931typedef struct VmaStats
1932{
1933 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1934 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1935 VmaStatInfo total;
1936} VmaStats;
1937
1938/// Retrieves statistics from current state of the Allocator.
1939void vmaCalculateStats(
1940 VmaAllocator allocator,
1941 VmaStats* pStats);
1942
1943#define VMA_STATS_STRING_ENABLED 1
1944
1945#if VMA_STATS_STRING_ENABLED
1946
1947/// Builds and returns statistics as string in JSON format.
1948/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1949*/
1950void vmaBuildStatsString(
1951 VmaAllocator allocator,
1952 char** ppStatsString,
1953 VkBool32 detailedMap);
1954
1955void vmaFreeStatsString(
1956 VmaAllocator allocator,
1957 char* pStatsString);
1958
1959#endif // #if VMA_STATS_STRING_ENABLED
1960
1961/** \struct VmaPool
1962\brief Represents custom memory pool
1963
1964Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1965Call function vmaDestroyPool() to destroy it.
1966
1967For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1968*/
1969VK_DEFINE_HANDLE(VmaPool)
1970
1971typedef enum VmaMemoryUsage
1972{
1973 /** No intended memory usage specified.
1974 Use other members of VmaAllocationCreateInfo to specify your requirements.
1975 */
1976 VMA_MEMORY_USAGE_UNKNOWN = 0,
1977 /** Memory will be used on device only, so fast access from the device is preferred.
1978 It usually means device-local GPU (video) memory.
1979 No need to be mappable on host.
1980 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1981
1982 Usage:
1983
1984 - Resources written and read by device, e.g. images used as attachments.
1985 - Resources transferred from host once (immutable) or infrequently and read by
1986 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1987 (constant) buffers, and majority of other types of resources used on GPU.
1988
1989 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1990 In such case, you are free to map it.
1991 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1992 */
1993 VMA_MEMORY_USAGE_GPU_ONLY = 1,
1994 /** Memory will be mappable on host.
1995 It usually means CPU (system) memory.
1996 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1997 CPU access is typically uncached. Writes may be write-combined.
1998 Resources created in this pool may still be accessible to the device, but access to them can be slow.
1999 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2000
2001 Usage: Staging copy of resources used as transfer source.
2002 */
2003 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2004 /**
2005 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2006 CPU access is typically uncached. Writes may be write-combined.
2007
2008 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
2009 */
2010 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2011 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2012 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2013
2014 Usage:
2015
2016 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2017 - 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.
2018 */
2019 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2020 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2021} VmaMemoryUsage;
2022
2023/// Flags to be passed as VmaAllocationCreateInfo::flags.
2024typedef enum VmaAllocationCreateFlagBits {
2025 /** \brief Set this flag if the allocation should have its own memory block.
2026
2027 Use it for special, big resources, like fullscreen images used as attachments.
2028
2029 This flag must also be used for host visible resources that you want to map
2030 simultaneously because otherwise they might end up as regions of the same
2031 `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2032 simultaneously is illegal.
2033
2034 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2035 */
2036 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2037
2038 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2039
2040 If new allocation cannot be placed in any of the existing blocks, allocation
2041 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2042
2043 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2044 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2045
2046 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2047 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2048 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2049
2050 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2051
2052 Is it valid to use this flag for allocation made from memory type that is not
2053 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2054 useful if you need an allocation that is efficient to use on GPU
2055 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2056 support it (e.g. Intel GPU).
2057
2058 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2059 */
2060 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2061 /** Allocation created with this flag can become lost as a result of another
2062 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2063 must check it before use.
2064
2065 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2066 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2067
2068 For details about supporting lost allocations, see Lost Allocations
2069 chapter of User Guide on Main Page.
2070
2071 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2072 */
2073 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2074 /** While creating allocation using this flag, other allocations that were
2075 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2076
2077 For details about supporting lost allocations, see Lost Allocations
2078 chapter of User Guide on Main Page.
2079 */
2080 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2081 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2082 null-terminated string. Instead of copying pointer value, a local copy of the
2083 string is made and stored in allocation's `pUserData`. The string is automatically
2084 freed together with the allocation. It is also used in vmaBuildStatsString().
2085 */
2086 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2087 /** Allocation will be created from upper stack in a double stack pool.
2088
2089 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2090 */
2091 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2092
2093 /** Allocation strategy that chooses smallest possible free range for the
2094 allocation.
2095 */
2096 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2097 /** Allocation strategy that chooses biggest possible free range for the
2098 allocation.
2099 */
2100 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2101 /** Allocation strategy that chooses first suitable free range for the
2102 allocation.
2103
2104 "First" doesn't necessarily means the one with smallest offset in memory,
2105 but rather the one that is easiest and fastest to find.
2106 */
2107 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2108
2109 /** Allocation strategy that tries to minimize memory usage.
2110 */
2111 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2112 /** Allocation strategy that tries to minimize allocation time.
2113 */
2114 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2115 /** Allocation strategy that tries to minimize memory fragmentation.
2116 */
2117 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2118
2119 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2120 */
2121 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2122 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2123 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2124 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2125
2126 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2127} VmaAllocationCreateFlagBits;
2128typedef VkFlags VmaAllocationCreateFlags;
2129
2130typedef struct VmaAllocationCreateInfo
2131{
2132 /// Use #VmaAllocationCreateFlagBits enum.
2133 VmaAllocationCreateFlags flags;
2134 /** \brief Intended usage of memory.
2135
2136 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2137 If `pool` is not null, this member is ignored.
2138 */
2139 VmaMemoryUsage usage;
2140 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2141
2142 Leave 0 if you specify memory requirements in other way. \n
2143 If `pool` is not null, this member is ignored.*/
2144 VkMemoryPropertyFlags requiredFlags;
2145 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2146
2147 Set to 0 if no additional flags are prefered. \n
2148 If `pool` is not null, this member is ignored. */
2149 VkMemoryPropertyFlags preferredFlags;
2150 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2151
2152 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2153 it meets other requirements specified by this structure, with no further
2154 restrictions on memory type index. \n
2155 If `pool` is not null, this member is ignored.
2156 */
2157 uint32_t memoryTypeBits;
2158 /** \brief Pool that this allocation should be created in.
2159
2160 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2161 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2162 */
2163 VmaPool pool;
2164 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2165
2166 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2167 null or pointer to a null-terminated string. The string will be then copied to
2168 internal buffer, so it doesn't need to be valid after allocation call.
2169 */
2170 void* pUserData;
2171} VmaAllocationCreateInfo;
2172
2173/**
2174\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2175
2176This algorithm tries to find a memory type that:
2177
2178- Is allowed by memoryTypeBits.
2179- Contains all the flags from pAllocationCreateInfo->requiredFlags.
2180- Matches intended usage.
2181- Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2182
2183\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2184from this function or any other allocating function probably means that your
2185device doesn't support any memory type with requested features for the specific
2186type of resource you want to use it for. Please check parameters of your
2187resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2188*/
2189VkResult vmaFindMemoryTypeIndex(
2190 VmaAllocator allocator,
2191 uint32_t memoryTypeBits,
2192 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2193 uint32_t* pMemoryTypeIndex);
2194
2195/**
2196\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2197
2198It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2199It internally creates a temporary, dummy buffer that never has memory bound.
2200It is just a convenience function, equivalent to calling:
2201
2202- `vkCreateBuffer`
2203- `vkGetBufferMemoryRequirements`
2204- `vmaFindMemoryTypeIndex`
2205- `vkDestroyBuffer`
2206*/
2207VkResult vmaFindMemoryTypeIndexForBufferInfo(
2208 VmaAllocator allocator,
2209 const VkBufferCreateInfo* pBufferCreateInfo,
2210 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2211 uint32_t* pMemoryTypeIndex);
2212
2213/**
2214\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2215
2216It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2217It internally creates a temporary, dummy image that never has memory bound.
2218It is just a convenience function, equivalent to calling:
2219
2220- `vkCreateImage`
2221- `vkGetImageMemoryRequirements`
2222- `vmaFindMemoryTypeIndex`
2223- `vkDestroyImage`
2224*/
2225VkResult vmaFindMemoryTypeIndexForImageInfo(
2226 VmaAllocator allocator,
2227 const VkImageCreateInfo* pImageCreateInfo,
2228 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2229 uint32_t* pMemoryTypeIndex);
2230
2231/// Flags to be passed as VmaPoolCreateInfo::flags.
2232typedef enum VmaPoolCreateFlagBits {
2233 /** \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.
2234
2235 This is an optional optimization flag.
2236
2237 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2238 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2239 knows exact type of your allocations so it can handle Buffer-Image Granularity
2240 in the optimal way.
2241
2242 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2243 exact type of such allocations is not known, so allocator must be conservative
2244 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2245 (wasted memory). In that case, if you can make sure you always allocate only
2246 buffers and linear images or only optimal images out of this pool, use this flag
2247 to make allocator disregard Buffer-Image Granularity and so make allocations
2248 faster and more optimal.
2249 */
2250 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2251
2252 /** \brief Enables alternative, linear allocation algorithm in this pool.
2253
2254 Specify this flag to enable linear allocation algorithm, which always creates
2255 new allocations after last one and doesn't reuse space from allocations freed in
2256 between. It trades memory consumption for simplified algorithm and data
2257 structure, which has better performance and uses less memory for metadata.
2258
2259 By using this flag, you can achieve behavior of free-at-once, stack,
2260 ring buffer, and double stack. For details, see documentation chapter
2261 \ref linear_algorithm.
2262
2263 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2264
2265 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2266 */
2267 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2268
2269 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2270
2271 It operates on a tree of blocks, each having size that is a power of two and
2272 a half of its parent's size. Comparing to default algorithm, this one provides
2273 faster allocation and deallocation and decreased external fragmentation,
2274 at the expense of more memory wasted (internal fragmentation).
2275
2276 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2277 */
2278 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2279
2280 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2281 */
2282 VMA_POOL_CREATE_ALGORITHM_MASK =
2283 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2284 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2285
2286 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2287} VmaPoolCreateFlagBits;
2288typedef VkFlags VmaPoolCreateFlags;
2289
2290/** \brief Describes parameter of created #VmaPool.
2291*/
2292typedef struct VmaPoolCreateInfo {
2293 /** \brief Vulkan memory type index to allocate this pool from.
2294 */
2295 uint32_t memoryTypeIndex;
2296 /** \brief Use combination of #VmaPoolCreateFlagBits.
2297 */
2298 VmaPoolCreateFlags flags;
2299 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2300
2301 Specify nonzero to set explicit, constant size of memory blocks used by this
2302 pool.
2303
2304 Leave 0 to use default and let the library manage block sizes automatically.
2305 Sizes of particular blocks may vary.
2306 */
2307 VkDeviceSize blockSize;
2308 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2309
2310 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2311 */
2312 size_t minBlockCount;
2313 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2314
2315 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2316
2317 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2318 throughout whole lifetime of this pool.
2319 */
2320 size_t maxBlockCount;
2321 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2322
2323 This value is used only when you make allocations with
2324 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2325 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2326
2327 For example, if you double-buffer your command buffers, so resources used for
2328 rendering in previous frame may still be in use by the GPU at the moment you
2329 allocate resources needed for the current frame, set this value to 1.
2330
2331 If you want to allow any allocations other than used in the current frame to
2332 become lost, set this value to 0.
2333 */
2334 uint32_t frameInUseCount;
2335} VmaPoolCreateInfo;
2336
2337/** \brief Describes parameter of existing #VmaPool.
2338*/
2339typedef struct VmaPoolStats {
2340 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2341 */
2342 VkDeviceSize size;
2343 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2344 */
2345 VkDeviceSize unusedSize;
2346 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2347 */
2348 size_t allocationCount;
2349 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2350 */
2351 size_t unusedRangeCount;
2352 /** \brief Size of the largest continuous free memory region available for new allocation.
2353
2354 Making a new allocation of that size is not guaranteed to succeed because of
2355 possible additional margin required to respect alignment and buffer/image
2356 granularity.
2357 */
2358 VkDeviceSize unusedRangeSizeMax;
2359 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2360 */
2361 size_t blockCount;
2362} VmaPoolStats;
2363
2364/** \brief Allocates Vulkan device memory and creates #VmaPool object.
2365
2366@param allocator Allocator object.
2367@param pCreateInfo Parameters of pool to create.
2368@param[out] pPool Handle to created pool.
2369*/
2370VkResult vmaCreatePool(
2371 VmaAllocator allocator,
2372 const VmaPoolCreateInfo* pCreateInfo,
2373 VmaPool* pPool);
2374
2375/** \brief Destroys #VmaPool object and frees Vulkan device memory.
2376*/
2377void vmaDestroyPool(
2378 VmaAllocator allocator,
2379 VmaPool pool);
2380
2381/** \brief Retrieves statistics of existing #VmaPool object.
2382
2383@param allocator Allocator object.
2384@param pool Pool object.
2385@param[out] pPoolStats Statistics of specified pool.
2386*/
2387void vmaGetPoolStats(
2388 VmaAllocator allocator,
2389 VmaPool pool,
2390 VmaPoolStats* pPoolStats);
2391
2392/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2393
2394@param allocator Allocator object.
2395@param pool Pool.
2396@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2397*/
2398void vmaMakePoolAllocationsLost(
2399 VmaAllocator allocator,
2400 VmaPool pool,
2401 size_t* pLostAllocationCount);
2402
2403/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2404
2405Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2406`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2407`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2408
2409Possible return values:
2410
2411- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2412- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2413- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2414 `VMA_ASSERT` is also fired in that case.
2415- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2416*/
2417VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2418
2419/** \struct VmaAllocation
2420\brief Represents single memory allocation.
2421
2422It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2423plus unique offset.
2424
2425There are multiple ways to create such object.
2426You need to fill structure VmaAllocationCreateInfo.
2427For more information see [Choosing memory type](@ref choosing_memory_type).
2428
2429Although the library provides convenience functions that create Vulkan buffer or image,
2430allocate memory for it and bind them together,
2431binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2432Allocation object can exist without buffer/image bound,
2433binding can be done manually by the user, and destruction of it can be done
2434independently of destruction of the allocation.
2435
2436The object also remembers its size and some other information.
2437To retrieve this information, use function vmaGetAllocationInfo() and inspect
2438returned structure VmaAllocationInfo.
2439
2440Some kinds allocations can be in lost state.
2441For more information, see [Lost allocations](@ref lost_allocations).
2442*/
2443VK_DEFINE_HANDLE(VmaAllocation)
2444
2445/** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2446*/
2447typedef struct VmaAllocationInfo {
2448 /** \brief Memory type index that this allocation was allocated from.
2449
2450 It never changes.
2451 */
2452 uint32_t memoryType;
2453 /** \brief Handle to Vulkan memory object.
2454
2455 Same memory object can be shared by multiple allocations.
2456
2457 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2458
2459 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2460 */
2461 VkDeviceMemory deviceMemory;
2462 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2463
2464 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2465 */
2466 VkDeviceSize offset;
2467 /** \brief Size of this allocation, in bytes.
2468
2469 It never changes, unless allocation is lost.
2470 */
2471 VkDeviceSize size;
2472 /** \brief Pointer to the beginning of this allocation as mapped data.
2473
2474 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2475 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2476
2477 It can change after call to vmaMapMemory(), vmaUnmapMemory().
2478 It can also change after call to vmaDefragment() if this allocation is passed to the function.
2479 */
2480 void* pMappedData;
2481 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2482
2483 It can change after call to vmaSetAllocationUserData() for this allocation.
2484 */
2485 void* pUserData;
2486} VmaAllocationInfo;
2487
2488/** \brief General purpose memory allocation.
2489
2490@param[out] pAllocation Handle to allocated memory.
2491@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2492
2493You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2494
2495It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2496vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2497*/
2498VkResult vmaAllocateMemory(
2499 VmaAllocator allocator,
2500 const VkMemoryRequirements* pVkMemoryRequirements,
2501 const VmaAllocationCreateInfo* pCreateInfo,
2502 VmaAllocation* pAllocation,
2503 VmaAllocationInfo* pAllocationInfo);
2504
2505/** \brief General purpose memory allocation for multiple allocation objects at once.
2506
2507@param allocator Allocator object.
2508@param pVkMemoryRequirements Memory requirements for each allocation.
2509@param pCreateInfo Creation parameters for each alloction.
2510@param allocationCount Number of allocations to make.
2511@param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2512@param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2513
2514You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2515
2516Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2517It is just a general purpose allocation function able to make multiple allocations at once.
2518It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2519
2520All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2521If any allocation fails, all allocations already made within this function call are also freed, so that when
2522returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2523*/
2524VkResult vmaAllocateMemoryPages(
2525 VmaAllocator allocator,
2526 const VkMemoryRequirements* pVkMemoryRequirements,
2527 const VmaAllocationCreateInfo* pCreateInfo,
2528 size_t allocationCount,
2529 VmaAllocation* pAllocations,
2530 VmaAllocationInfo* pAllocationInfo);
2531
2532/**
2533@param[out] pAllocation Handle to allocated memory.
2534@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2535
2536You should free the memory using vmaFreeMemory().
2537*/
2538VkResult vmaAllocateMemoryForBuffer(
2539 VmaAllocator allocator,
2540 VkBuffer buffer,
2541 const VmaAllocationCreateInfo* pCreateInfo,
2542 VmaAllocation* pAllocation,
2543 VmaAllocationInfo* pAllocationInfo);
2544
2545/// Function similar to vmaAllocateMemoryForBuffer().
2546VkResult vmaAllocateMemoryForImage(
2547 VmaAllocator allocator,
2548 VkImage image,
2549 const VmaAllocationCreateInfo* pCreateInfo,
2550 VmaAllocation* pAllocation,
2551 VmaAllocationInfo* pAllocationInfo);
2552
2553/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2554
2555Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2556*/
2557void vmaFreeMemory(
2558 VmaAllocator allocator,
2559 VmaAllocation allocation);
2560
2561/** \brief Frees memory and destroys multiple allocations.
2562
2563Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2564It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2565vmaAllocateMemoryPages() and other functions.
2566It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2567
2568Allocations in `pAllocations` array can come from any memory pools and types.
2569Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2570*/
2571void vmaFreeMemoryPages(
2572 VmaAllocator allocator,
2573 size_t allocationCount,
2574 VmaAllocation* pAllocations);
2575
2576/** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2577
2578Tries to change allocation's size without moving or reallocating it.
2579You can both shrink and grow allocation size.
2580When growing, it succeeds only when the allocation belongs to a memory block with enough
2581free space after it.
2582
2583Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2584Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2585
2586After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2587All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2588
2589- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2590- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2591- Resizing dedicated allocations, as well as allocations created in pools that use linear
2592 or buddy algorithm, is not supported.
2593 The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2594 Support may be added in the future.
2595*/
2596VkResult vmaResizeAllocation(
2597 VmaAllocator allocator,
2598 VmaAllocation allocation,
2599 VkDeviceSize newSize);
2600
2601/** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2602
2603Current paramters of given allocation are returned in `pAllocationInfo`.
2604
2605This function also atomically "touches" allocation - marks it as used in current frame,
2606just like vmaTouchAllocation().
2607If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2608
2609Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2610you can avoid calling it too often.
2611
2612- You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2613 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2614 (e.g. due to defragmentation or allocation becoming lost).
2615- If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2616*/
2617void vmaGetAllocationInfo(
2618 VmaAllocator allocator,
2619 VmaAllocation allocation,
2620 VmaAllocationInfo* pAllocationInfo);
2621
2622/** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2623
2624If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2625this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2626It then also atomically "touches" the allocation - marks it as used in current frame,
2627so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2628
2629If the allocation is in lost state, the function returns `VK_FALSE`.
2630Memory of such allocation, as well as buffer or image bound to it, should not be used.
2631Lost allocation and the buffer/image still need to be destroyed.
2632
2633If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2634this function always returns `VK_TRUE`.
2635*/
2636VkBool32 vmaTouchAllocation(
2637 VmaAllocator allocator,
2638 VmaAllocation allocation);
2639
2640/** \brief Sets pUserData in given allocation to new value.
2641
2642If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2643pUserData must be either null, or pointer to a null-terminated string. The function
2644makes local copy of the string and sets it as allocation's `pUserData`. String
2645passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2646you can free it after this call. String previously pointed by allocation's
2647pUserData is freed from memory.
2648
2649If the flag was not used, the value of pointer `pUserData` is just copied to
2650allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2651as a pointer, ordinal number or some handle to you own data.
2652*/
2653void vmaSetAllocationUserData(
2654 VmaAllocator allocator,
2655 VmaAllocation allocation,
2656 void* pUserData);
2657
2658/** \brief Creates new allocation that is in lost state from the beginning.
2659
2660It can be useful if you need a dummy, non-null allocation.
2661
2662You still need to destroy created object using vmaFreeMemory().
2663
2664Returned allocation is not tied to any specific memory pool or memory type and
2665not bound to any image or buffer. It has size = 0. It cannot be turned into
2666a real, non-empty allocation.
2667*/
2668void vmaCreateLostAllocation(
2669 VmaAllocator allocator,
2670 VmaAllocation* pAllocation);
2671
2672/** \brief Maps memory represented by given allocation and returns pointer to it.
2673
2674Maps memory represented by given allocation to make it accessible to CPU code.
2675When succeeded, `*ppData` contains pointer to first byte of this memory.
2676If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2677correctly offseted to the beginning of region assigned to this particular
2678allocation.
2679
2680Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2681function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2682multiple times simultaneously, it is safe to call this function on allocations
2683assigned to the same memory block. Actual Vulkan memory will be mapped on first
2684mapping and unmapped on last unmapping.
2685
2686If the function succeeded, you must call vmaUnmapMemory() to unmap the
2687allocation when mapping is no longer needed or before freeing the allocation, at
2688the latest.
2689
2690It also safe to call this function multiple times on the same allocation. You
2691must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2692
2693It is also safe to call this function on allocation created with
2694#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2695You must still call vmaUnmapMemory() same number of times as you called
2696vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2697"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2698
2699This function fails when used on allocation made in memory type that is not
2700`HOST_VISIBLE`.
2701
2702This function always fails when called for allocation that was created with
2703#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2704mapped.
2705*/
2706VkResult vmaMapMemory(
2707 VmaAllocator allocator,
2708 VmaAllocation allocation,
2709 void** ppData);
2710
2711/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2712
2713For details, see description of vmaMapMemory().
2714*/
2715void vmaUnmapMemory(
2716 VmaAllocator allocator,
2717 VmaAllocation allocation);
2718
2719/** \brief Flushes memory of given allocation.
2720
2721Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2722
2723- `offset` must be relative to the beginning of allocation.
2724- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2725- `offset` and `size` don't have to be aligned.
2726 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2727- If `size` is 0, this call is ignored.
2728- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2729 this call is ignored.
2730*/
2731void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2732
2733/** \brief Invalidates memory of given allocation.
2734
2735Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2736
2737- `offset` must be relative to the beginning of allocation.
2738- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2739- `offset` and `size` don't have to be aligned.
2740 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2741- If `size` is 0, this call is ignored.
2742- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2743 this call is ignored.
2744*/
2745void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2746
2747/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2748
2749@param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2750
2751Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2752`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2753`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2754
2755Possible return values:
2756
2757- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2758- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2759- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2760 `VMA_ASSERT` is also fired in that case.
2761- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2762*/
2763VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2764
2765/** \struct VmaDefragmentationContext
2766\brief Represents Opaque object that represents started defragmentation process.
2767
2768Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2769Call function vmaDefragmentationEnd() to destroy it.
2770*/
2771VK_DEFINE_HANDLE(VmaDefragmentationContext)
2772
2773/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2774typedef enum VmaDefragmentationFlagBits {
2775 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2776} VmaDefragmentationFlagBits;
2777typedef VkFlags VmaDefragmentationFlags;
2778
2779/** \brief Parameters for defragmentation.
2780
2781To be used with function vmaDefragmentationBegin().
2782*/
2783typedef struct VmaDefragmentationInfo2 {
2784 /** \brief Reserved for future use. Should be 0.
2785 */
2786 VmaDefragmentationFlags flags;
2787 /** \brief Number of allocations in `pAllocations` array.
2788 */
2789 uint32_t allocationCount;
2790 /** \brief Pointer to array of allocations that can be defragmented.
2791
2792 The array should have `allocationCount` elements.
2793 The array should not contain nulls.
2794 Elements in the array should be unique - same allocation cannot occur twice.
2795 It is safe to pass allocations that are in the lost state - they are ignored.
2796 All allocations not present in this array are considered non-moveable during this defragmentation.
2797 */
2798 VmaAllocation* pAllocations;
2799 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2800
2801 The array should have `allocationCount` elements.
2802 You can pass null if you are not interested in this information.
2803 */
2804 VkBool32* pAllocationsChanged;
2805 /** \brief Numer of pools in `pPools` array.
2806 */
2807 uint32_t poolCount;
2808 /** \brief Either null or pointer to array of pools to be defragmented.
2809
2810 All the allocations in the specified pools can be moved during defragmentation
2811 and there is no way to check if they were really moved as in `pAllocationsChanged`,
2812 so you must query all the allocations in all these pools for new `VkDeviceMemory`
2813 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2814 and images bound to them.
2815
2816 The array should have `poolCount` elements.
2817 The array should not contain nulls.
2818 Elements in the array should be unique - same pool cannot occur twice.
2819
2820 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2821 It might be more efficient.
2822 */
2823 VmaPool* pPools;
2824 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2825
2826 `VK_WHOLE_SIZE` means no limit.
2827 */
2828 VkDeviceSize maxCpuBytesToMove;
2829 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2830
2831 `UINT32_MAX` means no limit.
2832 */
2833 uint32_t maxCpuAllocationsToMove;
2834 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2835
2836 `VK_WHOLE_SIZE` means no limit.
2837 */
2838 VkDeviceSize maxGpuBytesToMove;
2839 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2840
2841 `UINT32_MAX` means no limit.
2842 */
2843 uint32_t maxGpuAllocationsToMove;
2844 /** \brief Optional. Command buffer where GPU copy commands will be posted.
2845
2846 If not null, it must be a valid command buffer handle that supports Transfer queue type.
2847 It must be in the recording state and outside of a render pass instance.
2848 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2849
2850 Passing null means that only CPU defragmentation will be performed.
2851 */
2852 VkCommandBuffer commandBuffer;
2853} VmaDefragmentationInfo2;
2854
2855/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2856
2857\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2858*/
2859typedef struct VmaDefragmentationInfo {
2860 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2861
2862 Default is `VK_WHOLE_SIZE`, which means no limit.
2863 */
2864 VkDeviceSize maxBytesToMove;
2865 /** \brief Maximum number of allocations that can be moved to different place.
2866
2867 Default is `UINT32_MAX`, which means no limit.
2868 */
2869 uint32_t maxAllocationsToMove;
2870} VmaDefragmentationInfo;
2871
2872/** \brief Statistics returned by function vmaDefragment(). */
2873typedef struct VmaDefragmentationStats {
2874 /// Total number of bytes that have been copied while moving allocations to different places.
2875 VkDeviceSize bytesMoved;
2876 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2877 VkDeviceSize bytesFreed;
2878 /// Number of allocations that have been moved to different places.
2879 uint32_t allocationsMoved;
2880 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2881 uint32_t deviceMemoryBlocksFreed;
2882} VmaDefragmentationStats;
2883
2884/** \brief Begins defragmentation process.
2885
2886@param allocator Allocator object.
2887@param pInfo Structure filled with parameters of defragmentation.
2888@param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2889@param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2890@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.
2891
2892Use this function instead of old, deprecated vmaDefragment().
2893
2894Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2895
2896- You should not use any of allocations passed as `pInfo->pAllocations` or
2897 any allocations that belong to pools passed as `pInfo->pPools`,
2898 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2899 their data.
2900- Some mutexes protecting internal data structures may be locked, so trying to
2901 make or free any allocations, bind buffers or images, map memory, or launch
2902 another simultaneous defragmentation in between may cause stall (when done on
2903 another thread) or deadlock (when done on the same thread), unless you are
2904 100% sure that defragmented allocations are in different pools.
2905- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2906 They become valid after call to vmaDefragmentationEnd().
2907- If `pInfo->commandBuffer` is not null, you must submit that command buffer
2908 and make sure it finished execution before calling vmaDefragmentationEnd().
2909*/
2910VkResult vmaDefragmentationBegin(
2911 VmaAllocator allocator,
2912 const VmaDefragmentationInfo2* pInfo,
2913 VmaDefragmentationStats* pStats,
2914 VmaDefragmentationContext *pContext);
2915
2916/** \brief Ends defragmentation process.
2917
2918Use this function to finish defragmentation started by vmaDefragmentationBegin().
2919It is safe to pass `context == null`. The function then does nothing.
2920*/
2921VkResult vmaDefragmentationEnd(
2922 VmaAllocator allocator,
2923 VmaDefragmentationContext context);
2924
2925/** \brief Deprecated. Compacts memory by moving allocations.
2926
2927@param pAllocations Array of allocations that can be moved during this compation.
2928@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2929@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.
2930@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2931@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2932@return `VK_SUCCESS` if completed, negative error code in case of error.
2933
2934\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2935
2936This function works by moving allocations to different places (different
2937`VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2938usage. Only allocations that are in `pAllocations` array can be moved. All other
2939allocations are considered nonmovable in this call. Basic rules:
2940
2941- Only allocations made in memory types that have
2942 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2943 flags can be compacted. You may pass other allocations but it makes no sense -
2944 these will never be moved.
2945- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2946 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2947 passed to this function that come from such pools are ignored.
2948- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2949 created as dedicated allocations for any other reason are also ignored.
2950- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2951 flag can be compacted. If not persistently mapped, memory will be mapped
2952 temporarily inside this function if needed.
2953- You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2954
2955The function also frees empty `VkDeviceMemory` blocks.
2956
2957Warning: This function may be time-consuming, so you shouldn't call it too often
2958(like after every resource creation/destruction).
2959You can call it on special occasions (like when reloading a game level or
2960when you just destroyed a lot of objects). Calling it every frame may be OK, but
2961you should measure that on your platform.
2962
2963For more information, see [Defragmentation](@ref defragmentation) chapter.
2964*/
2965VkResult vmaDefragment(
2966 VmaAllocator allocator,
2967 VmaAllocation* pAllocations,
2968 size_t allocationCount,
2969 VkBool32* pAllocationsChanged,
2970 const VmaDefragmentationInfo *pDefragmentationInfo,
2971 VmaDefragmentationStats* pDefragmentationStats);
2972
2973/** \brief Binds buffer to allocation.
2974
2975Binds specified buffer to region of memory represented by specified allocation.
2976Gets `VkDeviceMemory` handle and offset from the allocation.
2977If you want to create a buffer, allocate memory for it and bind them together separately,
2978you should use this function for binding instead of standard `vkBindBufferMemory()`,
2979because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2980allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2981(which is illegal in Vulkan).
2982
2983It is recommended to use function vmaCreateBuffer() instead of this one.
2984*/
2985VkResult vmaBindBufferMemory(
2986 VmaAllocator allocator,
2987 VmaAllocation allocation,
2988 VkBuffer buffer);
2989
2990/** \brief Binds image to allocation.
2991
2992Binds specified image to region of memory represented by specified allocation.
2993Gets `VkDeviceMemory` handle and offset from the allocation.
2994If you want to create an image, allocate memory for it and bind them together separately,
2995you should use this function for binding instead of standard `vkBindImageMemory()`,
2996because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2997allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2998(which is illegal in Vulkan).
2999
3000It is recommended to use function vmaCreateImage() instead of this one.
3001*/
3002VkResult vmaBindImageMemory(
3003 VmaAllocator allocator,
3004 VmaAllocation allocation,
3005 VkImage image);
3006
3007/**
3008@param[out] pBuffer Buffer that was created.
3009@param[out] pAllocation Allocation that was created.
3010@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3011
3012This function automatically:
3013
3014-# Creates buffer.
3015-# Allocates appropriate memory for it.
3016-# Binds the buffer with the memory.
3017
3018If any of these operations fail, buffer and allocation are not created,
3019returned value is negative error code, *pBuffer and *pAllocation are null.
3020
3021If the function succeeded, you must destroy both buffer and allocation when you
3022no longer need them using either convenience function vmaDestroyBuffer() or
3023separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3024
3025If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3026VK_KHR_dedicated_allocation extension is used internally to query driver whether
3027it requires or prefers the new buffer to have dedicated allocation. If yes,
3028and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3029and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3030allocation for this buffer, just like when using
3031VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3032*/
3033VkResult vmaCreateBuffer(
3034 VmaAllocator allocator,
3035 const VkBufferCreateInfo* pBufferCreateInfo,
3036 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3037 VkBuffer* pBuffer,
3038 VmaAllocation* pAllocation,
3039 VmaAllocationInfo* pAllocationInfo);
3040
3041/** \brief Destroys Vulkan buffer and frees allocated memory.
3042
3043This is just a convenience function equivalent to:
3044
3045\code
3046vkDestroyBuffer(device, buffer, allocationCallbacks);
3047vmaFreeMemory(allocator, allocation);
3048\endcode
3049
3050It it safe to pass null as buffer and/or allocation.
3051*/
3052void vmaDestroyBuffer(
3053 VmaAllocator allocator,
3054 VkBuffer buffer,
3055 VmaAllocation allocation);
3056
3057/// Function similar to vmaCreateBuffer().
3058VkResult vmaCreateImage(
3059 VmaAllocator allocator,
3060 const VkImageCreateInfo* pImageCreateInfo,
3061 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3062 VkImage* pImage,
3063 VmaAllocation* pAllocation,
3064 VmaAllocationInfo* pAllocationInfo);
3065
3066/** \brief Destroys Vulkan image and frees allocated memory.
3067
3068This is just a convenience function equivalent to:
3069
3070\code
3071vkDestroyImage(device, image, allocationCallbacks);
3072vmaFreeMemory(allocator, allocation);
3073\endcode
3074
3075It it safe to pass null as image and/or allocation.
3076*/
3077void vmaDestroyImage(
3078 VmaAllocator allocator,
3079 VkImage image,
3080 VmaAllocation allocation);
3081
3082#ifdef __cplusplus
3083}
3084#endif
3085
3086#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3087
3088// For Visual Studio IntelliSense.
3089#if defined(__cplusplus) && defined(__INTELLISENSE__)
3090#define VMA_IMPLEMENTATION
3091#endif
3092
3093#ifdef VMA_IMPLEMENTATION
3094#undef VMA_IMPLEMENTATION
3095
3096#include <cstdint>
3097#include <cstdlib>
3098#include <cstring>
3099
3100/*******************************************************************************
3101CONFIGURATION SECTION
3102
3103Define some of these macros before each #include of this header or change them
3104here if you need other then default behavior depending on your environment.
3105*/
3106
3107/*
3108Define this macro to 1 to make the library fetch pointers to Vulkan functions
3109internally, like:
3110
3111 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3112
3113Define to 0 if you are going to provide you own pointers to Vulkan functions via
3114VmaAllocatorCreateInfo::pVulkanFunctions.
3115*/
3116#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3117#define VMA_STATIC_VULKAN_FUNCTIONS 1
3118#endif
3119
3120// Define this macro to 1 to make the library use STL containers instead of its own implementation.
3121//#define VMA_USE_STL_CONTAINERS 1
3122
3123/* Set this macro to 1 to make the library including and using STL containers:
3124std::pair, std::vector, std::list, std::unordered_map.
3125
3126Set it to 0 or undefined to make the library using its own implementation of
3127the containers.
3128*/
3129#if VMA_USE_STL_CONTAINERS
3130 #define VMA_USE_STL_VECTOR 1
3131 #define VMA_USE_STL_UNORDERED_MAP 1
3132 #define VMA_USE_STL_LIST 1
3133#endif
3134
3135#ifndef VMA_USE_STL_SHARED_MUTEX
3136 // Minimum Visual Studio 2015 Update 2
3137 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
3138 #define VMA_USE_STL_SHARED_MUTEX 1
3139 #endif
3140#endif
3141
3142#if VMA_USE_STL_VECTOR
3143 #include <vector>
3144#endif
3145
3146#if VMA_USE_STL_UNORDERED_MAP
3147 #include <unordered_map>
3148#endif
3149
3150#if VMA_USE_STL_LIST
3151 #include <list>
3152#endif
3153
3154/*
3155Following headers are used in this CONFIGURATION section only, so feel free to
3156remove them if not needed.
3157*/
3158#include <cassert> // for assert
3159#include <algorithm> // for min, max
3160#include <mutex>
3161#include <atomic> // for std::atomic
3162
3163#ifndef VMA_NULL
3164 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3165 #define VMA_NULL nullptr
3166#endif
3167
3168#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3169#include <cstdlib>
3170void *aligned_alloc(size_t alignment, size_t size)
3171{
3172 // alignment must be >= sizeof(void*)
3173 if(alignment < sizeof(void*))
3174 {
3175 alignment = sizeof(void*);
3176 }
3177
3178 return memalign(alignment, size);
3179}
3180#elif defined(__APPLE__) || defined(__ANDROID__)
François Bertel3aaaa812019-04-19 11:11:50 -04003181# define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3182#elif defined(__GNU_LIBRARY__)
3183# if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 16)
3184// aligned_alloc() is defined in glibc only for version >= 2.16
3185# define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3186# endif
3187#endif
3188
3189#ifdef ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06003190#include <cstdlib>
3191void *aligned_alloc(size_t alignment, size_t size)
3192{
3193 // alignment must be >= sizeof(void*)
3194 if(alignment < sizeof(void*))
3195 {
3196 alignment = sizeof(void*);
3197 }
3198
3199 void *pointer;
3200 if(posix_memalign(&pointer, alignment, size) == 0)
3201 return pointer;
3202 return VMA_NULL;
3203}
3204#endif
3205
3206// If your compiler is not compatible with C++11 and definition of
3207// aligned_alloc() function is missing, uncommeting following line may help:
3208
3209//#include <malloc.h>
3210
3211// Normal assert to check for programmer's errors, especially in Debug configuration.
3212#ifndef VMA_ASSERT
3213 #ifdef _DEBUG
3214 #define VMA_ASSERT(expr) assert(expr)
3215 #else
3216 #define VMA_ASSERT(expr)
3217 #endif
3218#endif
3219
3220// Assert that will be called very often, like inside data structures e.g. operator[].
3221// Making it non-empty can make program slow.
3222#ifndef VMA_HEAVY_ASSERT
3223 #ifdef _DEBUG
3224 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3225 #else
3226 #define VMA_HEAVY_ASSERT(expr)
3227 #endif
3228#endif
3229
3230#ifndef VMA_ALIGN_OF
3231 #define VMA_ALIGN_OF(type) (__alignof(type))
3232#endif
3233
3234#ifndef VMA_SYSTEM_ALIGNED_MALLOC
3235 #if defined(_WIN32)
3236 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
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(outStr, strLen, "%u", static_cast<unsigned int>(num));
3281 }
3282 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3283 {
3284 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3285 }
3286 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3287 {
3288 snprintf(outStr, strLen, "%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)
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
Tony-LunarG390319b2019-03-18 15:54:16 -06003448#if defined(__GNUC__)
3449#pragma GCC diagnostic push
3450#pragma GCC diagnostic ignored "-Wtype-limits"
3451#pragma GCC diagnostic ignored "-Wunused-variable"
3452#if defined(ANDROID)
3453#pragma GCC diagnostic ignored "-Wunused-private-field"
3454#endif
3455#endif
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06003456static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3457
3458static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3459 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3460
3461// Returns number of bits set to 1 in (v).
3462static inline uint32_t VmaCountBitsSet(uint32_t v)
3463{
3464 uint32_t c = v - ((v >> 1) & 0x55555555);
3465 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3466 c = ((c >> 4) + c) & 0x0F0F0F0F;
3467 c = ((c >> 8) + c) & 0x00FF00FF;
3468 c = ((c >> 16) + c) & 0x0000FFFF;
3469 return c;
3470}
3471
3472// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3473// Use types like uint32_t, uint64_t as T.
3474template <typename T>
3475static inline T VmaAlignUp(T val, T align)
3476{
3477 return (val + align - 1) / align * align;
3478}
3479// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3480// Use types like uint32_t, uint64_t as T.
3481template <typename T>
3482static inline T VmaAlignDown(T val, T align)
3483{
3484 return val / align * align;
3485}
3486
3487// Division with mathematical rounding to nearest number.
3488template <typename T>
3489static inline T VmaRoundDiv(T x, T y)
3490{
3491 return (x + (y / (T)2)) / y;
3492}
3493
3494/*
3495Returns true if given number is a power of two.
3496T must be unsigned integer number or signed integer but always nonnegative.
3497For 0 returns true.
3498*/
3499template <typename T>
3500inline bool VmaIsPow2(T x)
3501{
3502 return (x & (x-1)) == 0;
3503}
3504
3505// Returns smallest power of 2 greater or equal to v.
3506static inline uint32_t VmaNextPow2(uint32_t v)
3507{
3508 v--;
3509 v |= v >> 1;
3510 v |= v >> 2;
3511 v |= v >> 4;
3512 v |= v >> 8;
3513 v |= v >> 16;
3514 v++;
3515 return v;
3516}
3517static inline uint64_t VmaNextPow2(uint64_t v)
3518{
3519 v--;
3520 v |= v >> 1;
3521 v |= v >> 2;
3522 v |= v >> 4;
3523 v |= v >> 8;
3524 v |= v >> 16;
3525 v |= v >> 32;
3526 v++;
3527 return v;
3528}
3529
3530// Returns largest power of 2 less or equal to v.
3531static inline uint32_t VmaPrevPow2(uint32_t v)
3532{
3533 v |= v >> 1;
3534 v |= v >> 2;
3535 v |= v >> 4;
3536 v |= v >> 8;
3537 v |= v >> 16;
3538 v = v ^ (v >> 1);
3539 return v;
3540}
3541static inline uint64_t VmaPrevPow2(uint64_t v)
3542{
3543 v |= v >> 1;
3544 v |= v >> 2;
3545 v |= v >> 4;
3546 v |= v >> 8;
3547 v |= v >> 16;
3548 v |= v >> 32;
3549 v = v ^ (v >> 1);
3550 return v;
3551}
3552
3553static inline bool VmaStrIsEmpty(const char* pStr)
3554{
3555 return pStr == VMA_NULL || *pStr == '\0';
3556}
3557
3558static const char* VmaAlgorithmToStr(uint32_t algorithm)
3559{
3560 switch(algorithm)
3561 {
3562 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3563 return "Linear";
3564 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3565 return "Buddy";
3566 case 0:
3567 return "Default";
3568 default:
3569 VMA_ASSERT(0);
3570 return "";
3571 }
3572}
3573
3574#ifndef VMA_SORT
3575
3576template<typename Iterator, typename Compare>
3577Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3578{
3579 Iterator centerValue = end; --centerValue;
3580 Iterator insertIndex = beg;
3581 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3582 {
3583 if(cmp(*memTypeIndex, *centerValue))
3584 {
3585 if(insertIndex != memTypeIndex)
3586 {
3587 VMA_SWAP(*memTypeIndex, *insertIndex);
3588 }
3589 ++insertIndex;
3590 }
3591 }
3592 if(insertIndex != centerValue)
3593 {
3594 VMA_SWAP(*insertIndex, *centerValue);
3595 }
3596 return insertIndex;
3597}
3598
3599template<typename Iterator, typename Compare>
3600void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3601{
3602 if(beg < end)
3603 {
3604 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3605 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3606 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3607 }
3608}
3609
3610#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3611
3612#endif // #ifndef VMA_SORT
3613
3614/*
3615Returns true if two memory blocks occupy overlapping pages.
3616ResourceA must be in less memory offset than ResourceB.
3617
3618Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3619chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3620*/
3621static inline bool VmaBlocksOnSamePage(
3622 VkDeviceSize resourceAOffset,
3623 VkDeviceSize resourceASize,
3624 VkDeviceSize resourceBOffset,
3625 VkDeviceSize pageSize)
3626{
3627 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3628 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3629 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3630 VkDeviceSize resourceBStart = resourceBOffset;
3631 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3632 return resourceAEndPage == resourceBStartPage;
3633}
3634
3635enum VmaSuballocationType
3636{
3637 VMA_SUBALLOCATION_TYPE_FREE = 0,
3638 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3639 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3640 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3641 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3642 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3643 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3644};
3645
3646/*
3647Returns true if given suballocation types could conflict and must respect
3648VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3649or linear image and another one is optimal image. If type is unknown, behave
3650conservatively.
3651*/
3652static inline bool VmaIsBufferImageGranularityConflict(
3653 VmaSuballocationType suballocType1,
3654 VmaSuballocationType suballocType2)
3655{
3656 if(suballocType1 > suballocType2)
3657 {
3658 VMA_SWAP(suballocType1, suballocType2);
3659 }
3660
3661 switch(suballocType1)
3662 {
3663 case VMA_SUBALLOCATION_TYPE_FREE:
3664 return false;
3665 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3666 return true;
3667 case VMA_SUBALLOCATION_TYPE_BUFFER:
3668 return
3669 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3670 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3671 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3672 return
3673 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3674 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3675 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3676 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3677 return
3678 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3679 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3680 return false;
3681 default:
3682 VMA_ASSERT(0);
3683 return true;
3684 }
3685}
3686
3687static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3688{
3689 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3690 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3691 for(size_t i = 0; i < numberCount; ++i, ++pDst)
3692 {
3693 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3694 }
3695}
3696
3697static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3698{
3699 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3700 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3701 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3702 {
3703 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3704 {
3705 return false;
3706 }
3707 }
3708 return true;
3709}
3710
3711// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3712struct VmaMutexLock
3713{
3714 VMA_CLASS_NO_COPY(VmaMutexLock)
3715public:
3716 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3717 m_pMutex(useMutex ? &mutex : VMA_NULL)
3718 { if(m_pMutex) { m_pMutex->Lock(); } }
3719 ~VmaMutexLock()
3720 { if(m_pMutex) { m_pMutex->Unlock(); } }
3721private:
3722 VMA_MUTEX* m_pMutex;
3723};
3724
3725// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3726struct VmaMutexLockRead
3727{
3728 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3729public:
3730 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3731 m_pMutex(useMutex ? &mutex : VMA_NULL)
3732 { if(m_pMutex) { m_pMutex->LockRead(); } }
3733 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3734private:
3735 VMA_RW_MUTEX* m_pMutex;
3736};
3737
3738// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3739struct VmaMutexLockWrite
3740{
3741 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3742public:
3743 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3744 m_pMutex(useMutex ? &mutex : VMA_NULL)
3745 { if(m_pMutex) { m_pMutex->LockWrite(); } }
3746 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3747private:
3748 VMA_RW_MUTEX* m_pMutex;
3749};
3750
3751#if VMA_DEBUG_GLOBAL_MUTEX
3752 static VMA_MUTEX gDebugGlobalMutex;
3753 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3754#else
3755 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3756#endif
3757
3758// Minimum size of a free suballocation to register it in the free suballocation collection.
3759static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3760
3761/*
3762Performs binary search and returns iterator to first element that is greater or
3763equal to (key), according to comparison (cmp).
3764
3765Cmp should return true if first argument is less than second argument.
3766
3767Returned value is the found element, if present in the collection or place where
3768new element with value (key) should be inserted.
3769*/
3770template <typename CmpLess, typename IterT, typename KeyT>
3771static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3772{
3773 size_t down = 0, up = (end - beg);
3774 while(down < up)
3775 {
3776 const size_t mid = (down + up) / 2;
3777 if(cmp(*(beg+mid), key))
3778 {
3779 down = mid + 1;
3780 }
3781 else
3782 {
3783 up = mid;
3784 }
3785 }
3786 return beg + down;
3787}
3788
3789/*
3790Returns true if all pointers in the array are not-null and unique.
3791Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3792T must be pointer type, e.g. VmaAllocation, VmaPool.
3793*/
3794template<typename T>
3795static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3796{
3797 for(uint32_t i = 0; i < count; ++i)
3798 {
3799 const T iPtr = arr[i];
3800 if(iPtr == VMA_NULL)
3801 {
3802 return false;
3803 }
3804 for(uint32_t j = i + 1; j < count; ++j)
3805 {
3806 if(iPtr == arr[j])
3807 {
3808 return false;
3809 }
3810 }
3811 }
3812 return true;
3813}
3814
3815////////////////////////////////////////////////////////////////////////////////
3816// Memory allocation
3817
3818static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3819{
3820 if((pAllocationCallbacks != VMA_NULL) &&
3821 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3822 {
3823 return (*pAllocationCallbacks->pfnAllocation)(
3824 pAllocationCallbacks->pUserData,
3825 size,
3826 alignment,
3827 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3828 }
3829 else
3830 {
3831 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3832 }
3833}
3834
3835static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3836{
3837 if((pAllocationCallbacks != VMA_NULL) &&
3838 (pAllocationCallbacks->pfnFree != VMA_NULL))
3839 {
3840 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3841 }
3842 else
3843 {
3844 VMA_SYSTEM_FREE(ptr);
3845 }
3846}
3847
3848template<typename T>
3849static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3850{
3851 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3852}
3853
3854template<typename T>
3855static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3856{
3857 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3858}
3859
3860#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3861
3862#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3863
3864template<typename T>
3865static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3866{
3867 ptr->~T();
3868 VmaFree(pAllocationCallbacks, ptr);
3869}
3870
3871template<typename T>
3872static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3873{
3874 if(ptr != VMA_NULL)
3875 {
3876 for(size_t i = count; i--; )
3877 {
3878 ptr[i].~T();
3879 }
3880 VmaFree(pAllocationCallbacks, ptr);
3881 }
3882}
3883
3884// STL-compatible allocator.
3885template<typename T>
3886class VmaStlAllocator
3887{
3888public:
3889 const VkAllocationCallbacks* const m_pCallbacks;
3890 typedef T value_type;
3891
3892 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
3893 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3894
3895 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
3896 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3897
3898 template<typename U>
3899 bool operator==(const VmaStlAllocator<U>& rhs) const
3900 {
3901 return m_pCallbacks == rhs.m_pCallbacks;
3902 }
3903 template<typename U>
3904 bool operator!=(const VmaStlAllocator<U>& rhs) const
3905 {
3906 return m_pCallbacks != rhs.m_pCallbacks;
3907 }
3908
3909 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3910};
3911
3912#if VMA_USE_STL_VECTOR
3913
3914#define VmaVector std::vector
3915
3916template<typename T, typename allocatorT>
3917static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3918{
3919 vec.insert(vec.begin() + index, item);
3920}
3921
3922template<typename T, typename allocatorT>
3923static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3924{
3925 vec.erase(vec.begin() + index);
3926}
3927
3928#else // #if VMA_USE_STL_VECTOR
3929
3930/* Class with interface compatible with subset of std::vector.
3931T must be POD because constructors and destructors are not called and memcpy is
3932used for these objects. */
3933template<typename T, typename AllocatorT>
3934class VmaVector
3935{
3936public:
3937 typedef T value_type;
3938
3939 VmaVector(const AllocatorT& allocator) :
3940 m_Allocator(allocator),
3941 m_pArray(VMA_NULL),
3942 m_Count(0),
3943 m_Capacity(0)
3944 {
3945 }
3946
3947 VmaVector(size_t count, const AllocatorT& allocator) :
3948 m_Allocator(allocator),
3949 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3950 m_Count(count),
3951 m_Capacity(count)
3952 {
3953 }
3954
3955 VmaVector(const VmaVector<T, AllocatorT>& src) :
3956 m_Allocator(src.m_Allocator),
3957 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3958 m_Count(src.m_Count),
3959 m_Capacity(src.m_Count)
3960 {
3961 if(m_Count != 0)
3962 {
3963 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3964 }
3965 }
3966
3967 ~VmaVector()
3968 {
3969 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3970 }
3971
3972 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3973 {
3974 if(&rhs != this)
3975 {
3976 resize(rhs.m_Count);
3977 if(m_Count != 0)
3978 {
3979 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3980 }
3981 }
3982 return *this;
3983 }
3984
3985 bool empty() const { return m_Count == 0; }
3986 size_t size() const { return m_Count; }
3987 T* data() { return m_pArray; }
3988 const T* data() const { return m_pArray; }
3989
3990 T& operator[](size_t index)
3991 {
3992 VMA_HEAVY_ASSERT(index < m_Count);
3993 return m_pArray[index];
3994 }
3995 const T& operator[](size_t index) const
3996 {
3997 VMA_HEAVY_ASSERT(index < m_Count);
3998 return m_pArray[index];
3999 }
4000
4001 T& front()
4002 {
4003 VMA_HEAVY_ASSERT(m_Count > 0);
4004 return m_pArray[0];
4005 }
4006 const T& front() const
4007 {
4008 VMA_HEAVY_ASSERT(m_Count > 0);
4009 return m_pArray[0];
4010 }
4011 T& back()
4012 {
4013 VMA_HEAVY_ASSERT(m_Count > 0);
4014 return m_pArray[m_Count - 1];
4015 }
4016 const T& back() const
4017 {
4018 VMA_HEAVY_ASSERT(m_Count > 0);
4019 return m_pArray[m_Count - 1];
4020 }
4021
4022 void reserve(size_t newCapacity, bool freeMemory = false)
4023 {
4024 newCapacity = VMA_MAX(newCapacity, m_Count);
4025
4026 if((newCapacity < m_Capacity) && !freeMemory)
4027 {
4028 newCapacity = m_Capacity;
4029 }
4030
4031 if(newCapacity != m_Capacity)
4032 {
4033 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4034 if(m_Count != 0)
4035 {
4036 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4037 }
4038 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4039 m_Capacity = newCapacity;
4040 m_pArray = newArray;
4041 }
4042 }
4043
4044 void resize(size_t newCount, bool freeMemory = false)
4045 {
4046 size_t newCapacity = m_Capacity;
4047 if(newCount > m_Capacity)
4048 {
4049 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4050 }
4051 else if(freeMemory)
4052 {
4053 newCapacity = newCount;
4054 }
4055
4056 if(newCapacity != m_Capacity)
4057 {
4058 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4059 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4060 if(elementsToCopy != 0)
4061 {
4062 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4063 }
4064 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4065 m_Capacity = newCapacity;
4066 m_pArray = newArray;
4067 }
4068
4069 m_Count = newCount;
4070 }
4071
4072 void clear(bool freeMemory = false)
4073 {
4074 resize(0, freeMemory);
4075 }
4076
4077 void insert(size_t index, const T& src)
4078 {
4079 VMA_HEAVY_ASSERT(index <= m_Count);
4080 const size_t oldCount = size();
4081 resize(oldCount + 1);
4082 if(index < oldCount)
4083 {
4084 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4085 }
4086 m_pArray[index] = src;
4087 }
4088
4089 void remove(size_t index)
4090 {
4091 VMA_HEAVY_ASSERT(index < m_Count);
4092 const size_t oldCount = size();
4093 if(index < oldCount - 1)
4094 {
4095 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4096 }
4097 resize(oldCount - 1);
4098 }
4099
4100 void push_back(const T& src)
4101 {
4102 const size_t newIndex = size();
4103 resize(newIndex + 1);
4104 m_pArray[newIndex] = src;
4105 }
4106
4107 void pop_back()
4108 {
4109 VMA_HEAVY_ASSERT(m_Count > 0);
4110 resize(size() - 1);
4111 }
4112
4113 void push_front(const T& src)
4114 {
4115 insert(0, src);
4116 }
4117
4118 void pop_front()
4119 {
4120 VMA_HEAVY_ASSERT(m_Count > 0);
4121 remove(0);
4122 }
4123
4124 typedef T* iterator;
4125
4126 iterator begin() { return m_pArray; }
4127 iterator end() { return m_pArray + m_Count; }
4128
4129private:
4130 AllocatorT m_Allocator;
4131 T* m_pArray;
4132 size_t m_Count;
4133 size_t m_Capacity;
4134};
4135
4136template<typename T, typename allocatorT>
4137static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4138{
4139 vec.insert(index, item);
4140}
4141
4142template<typename T, typename allocatorT>
4143static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4144{
4145 vec.remove(index);
4146}
4147
4148#endif // #if VMA_USE_STL_VECTOR
4149
4150template<typename CmpLess, typename VectorT>
4151size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4152{
4153 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4154 vector.data(),
4155 vector.data() + vector.size(),
4156 value,
4157 CmpLess()) - vector.data();
4158 VmaVectorInsert(vector, indexToInsert, value);
4159 return indexToInsert;
4160}
4161
4162template<typename CmpLess, typename VectorT>
4163bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4164{
4165 CmpLess comparator;
4166 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4167 vector.begin(),
4168 vector.end(),
4169 value,
4170 comparator);
4171 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4172 {
4173 size_t indexToRemove = it - vector.begin();
4174 VmaVectorRemove(vector, indexToRemove);
4175 return true;
4176 }
4177 return false;
4178}
4179
4180template<typename CmpLess, typename IterT, typename KeyT>
4181IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4182{
4183 CmpLess comparator;
4184 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4185 beg, end, value, comparator);
4186 if(it == end ||
4187 (!comparator(*it, value) && !comparator(value, *it)))
4188 {
4189 return it;
4190 }
4191 return end;
4192}
4193
4194////////////////////////////////////////////////////////////////////////////////
4195// class VmaPoolAllocator
4196
4197/*
4198Allocator for objects of type T using a list of arrays (pools) to speed up
4199allocation. Number of elements that can be allocated is not bounded because
4200allocator can create multiple blocks.
4201*/
4202template<typename T>
4203class VmaPoolAllocator
4204{
4205 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4206public:
4207 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4208 ~VmaPoolAllocator();
4209 void Clear();
4210 T* Alloc();
4211 void Free(T* ptr);
4212
4213private:
4214 union Item
4215 {
4216 uint32_t NextFreeIndex;
4217 T Value;
4218 };
4219
4220 struct ItemBlock
4221 {
4222 Item* pItems;
4223 uint32_t FirstFreeIndex;
4224 };
4225
4226 const VkAllocationCallbacks* m_pAllocationCallbacks;
4227 size_t m_ItemsPerBlock;
4228 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4229
4230 ItemBlock& CreateNewBlock();
4231};
4232
4233template<typename T>
4234VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4235 m_pAllocationCallbacks(pAllocationCallbacks),
4236 m_ItemsPerBlock(itemsPerBlock),
4237 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4238{
4239 VMA_ASSERT(itemsPerBlock > 0);
4240}
4241
4242template<typename T>
4243VmaPoolAllocator<T>::~VmaPoolAllocator()
4244{
4245 Clear();
4246}
4247
4248template<typename T>
4249void VmaPoolAllocator<T>::Clear()
4250{
4251 for(size_t i = m_ItemBlocks.size(); i--; )
4252 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4253 m_ItemBlocks.clear();
4254}
4255
4256template<typename T>
4257T* VmaPoolAllocator<T>::Alloc()
4258{
4259 for(size_t i = m_ItemBlocks.size(); i--; )
4260 {
4261 ItemBlock& block = m_ItemBlocks[i];
4262 // This block has some free items: Use first one.
4263 if(block.FirstFreeIndex != UINT32_MAX)
4264 {
4265 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4266 block.FirstFreeIndex = pItem->NextFreeIndex;
4267 return &pItem->Value;
4268 }
4269 }
4270
4271 // No block has free item: Create new one and use it.
4272 ItemBlock& newBlock = CreateNewBlock();
4273 Item* const pItem = &newBlock.pItems[0];
4274 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4275 return &pItem->Value;
4276}
4277
4278template<typename T>
4279void VmaPoolAllocator<T>::Free(T* ptr)
4280{
4281 // Search all memory blocks to find ptr.
4282 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4283 {
4284 ItemBlock& block = m_ItemBlocks[i];
4285
4286 // Casting to union.
4287 Item* pItemPtr;
4288 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4289
4290 // Check if pItemPtr is in address range of this block.
4291 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4292 {
4293 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4294 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4295 block.FirstFreeIndex = index;
4296 return;
4297 }
4298 }
4299 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4300}
4301
4302template<typename T>
4303typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4304{
4305 ItemBlock newBlock = {
4306 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4307
4308 m_ItemBlocks.push_back(newBlock);
4309
4310 // Setup singly-linked list of all free items in this block.
4311 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4312 newBlock.pItems[i].NextFreeIndex = i + 1;
4313 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4314 return m_ItemBlocks.back();
4315}
4316
4317////////////////////////////////////////////////////////////////////////////////
4318// class VmaRawList, VmaList
4319
4320#if VMA_USE_STL_LIST
4321
4322#define VmaList std::list
4323
4324#else // #if VMA_USE_STL_LIST
4325
4326template<typename T>
4327struct VmaListItem
4328{
4329 VmaListItem* pPrev;
4330 VmaListItem* pNext;
4331 T Value;
4332};
4333
4334// Doubly linked list.
4335template<typename T>
4336class VmaRawList
4337{
4338 VMA_CLASS_NO_COPY(VmaRawList)
4339public:
4340 typedef VmaListItem<T> ItemType;
4341
4342 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4343 ~VmaRawList();
4344 void Clear();
4345
4346 size_t GetCount() const { return m_Count; }
4347 bool IsEmpty() const { return m_Count == 0; }
4348
4349 ItemType* Front() { return m_pFront; }
4350 const ItemType* Front() const { return m_pFront; }
4351 ItemType* Back() { return m_pBack; }
4352 const ItemType* Back() const { return m_pBack; }
4353
4354 ItemType* PushBack();
4355 ItemType* PushFront();
4356 ItemType* PushBack(const T& value);
4357 ItemType* PushFront(const T& value);
4358 void PopBack();
4359 void PopFront();
4360
4361 // Item can be null - it means PushBack.
4362 ItemType* InsertBefore(ItemType* pItem);
4363 // Item can be null - it means PushFront.
4364 ItemType* InsertAfter(ItemType* pItem);
4365
4366 ItemType* InsertBefore(ItemType* pItem, const T& value);
4367 ItemType* InsertAfter(ItemType* pItem, const T& value);
4368
4369 void Remove(ItemType* pItem);
4370
4371private:
4372 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4373 VmaPoolAllocator<ItemType> m_ItemAllocator;
4374 ItemType* m_pFront;
4375 ItemType* m_pBack;
4376 size_t m_Count;
4377};
4378
4379template<typename T>
4380VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4381 m_pAllocationCallbacks(pAllocationCallbacks),
4382 m_ItemAllocator(pAllocationCallbacks, 128),
4383 m_pFront(VMA_NULL),
4384 m_pBack(VMA_NULL),
4385 m_Count(0)
4386{
4387}
4388
4389template<typename T>
4390VmaRawList<T>::~VmaRawList()
4391{
4392 // Intentionally not calling Clear, because that would be unnecessary
4393 // computations to return all items to m_ItemAllocator as free.
4394}
4395
4396template<typename T>
4397void VmaRawList<T>::Clear()
4398{
4399 if(IsEmpty() == false)
4400 {
4401 ItemType* pItem = m_pBack;
4402 while(pItem != VMA_NULL)
4403 {
4404 ItemType* const pPrevItem = pItem->pPrev;
4405 m_ItemAllocator.Free(pItem);
4406 pItem = pPrevItem;
4407 }
4408 m_pFront = VMA_NULL;
4409 m_pBack = VMA_NULL;
4410 m_Count = 0;
4411 }
4412}
4413
4414template<typename T>
4415VmaListItem<T>* VmaRawList<T>::PushBack()
4416{
4417 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4418 pNewItem->pNext = VMA_NULL;
4419 if(IsEmpty())
4420 {
4421 pNewItem->pPrev = VMA_NULL;
4422 m_pFront = pNewItem;
4423 m_pBack = pNewItem;
4424 m_Count = 1;
4425 }
4426 else
4427 {
4428 pNewItem->pPrev = m_pBack;
4429 m_pBack->pNext = pNewItem;
4430 m_pBack = pNewItem;
4431 ++m_Count;
4432 }
4433 return pNewItem;
4434}
4435
4436template<typename T>
4437VmaListItem<T>* VmaRawList<T>::PushFront()
4438{
4439 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4440 pNewItem->pPrev = VMA_NULL;
4441 if(IsEmpty())
4442 {
4443 pNewItem->pNext = VMA_NULL;
4444 m_pFront = pNewItem;
4445 m_pBack = pNewItem;
4446 m_Count = 1;
4447 }
4448 else
4449 {
4450 pNewItem->pNext = m_pFront;
4451 m_pFront->pPrev = pNewItem;
4452 m_pFront = pNewItem;
4453 ++m_Count;
4454 }
4455 return pNewItem;
4456}
4457
4458template<typename T>
4459VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4460{
4461 ItemType* const pNewItem = PushBack();
4462 pNewItem->Value = value;
4463 return pNewItem;
4464}
4465
4466template<typename T>
4467VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4468{
4469 ItemType* const pNewItem = PushFront();
4470 pNewItem->Value = value;
4471 return pNewItem;
4472}
4473
4474template<typename T>
4475void VmaRawList<T>::PopBack()
4476{
4477 VMA_HEAVY_ASSERT(m_Count > 0);
4478 ItemType* const pBackItem = m_pBack;
4479 ItemType* const pPrevItem = pBackItem->pPrev;
4480 if(pPrevItem != VMA_NULL)
4481 {
4482 pPrevItem->pNext = VMA_NULL;
4483 }
4484 m_pBack = pPrevItem;
4485 m_ItemAllocator.Free(pBackItem);
4486 --m_Count;
4487}
4488
4489template<typename T>
4490void VmaRawList<T>::PopFront()
4491{
4492 VMA_HEAVY_ASSERT(m_Count > 0);
4493 ItemType* const pFrontItem = m_pFront;
4494 ItemType* const pNextItem = pFrontItem->pNext;
4495 if(pNextItem != VMA_NULL)
4496 {
4497 pNextItem->pPrev = VMA_NULL;
4498 }
4499 m_pFront = pNextItem;
4500 m_ItemAllocator.Free(pFrontItem);
4501 --m_Count;
4502}
4503
4504template<typename T>
4505void VmaRawList<T>::Remove(ItemType* pItem)
4506{
4507 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4508 VMA_HEAVY_ASSERT(m_Count > 0);
4509
4510 if(pItem->pPrev != VMA_NULL)
4511 {
4512 pItem->pPrev->pNext = pItem->pNext;
4513 }
4514 else
4515 {
4516 VMA_HEAVY_ASSERT(m_pFront == pItem);
4517 m_pFront = pItem->pNext;
4518 }
4519
4520 if(pItem->pNext != VMA_NULL)
4521 {
4522 pItem->pNext->pPrev = pItem->pPrev;
4523 }
4524 else
4525 {
4526 VMA_HEAVY_ASSERT(m_pBack == pItem);
4527 m_pBack = pItem->pPrev;
4528 }
4529
4530 m_ItemAllocator.Free(pItem);
4531 --m_Count;
4532}
4533
4534template<typename T>
4535VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4536{
4537 if(pItem != VMA_NULL)
4538 {
4539 ItemType* const prevItem = pItem->pPrev;
4540 ItemType* const newItem = m_ItemAllocator.Alloc();
4541 newItem->pPrev = prevItem;
4542 newItem->pNext = pItem;
4543 pItem->pPrev = newItem;
4544 if(prevItem != VMA_NULL)
4545 {
4546 prevItem->pNext = newItem;
4547 }
4548 else
4549 {
4550 VMA_HEAVY_ASSERT(m_pFront == pItem);
4551 m_pFront = newItem;
4552 }
4553 ++m_Count;
4554 return newItem;
4555 }
4556 else
4557 return PushBack();
4558}
4559
4560template<typename T>
4561VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4562{
4563 if(pItem != VMA_NULL)
4564 {
4565 ItemType* const nextItem = pItem->pNext;
4566 ItemType* const newItem = m_ItemAllocator.Alloc();
4567 newItem->pNext = nextItem;
4568 newItem->pPrev = pItem;
4569 pItem->pNext = newItem;
4570 if(nextItem != VMA_NULL)
4571 {
4572 nextItem->pPrev = newItem;
4573 }
4574 else
4575 {
4576 VMA_HEAVY_ASSERT(m_pBack == pItem);
4577 m_pBack = newItem;
4578 }
4579 ++m_Count;
4580 return newItem;
4581 }
4582 else
4583 return PushFront();
4584}
4585
4586template<typename T>
4587VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4588{
4589 ItemType* const newItem = InsertBefore(pItem);
4590 newItem->Value = value;
4591 return newItem;
4592}
4593
4594template<typename T>
4595VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4596{
4597 ItemType* const newItem = InsertAfter(pItem);
4598 newItem->Value = value;
4599 return newItem;
4600}
4601
4602template<typename T, typename AllocatorT>
4603class VmaList
4604{
4605 VMA_CLASS_NO_COPY(VmaList)
4606public:
4607 class iterator
4608 {
4609 public:
4610 iterator() :
4611 m_pList(VMA_NULL),
4612 m_pItem(VMA_NULL)
4613 {
4614 }
4615
4616 T& operator*() const
4617 {
4618 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4619 return m_pItem->Value;
4620 }
4621 T* operator->() const
4622 {
4623 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4624 return &m_pItem->Value;
4625 }
4626
4627 iterator& operator++()
4628 {
4629 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4630 m_pItem = m_pItem->pNext;
4631 return *this;
4632 }
4633 iterator& operator--()
4634 {
4635 if(m_pItem != VMA_NULL)
4636 {
4637 m_pItem = m_pItem->pPrev;
4638 }
4639 else
4640 {
4641 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4642 m_pItem = m_pList->Back();
4643 }
4644 return *this;
4645 }
4646
4647 iterator operator++(int)
4648 {
4649 iterator result = *this;
4650 ++*this;
4651 return result;
4652 }
4653 iterator operator--(int)
4654 {
4655 iterator result = *this;
4656 --*this;
4657 return result;
4658 }
4659
4660 bool operator==(const iterator& rhs) const
4661 {
4662 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4663 return m_pItem == rhs.m_pItem;
4664 }
4665 bool operator!=(const iterator& rhs) const
4666 {
4667 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4668 return m_pItem != rhs.m_pItem;
4669 }
4670
4671 private:
4672 VmaRawList<T>* m_pList;
4673 VmaListItem<T>* m_pItem;
4674
4675 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4676 m_pList(pList),
4677 m_pItem(pItem)
4678 {
4679 }
4680
4681 friend class VmaList<T, AllocatorT>;
4682 };
4683
4684 class const_iterator
4685 {
4686 public:
4687 const_iterator() :
4688 m_pList(VMA_NULL),
4689 m_pItem(VMA_NULL)
4690 {
4691 }
4692
4693 const_iterator(const iterator& src) :
4694 m_pList(src.m_pList),
4695 m_pItem(src.m_pItem)
4696 {
4697 }
4698
4699 const T& operator*() const
4700 {
4701 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4702 return m_pItem->Value;
4703 }
4704 const T* operator->() const
4705 {
4706 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4707 return &m_pItem->Value;
4708 }
4709
4710 const_iterator& operator++()
4711 {
4712 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4713 m_pItem = m_pItem->pNext;
4714 return *this;
4715 }
4716 const_iterator& operator--()
4717 {
4718 if(m_pItem != VMA_NULL)
4719 {
4720 m_pItem = m_pItem->pPrev;
4721 }
4722 else
4723 {
4724 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4725 m_pItem = m_pList->Back();
4726 }
4727 return *this;
4728 }
4729
4730 const_iterator operator++(int)
4731 {
4732 const_iterator result = *this;
4733 ++*this;
4734 return result;
4735 }
4736 const_iterator operator--(int)
4737 {
4738 const_iterator result = *this;
4739 --*this;
4740 return result;
4741 }
4742
4743 bool operator==(const const_iterator& rhs) const
4744 {
4745 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4746 return m_pItem == rhs.m_pItem;
4747 }
4748 bool operator!=(const const_iterator& rhs) const
4749 {
4750 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4751 return m_pItem != rhs.m_pItem;
4752 }
4753
4754 private:
4755 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4756 m_pList(pList),
4757 m_pItem(pItem)
4758 {
4759 }
4760
4761 const VmaRawList<T>* m_pList;
4762 const VmaListItem<T>* m_pItem;
4763
4764 friend class VmaList<T, AllocatorT>;
4765 };
4766
4767 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4768
4769 bool empty() const { return m_RawList.IsEmpty(); }
4770 size_t size() const { return m_RawList.GetCount(); }
4771
4772 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
4773 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4774
4775 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
4776 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4777
4778 void clear() { m_RawList.Clear(); }
4779 void push_back(const T& value) { m_RawList.PushBack(value); }
4780 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4781 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4782
4783private:
4784 VmaRawList<T> m_RawList;
4785};
4786
4787#endif // #if VMA_USE_STL_LIST
4788
4789////////////////////////////////////////////////////////////////////////////////
4790// class VmaMap
4791
4792// Unused in this version.
4793#if 0
4794
4795#if VMA_USE_STL_UNORDERED_MAP
4796
4797#define VmaPair std::pair
4798
4799#define VMA_MAP_TYPE(KeyT, ValueT) \
4800 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4801
4802#else // #if VMA_USE_STL_UNORDERED_MAP
4803
4804template<typename T1, typename T2>
4805struct VmaPair
4806{
4807 T1 first;
4808 T2 second;
4809
4810 VmaPair() : first(), second() { }
4811 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4812};
4813
4814/* Class compatible with subset of interface of std::unordered_map.
4815KeyT, ValueT must be POD because they will be stored in VmaVector.
4816*/
4817template<typename KeyT, typename ValueT>
4818class VmaMap
4819{
4820public:
4821 typedef VmaPair<KeyT, ValueT> PairType;
4822 typedef PairType* iterator;
4823
4824 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4825
4826 iterator begin() { return m_Vector.begin(); }
4827 iterator end() { return m_Vector.end(); }
4828
4829 void insert(const PairType& pair);
4830 iterator find(const KeyT& key);
4831 void erase(iterator it);
4832
4833private:
4834 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4835};
4836
4837#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4838
4839template<typename FirstT, typename SecondT>
4840struct VmaPairFirstLess
4841{
4842 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4843 {
4844 return lhs.first < rhs.first;
4845 }
4846 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4847 {
4848 return lhs.first < rhsFirst;
4849 }
4850};
4851
4852template<typename KeyT, typename ValueT>
4853void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4854{
4855 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4856 m_Vector.data(),
4857 m_Vector.data() + m_Vector.size(),
4858 pair,
4859 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4860 VmaVectorInsert(m_Vector, indexToInsert, pair);
4861}
4862
4863template<typename KeyT, typename ValueT>
4864VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4865{
4866 PairType* it = VmaBinaryFindFirstNotLess(
4867 m_Vector.data(),
4868 m_Vector.data() + m_Vector.size(),
4869 key,
4870 VmaPairFirstLess<KeyT, ValueT>());
4871 if((it != m_Vector.end()) && (it->first == key))
4872 {
4873 return it;
4874 }
4875 else
4876 {
4877 return m_Vector.end();
4878 }
4879}
4880
4881template<typename KeyT, typename ValueT>
4882void VmaMap<KeyT, ValueT>::erase(iterator it)
4883{
4884 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4885}
4886
4887#endif // #if VMA_USE_STL_UNORDERED_MAP
4888
4889#endif // #if 0
4890
4891////////////////////////////////////////////////////////////////////////////////
4892
4893class VmaDeviceMemoryBlock;
4894
4895enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4896
4897struct VmaAllocation_T
4898{
4899 VMA_CLASS_NO_COPY(VmaAllocation_T)
4900private:
4901 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4902
4903 enum FLAGS
4904 {
4905 FLAG_USER_DATA_STRING = 0x01,
4906 };
4907
4908public:
4909 enum ALLOCATION_TYPE
4910 {
4911 ALLOCATION_TYPE_NONE,
4912 ALLOCATION_TYPE_BLOCK,
4913 ALLOCATION_TYPE_DEDICATED,
4914 };
4915
4916 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4917 m_Alignment(1),
4918 m_Size(0),
4919 m_pUserData(VMA_NULL),
4920 m_LastUseFrameIndex(currentFrameIndex),
4921 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4922 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4923 m_MapCount(0),
4924 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4925 {
4926#if VMA_STATS_STRING_ENABLED
4927 m_CreationFrameIndex = currentFrameIndex;
4928 m_BufferImageUsage = 0;
4929#endif
4930 }
4931
4932 ~VmaAllocation_T()
4933 {
4934 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4935
4936 // Check if owned string was freed.
4937 VMA_ASSERT(m_pUserData == VMA_NULL);
4938 }
4939
4940 void InitBlockAllocation(
4941 VmaPool hPool,
4942 VmaDeviceMemoryBlock* block,
4943 VkDeviceSize offset,
4944 VkDeviceSize alignment,
4945 VkDeviceSize size,
4946 VmaSuballocationType suballocationType,
4947 bool mapped,
4948 bool canBecomeLost)
4949 {
4950 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4951 VMA_ASSERT(block != VMA_NULL);
4952 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4953 m_Alignment = alignment;
4954 m_Size = size;
4955 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4956 m_SuballocationType = (uint8_t)suballocationType;
4957 m_BlockAllocation.m_hPool = hPool;
4958 m_BlockAllocation.m_Block = block;
4959 m_BlockAllocation.m_Offset = offset;
4960 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4961 }
4962
4963 void InitLost()
4964 {
4965 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4966 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4967 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4968 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4969 m_BlockAllocation.m_Block = VMA_NULL;
4970 m_BlockAllocation.m_Offset = 0;
4971 m_BlockAllocation.m_CanBecomeLost = true;
4972 }
4973
4974 void ChangeBlockAllocation(
4975 VmaAllocator hAllocator,
4976 VmaDeviceMemoryBlock* block,
4977 VkDeviceSize offset);
4978
4979 void ChangeSize(VkDeviceSize newSize);
4980 void ChangeOffset(VkDeviceSize newOffset);
4981
4982 // pMappedData not null means allocation is created with MAPPED flag.
4983 void InitDedicatedAllocation(
4984 uint32_t memoryTypeIndex,
4985 VkDeviceMemory hMemory,
4986 VmaSuballocationType suballocationType,
4987 void* pMappedData,
4988 VkDeviceSize size)
4989 {
4990 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4991 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
4992 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
4993 m_Alignment = 0;
4994 m_Size = size;
4995 m_SuballocationType = (uint8_t)suballocationType;
4996 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4997 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
4998 m_DedicatedAllocation.m_hMemory = hMemory;
4999 m_DedicatedAllocation.m_pMappedData = pMappedData;
5000 }
5001
5002 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
5003 VkDeviceSize GetAlignment() const { return m_Alignment; }
5004 VkDeviceSize GetSize() const { return m_Size; }
5005 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
5006 void* GetUserData() const { return m_pUserData; }
5007 void SetUserData(VmaAllocator hAllocator, void* pUserData);
5008 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
5009
5010 VmaDeviceMemoryBlock* GetBlock() const
5011 {
5012 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5013 return m_BlockAllocation.m_Block;
5014 }
5015 VkDeviceSize GetOffset() const;
5016 VkDeviceMemory GetMemory() const;
5017 uint32_t GetMemoryTypeIndex() const;
5018 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5019 void* GetMappedData() const;
5020 bool CanBecomeLost() const;
5021 VmaPool GetPool() const;
5022
5023 uint32_t GetLastUseFrameIndex() const
5024 {
5025 return m_LastUseFrameIndex.load();
5026 }
5027 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5028 {
5029 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5030 }
5031 /*
5032 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5033 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5034 - Else, returns false.
5035
5036 If hAllocation is already lost, assert - you should not call it then.
5037 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5038 */
5039 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5040
5041 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5042 {
5043 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5044 outInfo.blockCount = 1;
5045 outInfo.allocationCount = 1;
5046 outInfo.unusedRangeCount = 0;
5047 outInfo.usedBytes = m_Size;
5048 outInfo.unusedBytes = 0;
5049 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5050 outInfo.unusedRangeSizeMin = UINT64_MAX;
5051 outInfo.unusedRangeSizeMax = 0;
5052 }
5053
5054 void BlockAllocMap();
5055 void BlockAllocUnmap();
5056 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5057 void DedicatedAllocUnmap(VmaAllocator hAllocator);
5058
5059#if VMA_STATS_STRING_ENABLED
5060 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
5061 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5062
5063 void InitBufferImageUsage(uint32_t bufferImageUsage)
5064 {
5065 VMA_ASSERT(m_BufferImageUsage == 0);
5066 m_BufferImageUsage = bufferImageUsage;
5067 }
5068
5069 void PrintParameters(class VmaJsonWriter& json) const;
5070#endif
5071
5072private:
5073 VkDeviceSize m_Alignment;
5074 VkDeviceSize m_Size;
5075 void* m_pUserData;
5076 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5077 uint8_t m_Type; // ALLOCATION_TYPE
5078 uint8_t m_SuballocationType; // VmaSuballocationType
5079 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5080 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5081 uint8_t m_MapCount;
5082 uint8_t m_Flags; // enum FLAGS
5083
5084 // Allocation out of VmaDeviceMemoryBlock.
5085 struct BlockAllocation
5086 {
5087 VmaPool m_hPool; // Null if belongs to general memory.
5088 VmaDeviceMemoryBlock* m_Block;
5089 VkDeviceSize m_Offset;
5090 bool m_CanBecomeLost;
5091 };
5092
5093 // Allocation for an object that has its own private VkDeviceMemory.
5094 struct DedicatedAllocation
5095 {
5096 uint32_t m_MemoryTypeIndex;
5097 VkDeviceMemory m_hMemory;
5098 void* m_pMappedData; // Not null means memory is mapped.
5099 };
5100
5101 union
5102 {
5103 // Allocation out of VmaDeviceMemoryBlock.
5104 BlockAllocation m_BlockAllocation;
5105 // Allocation for an object that has its own private VkDeviceMemory.
5106 DedicatedAllocation m_DedicatedAllocation;
5107 };
5108
5109#if VMA_STATS_STRING_ENABLED
5110 uint32_t m_CreationFrameIndex;
5111 uint32_t m_BufferImageUsage; // 0 if unknown.
5112#endif
5113
5114 void FreeUserDataString(VmaAllocator hAllocator);
5115};
5116
5117/*
5118Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5119allocated memory block or free.
5120*/
5121struct VmaSuballocation
5122{
5123 VkDeviceSize offset;
5124 VkDeviceSize size;
5125 VmaAllocation hAllocation;
5126 VmaSuballocationType type;
5127};
5128
5129// Comparator for offsets.
5130struct VmaSuballocationOffsetLess
5131{
5132 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5133 {
5134 return lhs.offset < rhs.offset;
5135 }
5136};
5137struct VmaSuballocationOffsetGreater
5138{
5139 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5140 {
5141 return lhs.offset > rhs.offset;
5142 }
5143};
5144
5145typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5146
5147// Cost of one additional allocation lost, as equivalent in bytes.
5148static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5149
5150/*
5151Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5152
5153If canMakeOtherLost was false:
5154- item points to a FREE suballocation.
5155- itemsToMakeLostCount is 0.
5156
5157If canMakeOtherLost was true:
5158- item points to first of sequence of suballocations, which are either FREE,
5159 or point to VmaAllocations that can become lost.
5160- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5161 the requested allocation to succeed.
5162*/
5163struct VmaAllocationRequest
5164{
5165 VkDeviceSize offset;
5166 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5167 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5168 VmaSuballocationList::iterator item;
5169 size_t itemsToMakeLostCount;
5170 void* customData;
5171
5172 VkDeviceSize CalcCost() const
5173 {
5174 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5175 }
5176};
5177
5178/*
5179Data structure used for bookkeeping of allocations and unused ranges of memory
5180in a single VkDeviceMemory block.
5181*/
5182class VmaBlockMetadata
5183{
5184public:
5185 VmaBlockMetadata(VmaAllocator hAllocator);
5186 virtual ~VmaBlockMetadata() { }
5187 virtual void Init(VkDeviceSize size) { m_Size = size; }
5188
5189 // Validates all data structures inside this object. If not valid, returns false.
5190 virtual bool Validate() const = 0;
5191 VkDeviceSize GetSize() const { return m_Size; }
5192 virtual size_t GetAllocationCount() const = 0;
5193 virtual VkDeviceSize GetSumFreeSize() const = 0;
5194 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5195 // Returns true if this block is empty - contains only single free suballocation.
5196 virtual bool IsEmpty() const = 0;
5197
5198 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5199 // Shouldn't modify blockCount.
5200 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5201
5202#if VMA_STATS_STRING_ENABLED
5203 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5204#endif
5205
5206 // Tries to find a place for suballocation with given parameters inside this block.
5207 // If succeeded, fills pAllocationRequest and returns true.
5208 // If failed, returns false.
5209 virtual bool CreateAllocationRequest(
5210 uint32_t currentFrameIndex,
5211 uint32_t frameInUseCount,
5212 VkDeviceSize bufferImageGranularity,
5213 VkDeviceSize allocSize,
5214 VkDeviceSize allocAlignment,
5215 bool upperAddress,
5216 VmaSuballocationType allocType,
5217 bool canMakeOtherLost,
5218 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5219 uint32_t strategy,
5220 VmaAllocationRequest* pAllocationRequest) = 0;
5221
5222 virtual bool MakeRequestedAllocationsLost(
5223 uint32_t currentFrameIndex,
5224 uint32_t frameInUseCount,
5225 VmaAllocationRequest* pAllocationRequest) = 0;
5226
5227 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5228
5229 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5230
5231 // Makes actual allocation based on request. Request must already be checked and valid.
5232 virtual void Alloc(
5233 const VmaAllocationRequest& request,
5234 VmaSuballocationType type,
5235 VkDeviceSize allocSize,
5236 bool upperAddress,
5237 VmaAllocation hAllocation) = 0;
5238
5239 // Frees suballocation assigned to given memory region.
5240 virtual void Free(const VmaAllocation allocation) = 0;
5241 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5242
5243 // Tries to resize (grow or shrink) space for given allocation, in place.
5244 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5245
5246protected:
5247 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5248
5249#if VMA_STATS_STRING_ENABLED
5250 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5251 VkDeviceSize unusedBytes,
5252 size_t allocationCount,
5253 size_t unusedRangeCount) const;
5254 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5255 VkDeviceSize offset,
5256 VmaAllocation hAllocation) const;
5257 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5258 VkDeviceSize offset,
5259 VkDeviceSize size) const;
5260 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5261#endif
5262
5263private:
5264 VkDeviceSize m_Size;
5265 const VkAllocationCallbacks* m_pAllocationCallbacks;
5266};
5267
5268#define VMA_VALIDATE(cond) do { if(!(cond)) { \
5269 VMA_ASSERT(0 && "Validation failed: " #cond); \
5270 return false; \
5271 } } while(false)
5272
5273class VmaBlockMetadata_Generic : public VmaBlockMetadata
5274{
5275 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5276public:
5277 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5278 virtual ~VmaBlockMetadata_Generic();
5279 virtual void Init(VkDeviceSize size);
5280
5281 virtual bool Validate() const;
5282 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
5283 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5284 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5285 virtual bool IsEmpty() const;
5286
5287 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5288 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5289
5290#if VMA_STATS_STRING_ENABLED
5291 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5292#endif
5293
5294 virtual bool CreateAllocationRequest(
5295 uint32_t currentFrameIndex,
5296 uint32_t frameInUseCount,
5297 VkDeviceSize bufferImageGranularity,
5298 VkDeviceSize allocSize,
5299 VkDeviceSize allocAlignment,
5300 bool upperAddress,
5301 VmaSuballocationType allocType,
5302 bool canMakeOtherLost,
5303 uint32_t strategy,
5304 VmaAllocationRequest* pAllocationRequest);
5305
5306 virtual bool MakeRequestedAllocationsLost(
5307 uint32_t currentFrameIndex,
5308 uint32_t frameInUseCount,
5309 VmaAllocationRequest* pAllocationRequest);
5310
5311 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5312
5313 virtual VkResult CheckCorruption(const void* pBlockData);
5314
5315 virtual void Alloc(
5316 const VmaAllocationRequest& request,
5317 VmaSuballocationType type,
5318 VkDeviceSize allocSize,
5319 bool upperAddress,
5320 VmaAllocation hAllocation);
5321
5322 virtual void Free(const VmaAllocation allocation);
5323 virtual void FreeAtOffset(VkDeviceSize offset);
5324
5325 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5326
5327 ////////////////////////////////////////////////////////////////////////////////
5328 // For defragmentation
5329
5330 bool IsBufferImageGranularityConflictPossible(
5331 VkDeviceSize bufferImageGranularity,
5332 VmaSuballocationType& inOutPrevSuballocType) const;
5333
5334private:
5335 friend class VmaDefragmentationAlgorithm_Generic;
5336 friend class VmaDefragmentationAlgorithm_Fast;
5337
5338 uint32_t m_FreeCount;
5339 VkDeviceSize m_SumFreeSize;
5340 VmaSuballocationList m_Suballocations;
5341 // Suballocations that are free and have size greater than certain threshold.
5342 // Sorted by size, ascending.
5343 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5344
5345 bool ValidateFreeSuballocationList() const;
5346
5347 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5348 // If yes, fills pOffset and returns true. If no, returns false.
5349 bool CheckAllocation(
5350 uint32_t currentFrameIndex,
5351 uint32_t frameInUseCount,
5352 VkDeviceSize bufferImageGranularity,
5353 VkDeviceSize allocSize,
5354 VkDeviceSize allocAlignment,
5355 VmaSuballocationType allocType,
5356 VmaSuballocationList::const_iterator suballocItem,
5357 bool canMakeOtherLost,
5358 VkDeviceSize* pOffset,
5359 size_t* itemsToMakeLostCount,
5360 VkDeviceSize* pSumFreeSize,
5361 VkDeviceSize* pSumItemSize) const;
5362 // Given free suballocation, it merges it with following one, which must also be free.
5363 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5364 // Releases given suballocation, making it free.
5365 // Merges it with adjacent free suballocations if applicable.
5366 // Returns iterator to new free suballocation at this place.
5367 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5368 // Given free suballocation, it inserts it into sorted list of
5369 // m_FreeSuballocationsBySize if it's suitable.
5370 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5371 // Given free suballocation, it removes it from sorted list of
5372 // m_FreeSuballocationsBySize if it's suitable.
5373 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5374};
5375
5376/*
5377Allocations and their references in internal data structure look like this:
5378
5379if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5380
5381 0 +-------+
5382 | |
5383 | |
5384 | |
5385 +-------+
5386 | Alloc | 1st[m_1stNullItemsBeginCount]
5387 +-------+
5388 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5389 +-------+
5390 | ... |
5391 +-------+
5392 | Alloc | 1st[1st.size() - 1]
5393 +-------+
5394 | |
5395 | |
5396 | |
5397GetSize() +-------+
5398
5399if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5400
5401 0 +-------+
5402 | Alloc | 2nd[0]
5403 +-------+
5404 | Alloc | 2nd[1]
5405 +-------+
5406 | ... |
5407 +-------+
5408 | Alloc | 2nd[2nd.size() - 1]
5409 +-------+
5410 | |
5411 | |
5412 | |
5413 +-------+
5414 | Alloc | 1st[m_1stNullItemsBeginCount]
5415 +-------+
5416 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5417 +-------+
5418 | ... |
5419 +-------+
5420 | Alloc | 1st[1st.size() - 1]
5421 +-------+
5422 | |
5423GetSize() +-------+
5424
5425if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5426
5427 0 +-------+
5428 | |
5429 | |
5430 | |
5431 +-------+
5432 | Alloc | 1st[m_1stNullItemsBeginCount]
5433 +-------+
5434 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5435 +-------+
5436 | ... |
5437 +-------+
5438 | Alloc | 1st[1st.size() - 1]
5439 +-------+
5440 | |
5441 | |
5442 | |
5443 +-------+
5444 | Alloc | 2nd[2nd.size() - 1]
5445 +-------+
5446 | ... |
5447 +-------+
5448 | Alloc | 2nd[1]
5449 +-------+
5450 | Alloc | 2nd[0]
5451GetSize() +-------+
5452
5453*/
5454class VmaBlockMetadata_Linear : public VmaBlockMetadata
5455{
5456 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5457public:
5458 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5459 virtual ~VmaBlockMetadata_Linear();
5460 virtual void Init(VkDeviceSize size);
5461
5462 virtual bool Validate() const;
5463 virtual size_t GetAllocationCount() const;
5464 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5465 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5466 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5467
5468 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5469 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5470
5471#if VMA_STATS_STRING_ENABLED
5472 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5473#endif
5474
5475 virtual bool CreateAllocationRequest(
5476 uint32_t currentFrameIndex,
5477 uint32_t frameInUseCount,
5478 VkDeviceSize bufferImageGranularity,
5479 VkDeviceSize allocSize,
5480 VkDeviceSize allocAlignment,
5481 bool upperAddress,
5482 VmaSuballocationType allocType,
5483 bool canMakeOtherLost,
5484 uint32_t strategy,
5485 VmaAllocationRequest* pAllocationRequest);
5486
5487 virtual bool MakeRequestedAllocationsLost(
5488 uint32_t currentFrameIndex,
5489 uint32_t frameInUseCount,
5490 VmaAllocationRequest* pAllocationRequest);
5491
5492 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5493
5494 virtual VkResult CheckCorruption(const void* pBlockData);
5495
5496 virtual void Alloc(
5497 const VmaAllocationRequest& request,
5498 VmaSuballocationType type,
5499 VkDeviceSize allocSize,
5500 bool upperAddress,
5501 VmaAllocation hAllocation);
5502
5503 virtual void Free(const VmaAllocation allocation);
5504 virtual void FreeAtOffset(VkDeviceSize offset);
5505
5506private:
5507 /*
5508 There are two suballocation vectors, used in ping-pong way.
5509 The one with index m_1stVectorIndex is called 1st.
5510 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5511 2nd can be non-empty only when 1st is not empty.
5512 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5513 */
5514 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5515
5516 enum SECOND_VECTOR_MODE
5517 {
5518 SECOND_VECTOR_EMPTY,
5519 /*
5520 Suballocations in 2nd vector are created later than the ones in 1st, but they
5521 all have smaller offset.
5522 */
5523 SECOND_VECTOR_RING_BUFFER,
5524 /*
5525 Suballocations in 2nd vector are upper side of double stack.
5526 They all have offsets higher than those in 1st vector.
5527 Top of this stack means smaller offsets, but higher indices in this vector.
5528 */
5529 SECOND_VECTOR_DOUBLE_STACK,
5530 };
5531
5532 VkDeviceSize m_SumFreeSize;
5533 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5534 uint32_t m_1stVectorIndex;
5535 SECOND_VECTOR_MODE m_2ndVectorMode;
5536
5537 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5538 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5539 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5540 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5541
5542 // Number of items in 1st vector with hAllocation = null at the beginning.
5543 size_t m_1stNullItemsBeginCount;
5544 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5545 size_t m_1stNullItemsMiddleCount;
5546 // Number of items in 2nd vector with hAllocation = null.
5547 size_t m_2ndNullItemsCount;
5548
5549 bool ShouldCompact1st() const;
5550 void CleanupAfterFree();
5551};
5552
5553/*
5554- GetSize() is the original size of allocated memory block.
5555- m_UsableSize is this size aligned down to a power of two.
5556 All allocations and calculations happen relative to m_UsableSize.
5557- GetUnusableSize() is the difference between them.
5558 It is repoted as separate, unused range, not available for allocations.
5559
5560Node at level 0 has size = m_UsableSize.
5561Each next level contains nodes with size 2 times smaller than current level.
5562m_LevelCount is the maximum number of levels to use in the current object.
5563*/
5564class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5565{
5566 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5567public:
5568 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5569 virtual ~VmaBlockMetadata_Buddy();
5570 virtual void Init(VkDeviceSize size);
5571
5572 virtual bool Validate() const;
5573 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
5574 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5575 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5576 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5577
5578 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5579 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5580
5581#if VMA_STATS_STRING_ENABLED
5582 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5583#endif
5584
5585 virtual bool CreateAllocationRequest(
5586 uint32_t currentFrameIndex,
5587 uint32_t frameInUseCount,
5588 VkDeviceSize bufferImageGranularity,
5589 VkDeviceSize allocSize,
5590 VkDeviceSize allocAlignment,
5591 bool upperAddress,
5592 VmaSuballocationType allocType,
5593 bool canMakeOtherLost,
5594 uint32_t strategy,
5595 VmaAllocationRequest* pAllocationRequest);
5596
5597 virtual bool MakeRequestedAllocationsLost(
5598 uint32_t currentFrameIndex,
5599 uint32_t frameInUseCount,
5600 VmaAllocationRequest* pAllocationRequest);
5601
5602 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5603
5604 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5605
5606 virtual void Alloc(
5607 const VmaAllocationRequest& request,
5608 VmaSuballocationType type,
5609 VkDeviceSize allocSize,
5610 bool upperAddress,
5611 VmaAllocation hAllocation);
5612
5613 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
5614 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5615
5616private:
5617 static const VkDeviceSize MIN_NODE_SIZE = 32;
5618 static const size_t MAX_LEVELS = 30;
5619
5620 struct ValidationContext
5621 {
5622 size_t calculatedAllocationCount;
5623 size_t calculatedFreeCount;
5624 VkDeviceSize calculatedSumFreeSize;
5625
5626 ValidationContext() :
5627 calculatedAllocationCount(0),
5628 calculatedFreeCount(0),
5629 calculatedSumFreeSize(0) { }
5630 };
5631
5632 struct Node
5633 {
5634 VkDeviceSize offset;
5635 enum TYPE
5636 {
5637 TYPE_FREE,
5638 TYPE_ALLOCATION,
5639 TYPE_SPLIT,
5640 TYPE_COUNT
5641 } type;
5642 Node* parent;
5643 Node* buddy;
5644
5645 union
5646 {
5647 struct
5648 {
5649 Node* prev;
5650 Node* next;
5651 } free;
5652 struct
5653 {
5654 VmaAllocation alloc;
5655 } allocation;
5656 struct
5657 {
5658 Node* leftChild;
5659 } split;
5660 };
5661 };
5662
5663 // Size of the memory block aligned down to a power of two.
5664 VkDeviceSize m_UsableSize;
5665 uint32_t m_LevelCount;
5666
5667 Node* m_Root;
5668 struct {
5669 Node* front;
5670 Node* back;
5671 } m_FreeList[MAX_LEVELS];
5672 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5673 size_t m_AllocationCount;
5674 // Number of nodes in the tree with type == TYPE_FREE.
5675 size_t m_FreeCount;
5676 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5677 VkDeviceSize m_SumFreeSize;
5678
5679 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5680 void DeleteNode(Node* node);
5681 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5682 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
5683 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5684 // Alloc passed just for validation. Can be null.
5685 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5686 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5687 // Adds node to the front of FreeList at given level.
5688 // node->type must be FREE.
5689 // node->free.prev, next can be undefined.
5690 void AddToFreeListFront(uint32_t level, Node* node);
5691 // Removes node from FreeList at given level.
5692 // node->type must be FREE.
5693 // node->free.prev, next stay untouched.
5694 void RemoveFromFreeList(uint32_t level, Node* node);
5695
5696#if VMA_STATS_STRING_ENABLED
5697 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5698#endif
5699};
5700
5701/*
5702Represents a single block of device memory (`VkDeviceMemory`) with all the
5703data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5704
5705Thread-safety: This class must be externally synchronized.
5706*/
5707class VmaDeviceMemoryBlock
5708{
5709 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5710public:
5711 VmaBlockMetadata* m_pMetadata;
5712
5713 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5714
5715 ~VmaDeviceMemoryBlock()
5716 {
5717 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5718 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5719 }
5720
5721 // Always call after construction.
5722 void Init(
5723 VmaAllocator hAllocator,
5724 uint32_t newMemoryTypeIndex,
5725 VkDeviceMemory newMemory,
5726 VkDeviceSize newSize,
5727 uint32_t id,
5728 uint32_t algorithm);
5729 // Always call before destruction.
5730 void Destroy(VmaAllocator allocator);
5731
5732 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
5733 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5734 uint32_t GetId() const { return m_Id; }
5735 void* GetMappedData() const { return m_pMappedData; }
5736
5737 // Validates all data structures inside this object. If not valid, returns false.
5738 bool Validate() const;
5739
5740 VkResult CheckCorruption(VmaAllocator hAllocator);
5741
5742 // ppData can be null.
5743 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5744 void Unmap(VmaAllocator hAllocator, uint32_t count);
5745
5746 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5747 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5748
5749 VkResult BindBufferMemory(
5750 const VmaAllocator hAllocator,
5751 const VmaAllocation hAllocation,
5752 VkBuffer hBuffer);
5753 VkResult BindImageMemory(
5754 const VmaAllocator hAllocator,
5755 const VmaAllocation hAllocation,
5756 VkImage hImage);
5757
5758private:
5759 uint32_t m_MemoryTypeIndex;
5760 uint32_t m_Id;
5761 VkDeviceMemory m_hMemory;
5762
5763 /*
5764 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5765 Also protects m_MapCount, m_pMappedData.
5766 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5767 */
5768 VMA_MUTEX m_Mutex;
5769 uint32_t m_MapCount;
5770 void* m_pMappedData;
5771};
5772
5773struct VmaPointerLess
5774{
5775 bool operator()(const void* lhs, const void* rhs) const
5776 {
5777 return lhs < rhs;
5778 }
5779};
5780
5781struct VmaDefragmentationMove
5782{
5783 size_t srcBlockIndex;
5784 size_t dstBlockIndex;
5785 VkDeviceSize srcOffset;
5786 VkDeviceSize dstOffset;
5787 VkDeviceSize size;
5788};
5789
5790class VmaDefragmentationAlgorithm;
5791
5792/*
5793Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5794Vulkan memory type.
5795
5796Synchronized internally with a mutex.
5797*/
5798struct VmaBlockVector
5799{
5800 VMA_CLASS_NO_COPY(VmaBlockVector)
5801public:
5802 VmaBlockVector(
5803 VmaAllocator hAllocator,
5804 uint32_t memoryTypeIndex,
5805 VkDeviceSize preferredBlockSize,
5806 size_t minBlockCount,
5807 size_t maxBlockCount,
5808 VkDeviceSize bufferImageGranularity,
5809 uint32_t frameInUseCount,
5810 bool isCustomPool,
5811 bool explicitBlockSize,
5812 uint32_t algorithm);
5813 ~VmaBlockVector();
5814
5815 VkResult CreateMinBlocks();
5816
5817 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5818 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
5819 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
5820 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
5821 uint32_t GetAlgorithm() const { return m_Algorithm; }
5822
5823 void GetPoolStats(VmaPoolStats* pStats);
5824
5825 bool IsEmpty() const { return m_Blocks.empty(); }
5826 bool IsCorruptionDetectionEnabled() const;
5827
5828 VkResult Allocate(
5829 VmaPool hCurrentPool,
5830 uint32_t currentFrameIndex,
5831 VkDeviceSize size,
5832 VkDeviceSize alignment,
5833 const VmaAllocationCreateInfo& createInfo,
5834 VmaSuballocationType suballocType,
5835 size_t allocationCount,
5836 VmaAllocation* pAllocations);
5837
5838 void Free(
5839 VmaAllocation hAllocation);
5840
5841 // Adds statistics of this BlockVector to pStats.
5842 void AddStats(VmaStats* pStats);
5843
5844#if VMA_STATS_STRING_ENABLED
5845 void PrintDetailedMap(class VmaJsonWriter& json);
5846#endif
5847
5848 void MakePoolAllocationsLost(
5849 uint32_t currentFrameIndex,
5850 size_t* pLostAllocationCount);
5851 VkResult CheckCorruption();
5852
5853 // Saves results in pCtx->res.
5854 void Defragment(
5855 class VmaBlockVectorDefragmentationContext* pCtx,
5856 VmaDefragmentationStats* pStats,
5857 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5858 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5859 VkCommandBuffer commandBuffer);
5860 void DefragmentationEnd(
5861 class VmaBlockVectorDefragmentationContext* pCtx,
5862 VmaDefragmentationStats* pStats);
5863
5864 ////////////////////////////////////////////////////////////////////////////////
5865 // To be used only while the m_Mutex is locked. Used during defragmentation.
5866
5867 size_t GetBlockCount() const { return m_Blocks.size(); }
5868 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5869 size_t CalcAllocationCount() const;
5870 bool IsBufferImageGranularityConflictPossible() const;
5871
5872private:
5873 friend class VmaDefragmentationAlgorithm_Generic;
5874
5875 const VmaAllocator m_hAllocator;
5876 const uint32_t m_MemoryTypeIndex;
5877 const VkDeviceSize m_PreferredBlockSize;
5878 const size_t m_MinBlockCount;
5879 const size_t m_MaxBlockCount;
5880 const VkDeviceSize m_BufferImageGranularity;
5881 const uint32_t m_FrameInUseCount;
5882 const bool m_IsCustomPool;
5883 const bool m_ExplicitBlockSize;
5884 const uint32_t m_Algorithm;
5885 /* There can be at most one allocation that is completely empty - a
5886 hysteresis to avoid pessimistic case of alternating creation and destruction
5887 of a VkDeviceMemory. */
5888 bool m_HasEmptyBlock;
5889 VMA_RW_MUTEX m_Mutex;
5890 // Incrementally sorted by sumFreeSize, ascending.
5891 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5892 uint32_t m_NextBlockId;
5893
5894 VkDeviceSize CalcMaxBlockSize() const;
5895
5896 // Finds and removes given block from vector.
5897 void Remove(VmaDeviceMemoryBlock* pBlock);
5898
5899 // Performs single step in sorting m_Blocks. They may not be fully sorted
5900 // after this call.
5901 void IncrementallySortBlocks();
5902
5903 VkResult AllocatePage(
5904 VmaPool hCurrentPool,
5905 uint32_t currentFrameIndex,
5906 VkDeviceSize size,
5907 VkDeviceSize alignment,
5908 const VmaAllocationCreateInfo& createInfo,
5909 VmaSuballocationType suballocType,
5910 VmaAllocation* pAllocation);
5911
5912 // To be used only without CAN_MAKE_OTHER_LOST flag.
5913 VkResult AllocateFromBlock(
5914 VmaDeviceMemoryBlock* pBlock,
5915 VmaPool hCurrentPool,
5916 uint32_t currentFrameIndex,
5917 VkDeviceSize size,
5918 VkDeviceSize alignment,
5919 VmaAllocationCreateFlags allocFlags,
5920 void* pUserData,
5921 VmaSuballocationType suballocType,
5922 uint32_t strategy,
5923 VmaAllocation* pAllocation);
5924
5925 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5926
5927 // Saves result to pCtx->res.
5928 void ApplyDefragmentationMovesCpu(
5929 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5930 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5931 // Saves result to pCtx->res.
5932 void ApplyDefragmentationMovesGpu(
5933 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5934 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5935 VkCommandBuffer commandBuffer);
5936
5937 /*
5938 Used during defragmentation. pDefragmentationStats is optional. It's in/out
5939 - updated with new data.
5940 */
5941 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5942};
5943
5944struct VmaPool_T
5945{
5946 VMA_CLASS_NO_COPY(VmaPool_T)
5947public:
5948 VmaBlockVector m_BlockVector;
5949
5950 VmaPool_T(
5951 VmaAllocator hAllocator,
5952 const VmaPoolCreateInfo& createInfo,
5953 VkDeviceSize preferredBlockSize);
5954 ~VmaPool_T();
5955
5956 uint32_t GetId() const { return m_Id; }
5957 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5958
5959#if VMA_STATS_STRING_ENABLED
5960 //void PrintDetailedMap(class VmaStringBuilder& sb);
5961#endif
5962
5963private:
5964 uint32_t m_Id;
5965};
5966
5967/*
5968Performs defragmentation:
5969
5970- Updates `pBlockVector->m_pMetadata`.
5971- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5972- Does not move actual data, only returns requested moves as `moves`.
5973*/
5974class VmaDefragmentationAlgorithm
5975{
5976 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5977public:
5978 VmaDefragmentationAlgorithm(
5979 VmaAllocator hAllocator,
5980 VmaBlockVector* pBlockVector,
5981 uint32_t currentFrameIndex) :
5982 m_hAllocator(hAllocator),
5983 m_pBlockVector(pBlockVector),
5984 m_CurrentFrameIndex(currentFrameIndex)
5985 {
5986 }
5987 virtual ~VmaDefragmentationAlgorithm()
5988 {
5989 }
5990
5991 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
5992 virtual void AddAll() = 0;
5993
5994 virtual VkResult Defragment(
5995 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5996 VkDeviceSize maxBytesToMove,
5997 uint32_t maxAllocationsToMove) = 0;
5998
5999 virtual VkDeviceSize GetBytesMoved() const = 0;
6000 virtual uint32_t GetAllocationsMoved() const = 0;
6001
6002protected:
6003 VmaAllocator const m_hAllocator;
6004 VmaBlockVector* const m_pBlockVector;
6005 const uint32_t m_CurrentFrameIndex;
6006
6007 struct AllocationInfo
6008 {
6009 VmaAllocation m_hAllocation;
6010 VkBool32* m_pChanged;
6011
6012 AllocationInfo() :
6013 m_hAllocation(VK_NULL_HANDLE),
6014 m_pChanged(VMA_NULL)
6015 {
6016 }
6017 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6018 m_hAllocation(hAlloc),
6019 m_pChanged(pChanged)
6020 {
6021 }
6022 };
6023};
6024
6025class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6026{
6027 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6028public:
6029 VmaDefragmentationAlgorithm_Generic(
6030 VmaAllocator hAllocator,
6031 VmaBlockVector* pBlockVector,
6032 uint32_t currentFrameIndex,
6033 bool overlappingMoveSupported);
6034 virtual ~VmaDefragmentationAlgorithm_Generic();
6035
6036 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6037 virtual void AddAll() { m_AllAllocations = true; }
6038
6039 virtual VkResult Defragment(
6040 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6041 VkDeviceSize maxBytesToMove,
6042 uint32_t maxAllocationsToMove);
6043
6044 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6045 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6046
6047private:
6048 uint32_t m_AllocationCount;
6049 bool m_AllAllocations;
6050
6051 VkDeviceSize m_BytesMoved;
6052 uint32_t m_AllocationsMoved;
6053
6054 struct AllocationInfoSizeGreater
6055 {
6056 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6057 {
6058 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6059 }
6060 };
6061
6062 struct AllocationInfoOffsetGreater
6063 {
6064 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6065 {
6066 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6067 }
6068 };
6069
6070 struct BlockInfo
6071 {
6072 size_t m_OriginalBlockIndex;
6073 VmaDeviceMemoryBlock* m_pBlock;
6074 bool m_HasNonMovableAllocations;
6075 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6076
6077 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6078 m_OriginalBlockIndex(SIZE_MAX),
6079 m_pBlock(VMA_NULL),
6080 m_HasNonMovableAllocations(true),
6081 m_Allocations(pAllocationCallbacks)
6082 {
6083 }
6084
6085 void CalcHasNonMovableAllocations()
6086 {
6087 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6088 const size_t defragmentAllocCount = m_Allocations.size();
6089 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6090 }
6091
6092 void SortAllocationsBySizeDescending()
6093 {
6094 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6095 }
6096
6097 void SortAllocationsByOffsetDescending()
6098 {
6099 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6100 }
6101 };
6102
6103 struct BlockPointerLess
6104 {
6105 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6106 {
6107 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6108 }
6109 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6110 {
6111 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6112 }
6113 };
6114
6115 // 1. Blocks with some non-movable allocations go first.
6116 // 2. Blocks with smaller sumFreeSize go first.
6117 struct BlockInfoCompareMoveDestination
6118 {
6119 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6120 {
6121 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6122 {
6123 return true;
6124 }
6125 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6126 {
6127 return false;
6128 }
6129 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6130 {
6131 return true;
6132 }
6133 return false;
6134 }
6135 };
6136
6137 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6138 BlockInfoVector m_Blocks;
6139
6140 VkResult DefragmentRound(
6141 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6142 VkDeviceSize maxBytesToMove,
6143 uint32_t maxAllocationsToMove);
6144
6145 size_t CalcBlocksWithNonMovableCount() const;
6146
6147 static bool MoveMakesSense(
6148 size_t dstBlockIndex, VkDeviceSize dstOffset,
6149 size_t srcBlockIndex, VkDeviceSize srcOffset);
6150};
6151
6152class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6153{
6154 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6155public:
6156 VmaDefragmentationAlgorithm_Fast(
6157 VmaAllocator hAllocator,
6158 VmaBlockVector* pBlockVector,
6159 uint32_t currentFrameIndex,
6160 bool overlappingMoveSupported);
6161 virtual ~VmaDefragmentationAlgorithm_Fast();
6162
6163 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
6164 virtual void AddAll() { m_AllAllocations = true; }
6165
6166 virtual VkResult Defragment(
6167 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6168 VkDeviceSize maxBytesToMove,
6169 uint32_t maxAllocationsToMove);
6170
6171 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6172 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6173
6174private:
6175 struct BlockInfo
6176 {
6177 size_t origBlockIndex;
6178 };
6179
6180 class FreeSpaceDatabase
6181 {
6182 public:
6183 FreeSpaceDatabase()
6184 {
6185 FreeSpace s = {};
6186 s.blockInfoIndex = SIZE_MAX;
6187 for(size_t i = 0; i < MAX_COUNT; ++i)
6188 {
6189 m_FreeSpaces[i] = s;
6190 }
6191 }
6192
6193 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6194 {
6195 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6196 {
6197 return;
6198 }
6199
6200 // Find first invalid or the smallest structure.
6201 size_t bestIndex = SIZE_MAX;
6202 for(size_t i = 0; i < MAX_COUNT; ++i)
6203 {
6204 // Empty structure.
6205 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6206 {
6207 bestIndex = i;
6208 break;
6209 }
6210 if(m_FreeSpaces[i].size < size &&
6211 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6212 {
6213 bestIndex = i;
6214 }
6215 }
6216
6217 if(bestIndex != SIZE_MAX)
6218 {
6219 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6220 m_FreeSpaces[bestIndex].offset = offset;
6221 m_FreeSpaces[bestIndex].size = size;
6222 }
6223 }
6224
6225 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6226 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6227 {
6228 size_t bestIndex = SIZE_MAX;
6229 VkDeviceSize bestFreeSpaceAfter = 0;
6230 for(size_t i = 0; i < MAX_COUNT; ++i)
6231 {
6232 // Structure is valid.
6233 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6234 {
6235 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6236 // Allocation fits into this structure.
6237 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6238 {
6239 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6240 (dstOffset + size);
6241 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6242 {
6243 bestIndex = i;
6244 bestFreeSpaceAfter = freeSpaceAfter;
6245 }
6246 }
6247 }
6248 }
6249
6250 if(bestIndex != SIZE_MAX)
6251 {
6252 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6253 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6254
6255 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6256 {
6257 // Leave this structure for remaining empty space.
6258 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6259 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6260 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6261 }
6262 else
6263 {
6264 // This structure becomes invalid.
6265 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6266 }
6267
6268 return true;
6269 }
6270
6271 return false;
6272 }
6273
6274 private:
6275 static const size_t MAX_COUNT = 4;
6276
6277 struct FreeSpace
6278 {
6279 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6280 VkDeviceSize offset;
6281 VkDeviceSize size;
6282 } m_FreeSpaces[MAX_COUNT];
6283 };
6284
6285 const bool m_OverlappingMoveSupported;
6286
6287 uint32_t m_AllocationCount;
6288 bool m_AllAllocations;
6289
6290 VkDeviceSize m_BytesMoved;
6291 uint32_t m_AllocationsMoved;
6292
6293 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6294
6295 void PreprocessMetadata();
6296 void PostprocessMetadata();
6297 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6298};
6299
6300struct VmaBlockDefragmentationContext
6301{
6302 enum BLOCK_FLAG
6303 {
6304 BLOCK_FLAG_USED = 0x00000001,
6305 };
6306 uint32_t flags;
6307 VkBuffer hBuffer;
6308
6309 VmaBlockDefragmentationContext() :
6310 flags(0),
6311 hBuffer(VK_NULL_HANDLE)
6312 {
6313 }
6314};
6315
6316class VmaBlockVectorDefragmentationContext
6317{
6318 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6319public:
6320 VkResult res;
6321 bool mutexLocked;
6322 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6323
6324 VmaBlockVectorDefragmentationContext(
6325 VmaAllocator hAllocator,
6326 VmaPool hCustomPool, // Optional.
6327 VmaBlockVector* pBlockVector,
6328 uint32_t currFrameIndex,
6329 uint32_t flags);
6330 ~VmaBlockVectorDefragmentationContext();
6331
6332 VmaPool GetCustomPool() const { return m_hCustomPool; }
6333 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
6334 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6335
6336 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6337 void AddAll() { m_AllAllocations = true; }
6338
6339 void Begin(bool overlappingMoveSupported);
6340
6341private:
6342 const VmaAllocator m_hAllocator;
6343 // Null if not from custom pool.
6344 const VmaPool m_hCustomPool;
6345 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6346 VmaBlockVector* const m_pBlockVector;
6347 const uint32_t m_CurrFrameIndex;
Mike Schuchardte48dc142019-04-18 09:12:03 -07006348 //const uint32_t m_AlgorithmFlags;
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06006349 // Owner of this object.
6350 VmaDefragmentationAlgorithm* m_pAlgorithm;
6351
6352 struct AllocInfo
6353 {
6354 VmaAllocation hAlloc;
6355 VkBool32* pChanged;
6356 };
6357 // Used between constructor and Begin.
6358 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6359 bool m_AllAllocations;
6360};
6361
6362struct VmaDefragmentationContext_T
6363{
6364private:
6365 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6366public:
6367 VmaDefragmentationContext_T(
6368 VmaAllocator hAllocator,
6369 uint32_t currFrameIndex,
6370 uint32_t flags,
6371 VmaDefragmentationStats* pStats);
6372 ~VmaDefragmentationContext_T();
6373
6374 void AddPools(uint32_t poolCount, VmaPool* pPools);
6375 void AddAllocations(
6376 uint32_t allocationCount,
6377 VmaAllocation* pAllocations,
6378 VkBool32* pAllocationsChanged);
6379
6380 /*
6381 Returns:
6382 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6383 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6384 - Negative value if error occured and object can be destroyed immediately.
6385 */
6386 VkResult Defragment(
6387 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6388 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6389 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6390
6391private:
6392 const VmaAllocator m_hAllocator;
6393 const uint32_t m_CurrFrameIndex;
6394 const uint32_t m_Flags;
6395 VmaDefragmentationStats* const m_pStats;
6396 // Owner of these objects.
6397 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6398 // Owner of these objects.
6399 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6400};
6401
6402#if VMA_RECORDING_ENABLED
6403
6404class VmaRecorder
6405{
6406public:
6407 VmaRecorder();
6408 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6409 void WriteConfiguration(
6410 const VkPhysicalDeviceProperties& devProps,
6411 const VkPhysicalDeviceMemoryProperties& memProps,
6412 bool dedicatedAllocationExtensionEnabled);
6413 ~VmaRecorder();
6414
6415 void RecordCreateAllocator(uint32_t frameIndex);
6416 void RecordDestroyAllocator(uint32_t frameIndex);
6417 void RecordCreatePool(uint32_t frameIndex,
6418 const VmaPoolCreateInfo& createInfo,
6419 VmaPool pool);
6420 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6421 void RecordAllocateMemory(uint32_t frameIndex,
6422 const VkMemoryRequirements& vkMemReq,
6423 const VmaAllocationCreateInfo& createInfo,
6424 VmaAllocation allocation);
6425 void RecordAllocateMemoryPages(uint32_t frameIndex,
6426 const VkMemoryRequirements& vkMemReq,
6427 const VmaAllocationCreateInfo& createInfo,
6428 uint64_t allocationCount,
6429 const VmaAllocation* pAllocations);
6430 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6431 const VkMemoryRequirements& vkMemReq,
6432 bool requiresDedicatedAllocation,
6433 bool prefersDedicatedAllocation,
6434 const VmaAllocationCreateInfo& createInfo,
6435 VmaAllocation allocation);
6436 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6437 const VkMemoryRequirements& vkMemReq,
6438 bool requiresDedicatedAllocation,
6439 bool prefersDedicatedAllocation,
6440 const VmaAllocationCreateInfo& createInfo,
6441 VmaAllocation allocation);
6442 void RecordFreeMemory(uint32_t frameIndex,
6443 VmaAllocation allocation);
6444 void RecordFreeMemoryPages(uint32_t frameIndex,
6445 uint64_t allocationCount,
6446 const VmaAllocation* pAllocations);
6447 void RecordResizeAllocation(
6448 uint32_t frameIndex,
6449 VmaAllocation allocation,
6450 VkDeviceSize newSize);
6451 void RecordSetAllocationUserData(uint32_t frameIndex,
6452 VmaAllocation allocation,
6453 const void* pUserData);
6454 void RecordCreateLostAllocation(uint32_t frameIndex,
6455 VmaAllocation allocation);
6456 void RecordMapMemory(uint32_t frameIndex,
6457 VmaAllocation allocation);
6458 void RecordUnmapMemory(uint32_t frameIndex,
6459 VmaAllocation allocation);
6460 void RecordFlushAllocation(uint32_t frameIndex,
6461 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6462 void RecordInvalidateAllocation(uint32_t frameIndex,
6463 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6464 void RecordCreateBuffer(uint32_t frameIndex,
6465 const VkBufferCreateInfo& bufCreateInfo,
6466 const VmaAllocationCreateInfo& allocCreateInfo,
6467 VmaAllocation allocation);
6468 void RecordCreateImage(uint32_t frameIndex,
6469 const VkImageCreateInfo& imageCreateInfo,
6470 const VmaAllocationCreateInfo& allocCreateInfo,
6471 VmaAllocation allocation);
6472 void RecordDestroyBuffer(uint32_t frameIndex,
6473 VmaAllocation allocation);
6474 void RecordDestroyImage(uint32_t frameIndex,
6475 VmaAllocation allocation);
6476 void RecordTouchAllocation(uint32_t frameIndex,
6477 VmaAllocation allocation);
6478 void RecordGetAllocationInfo(uint32_t frameIndex,
6479 VmaAllocation allocation);
6480 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6481 VmaPool pool);
6482 void RecordDefragmentationBegin(uint32_t frameIndex,
6483 const VmaDefragmentationInfo2& info,
6484 VmaDefragmentationContext ctx);
6485 void RecordDefragmentationEnd(uint32_t frameIndex,
6486 VmaDefragmentationContext ctx);
6487
6488private:
6489 struct CallParams
6490 {
6491 uint32_t threadId;
6492 double time;
6493 };
6494
6495 class UserDataString
6496 {
6497 public:
6498 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
6499 const char* GetString() const { return m_Str; }
6500
6501 private:
6502 char m_PtrStr[17];
6503 const char* m_Str;
6504 };
6505
6506 bool m_UseMutex;
6507 VmaRecordFlags m_Flags;
6508 FILE* m_File;
6509 VMA_MUTEX m_FileMutex;
6510 int64_t m_Freq;
6511 int64_t m_StartCounter;
6512
6513 void GetBasicParams(CallParams& outParams);
6514
6515 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6516 template<typename T>
6517 void PrintPointerList(uint64_t count, const T* pItems)
6518 {
6519 if(count)
6520 {
6521 fprintf(m_File, "%p", pItems[0]);
6522 for(uint64_t i = 1; i < count; ++i)
6523 {
6524 fprintf(m_File, " %p", pItems[i]);
6525 }
6526 }
6527 }
6528
6529 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6530 void Flush();
6531};
6532
6533#endif // #if VMA_RECORDING_ENABLED
6534
6535// Main allocator object.
6536struct VmaAllocator_T
6537{
6538 VMA_CLASS_NO_COPY(VmaAllocator_T)
6539public:
6540 bool m_UseMutex;
6541 bool m_UseKhrDedicatedAllocation;
6542 VkDevice m_hDevice;
6543 bool m_AllocationCallbacksSpecified;
6544 VkAllocationCallbacks m_AllocationCallbacks;
6545 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6546
6547 // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6548 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6549 VMA_MUTEX m_HeapSizeLimitMutex;
6550
6551 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6552 VkPhysicalDeviceMemoryProperties m_MemProps;
6553
6554 // Default pools.
6555 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6556
6557 // Each vector is sorted by memory (handle value).
6558 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6559 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6560 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6561
6562 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6563 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6564 ~VmaAllocator_T();
6565
6566 const VkAllocationCallbacks* GetAllocationCallbacks() const
6567 {
6568 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6569 }
6570 const VmaVulkanFunctions& GetVulkanFunctions() const
6571 {
6572 return m_VulkanFunctions;
6573 }
6574
6575 VkDeviceSize GetBufferImageGranularity() const
6576 {
6577 return VMA_MAX(
6578 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6579 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6580 }
6581
6582 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
6583 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6584
6585 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6586 {
6587 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6588 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6589 }
6590 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
6591 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6592 {
6593 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6594 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6595 }
6596 // Minimum alignment for all allocations in specific memory type.
6597 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6598 {
6599 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6600 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6601 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6602 }
6603
6604 bool IsIntegratedGpu() const
6605 {
6606 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6607 }
6608
6609#if VMA_RECORDING_ENABLED
6610 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6611#endif
6612
6613 void GetBufferMemoryRequirements(
6614 VkBuffer hBuffer,
6615 VkMemoryRequirements& memReq,
6616 bool& requiresDedicatedAllocation,
6617 bool& prefersDedicatedAllocation) const;
6618 void GetImageMemoryRequirements(
6619 VkImage hImage,
6620 VkMemoryRequirements& memReq,
6621 bool& requiresDedicatedAllocation,
6622 bool& prefersDedicatedAllocation) const;
6623
6624 // Main allocation function.
6625 VkResult AllocateMemory(
6626 const VkMemoryRequirements& vkMemReq,
6627 bool requiresDedicatedAllocation,
6628 bool prefersDedicatedAllocation,
6629 VkBuffer dedicatedBuffer,
6630 VkImage dedicatedImage,
6631 const VmaAllocationCreateInfo& createInfo,
6632 VmaSuballocationType suballocType,
6633 size_t allocationCount,
6634 VmaAllocation* pAllocations);
6635
6636 // Main deallocation function.
6637 void FreeMemory(
6638 size_t allocationCount,
6639 const VmaAllocation* pAllocations);
6640
6641 VkResult ResizeAllocation(
6642 const VmaAllocation alloc,
6643 VkDeviceSize newSize);
6644
6645 void CalculateStats(VmaStats* pStats);
6646
6647#if VMA_STATS_STRING_ENABLED
6648 void PrintDetailedMap(class VmaJsonWriter& json);
6649#endif
6650
6651 VkResult DefragmentationBegin(
6652 const VmaDefragmentationInfo2& info,
6653 VmaDefragmentationStats* pStats,
6654 VmaDefragmentationContext* pContext);
6655 VkResult DefragmentationEnd(
6656 VmaDefragmentationContext context);
6657
6658 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6659 bool TouchAllocation(VmaAllocation hAllocation);
6660
6661 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6662 void DestroyPool(VmaPool pool);
6663 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6664
6665 void SetCurrentFrameIndex(uint32_t frameIndex);
6666 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6667
6668 void MakePoolAllocationsLost(
6669 VmaPool hPool,
6670 size_t* pLostAllocationCount);
6671 VkResult CheckPoolCorruption(VmaPool hPool);
6672 VkResult CheckCorruption(uint32_t memoryTypeBits);
6673
6674 void CreateLostAllocation(VmaAllocation* pAllocation);
6675
6676 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6677 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6678
6679 VkResult Map(VmaAllocation hAllocation, void** ppData);
6680 void Unmap(VmaAllocation hAllocation);
6681
6682 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6683 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6684
6685 void FlushOrInvalidateAllocation(
6686 VmaAllocation hAllocation,
6687 VkDeviceSize offset, VkDeviceSize size,
6688 VMA_CACHE_OPERATION op);
6689
6690 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6691
6692private:
6693 VkDeviceSize m_PreferredLargeHeapBlockSize;
6694
6695 VkPhysicalDevice m_PhysicalDevice;
6696 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6697
6698 VMA_RW_MUTEX m_PoolsMutex;
6699 // Protected by m_PoolsMutex. Sorted by pointer value.
6700 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6701 uint32_t m_NextPoolId;
6702
6703 VmaVulkanFunctions m_VulkanFunctions;
6704
6705#if VMA_RECORDING_ENABLED
6706 VmaRecorder* m_pRecorder;
6707#endif
6708
6709 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6710
6711 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6712
6713 VkResult AllocateMemoryOfType(
6714 VkDeviceSize size,
6715 VkDeviceSize alignment,
6716 bool dedicatedAllocation,
6717 VkBuffer dedicatedBuffer,
6718 VkImage dedicatedImage,
6719 const VmaAllocationCreateInfo& createInfo,
6720 uint32_t memTypeIndex,
6721 VmaSuballocationType suballocType,
6722 size_t allocationCount,
6723 VmaAllocation* pAllocations);
6724
6725 // Helper function only to be used inside AllocateDedicatedMemory.
6726 VkResult AllocateDedicatedMemoryPage(
6727 VkDeviceSize size,
6728 VmaSuballocationType suballocType,
6729 uint32_t memTypeIndex,
6730 const VkMemoryAllocateInfo& allocInfo,
6731 bool map,
6732 bool isUserDataString,
6733 void* pUserData,
6734 VmaAllocation* pAllocation);
6735
6736 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6737 VkResult AllocateDedicatedMemory(
6738 VkDeviceSize size,
6739 VmaSuballocationType suballocType,
6740 uint32_t memTypeIndex,
6741 bool map,
6742 bool isUserDataString,
6743 void* pUserData,
6744 VkBuffer dedicatedBuffer,
6745 VkImage dedicatedImage,
6746 size_t allocationCount,
6747 VmaAllocation* pAllocations);
6748
6749 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6750 void FreeDedicatedMemory(VmaAllocation allocation);
6751};
6752
6753////////////////////////////////////////////////////////////////////////////////
6754// Memory allocation #2 after VmaAllocator_T definition
6755
6756static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6757{
6758 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6759}
6760
6761static void VmaFree(VmaAllocator hAllocator, void* ptr)
6762{
6763 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6764}
6765
6766template<typename T>
6767static T* VmaAllocate(VmaAllocator hAllocator)
6768{
6769 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6770}
6771
6772template<typename T>
6773static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6774{
6775 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6776}
6777
6778template<typename T>
6779static void vma_delete(VmaAllocator hAllocator, T* ptr)
6780{
6781 if(ptr != VMA_NULL)
6782 {
6783 ptr->~T();
6784 VmaFree(hAllocator, ptr);
6785 }
6786}
6787
6788template<typename T>
6789static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6790{
6791 if(ptr != VMA_NULL)
6792 {
6793 for(size_t i = count; i--; )
6794 ptr[i].~T();
6795 VmaFree(hAllocator, ptr);
6796 }
6797}
6798
6799////////////////////////////////////////////////////////////////////////////////
6800// VmaStringBuilder
6801
6802#if VMA_STATS_STRING_ENABLED
6803
6804class VmaStringBuilder
6805{
6806public:
6807 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
6808 size_t GetLength() const { return m_Data.size(); }
6809 const char* GetData() const { return m_Data.data(); }
6810
6811 void Add(char ch) { m_Data.push_back(ch); }
6812 void Add(const char* pStr);
6813 void AddNewLine() { Add('\n'); }
6814 void AddNumber(uint32_t num);
6815 void AddNumber(uint64_t num);
6816 void AddPointer(const void* ptr);
6817
6818private:
6819 VmaVector< char, VmaStlAllocator<char> > m_Data;
6820};
6821
6822void VmaStringBuilder::Add(const char* pStr)
6823{
6824 const size_t strLen = strlen(pStr);
6825 if(strLen > 0)
6826 {
6827 const size_t oldCount = m_Data.size();
6828 m_Data.resize(oldCount + strLen);
6829 memcpy(m_Data.data() + oldCount, pStr, strLen);
6830 }
6831}
6832
6833void VmaStringBuilder::AddNumber(uint32_t num)
6834{
6835 char buf[11];
6836 VmaUint32ToStr(buf, sizeof(buf), num);
6837 Add(buf);
6838}
6839
6840void VmaStringBuilder::AddNumber(uint64_t num)
6841{
6842 char buf[21];
6843 VmaUint64ToStr(buf, sizeof(buf), num);
6844 Add(buf);
6845}
6846
6847void VmaStringBuilder::AddPointer(const void* ptr)
6848{
6849 char buf[21];
6850 VmaPtrToStr(buf, sizeof(buf), ptr);
6851 Add(buf);
6852}
6853
6854#endif // #if VMA_STATS_STRING_ENABLED
6855
6856////////////////////////////////////////////////////////////////////////////////
6857// VmaJsonWriter
6858
6859#if VMA_STATS_STRING_ENABLED
6860
6861class VmaJsonWriter
6862{
6863 VMA_CLASS_NO_COPY(VmaJsonWriter)
6864public:
6865 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6866 ~VmaJsonWriter();
6867
6868 void BeginObject(bool singleLine = false);
6869 void EndObject();
6870
6871 void BeginArray(bool singleLine = false);
6872 void EndArray();
6873
6874 void WriteString(const char* pStr);
6875 void BeginString(const char* pStr = VMA_NULL);
6876 void ContinueString(const char* pStr);
6877 void ContinueString(uint32_t n);
6878 void ContinueString(uint64_t n);
6879 void ContinueString_Pointer(const void* ptr);
6880 void EndString(const char* pStr = VMA_NULL);
6881
6882 void WriteNumber(uint32_t n);
6883 void WriteNumber(uint64_t n);
6884 void WriteBool(bool b);
6885 void WriteNull();
6886
6887private:
6888 static const char* const INDENT;
6889
6890 enum COLLECTION_TYPE
6891 {
6892 COLLECTION_TYPE_OBJECT,
6893 COLLECTION_TYPE_ARRAY,
6894 };
6895 struct StackItem
6896 {
6897 COLLECTION_TYPE type;
6898 uint32_t valueCount;
6899 bool singleLineMode;
6900 };
6901
6902 VmaStringBuilder& m_SB;
6903 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6904 bool m_InsideString;
6905
6906 void BeginValue(bool isString);
6907 void WriteIndent(bool oneLess = false);
6908};
6909
6910const char* const VmaJsonWriter::INDENT = " ";
6911
6912VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6913 m_SB(sb),
6914 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6915 m_InsideString(false)
6916{
6917}
6918
6919VmaJsonWriter::~VmaJsonWriter()
6920{
6921 VMA_ASSERT(!m_InsideString);
6922 VMA_ASSERT(m_Stack.empty());
6923}
6924
6925void VmaJsonWriter::BeginObject(bool singleLine)
6926{
6927 VMA_ASSERT(!m_InsideString);
6928
6929 BeginValue(false);
6930 m_SB.Add('{');
6931
6932 StackItem item;
6933 item.type = COLLECTION_TYPE_OBJECT;
6934 item.valueCount = 0;
6935 item.singleLineMode = singleLine;
6936 m_Stack.push_back(item);
6937}
6938
6939void VmaJsonWriter::EndObject()
6940{
6941 VMA_ASSERT(!m_InsideString);
6942
6943 WriteIndent(true);
6944 m_SB.Add('}');
6945
6946 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6947 m_Stack.pop_back();
6948}
6949
6950void VmaJsonWriter::BeginArray(bool singleLine)
6951{
6952 VMA_ASSERT(!m_InsideString);
6953
6954 BeginValue(false);
6955 m_SB.Add('[');
6956
6957 StackItem item;
6958 item.type = COLLECTION_TYPE_ARRAY;
6959 item.valueCount = 0;
6960 item.singleLineMode = singleLine;
6961 m_Stack.push_back(item);
6962}
6963
6964void VmaJsonWriter::EndArray()
6965{
6966 VMA_ASSERT(!m_InsideString);
6967
6968 WriteIndent(true);
6969 m_SB.Add(']');
6970
6971 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6972 m_Stack.pop_back();
6973}
6974
6975void VmaJsonWriter::WriteString(const char* pStr)
6976{
6977 BeginString(pStr);
6978 EndString();
6979}
6980
6981void VmaJsonWriter::BeginString(const char* pStr)
6982{
6983 VMA_ASSERT(!m_InsideString);
6984
6985 BeginValue(true);
6986 m_SB.Add('"');
6987 m_InsideString = true;
6988 if(pStr != VMA_NULL && pStr[0] != '\0')
6989 {
6990 ContinueString(pStr);
6991 }
6992}
6993
6994void VmaJsonWriter::ContinueString(const char* pStr)
6995{
6996 VMA_ASSERT(m_InsideString);
6997
6998 const size_t strLen = strlen(pStr);
6999 for(size_t i = 0; i < strLen; ++i)
7000 {
7001 char ch = pStr[i];
7002 if(ch == '\\')
7003 {
7004 m_SB.Add("\\\\");
7005 }
7006 else if(ch == '"')
7007 {
7008 m_SB.Add("\\\"");
7009 }
7010 else if(ch >= 32)
7011 {
7012 m_SB.Add(ch);
7013 }
7014 else switch(ch)
7015 {
7016 case '\b':
7017 m_SB.Add("\\b");
7018 break;
7019 case '\f':
7020 m_SB.Add("\\f");
7021 break;
7022 case '\n':
7023 m_SB.Add("\\n");
7024 break;
7025 case '\r':
7026 m_SB.Add("\\r");
7027 break;
7028 case '\t':
7029 m_SB.Add("\\t");
7030 break;
7031 default:
7032 VMA_ASSERT(0 && "Character not currently supported.");
7033 break;
7034 }
7035 }
7036}
7037
7038void VmaJsonWriter::ContinueString(uint32_t n)
7039{
7040 VMA_ASSERT(m_InsideString);
7041 m_SB.AddNumber(n);
7042}
7043
7044void VmaJsonWriter::ContinueString(uint64_t n)
7045{
7046 VMA_ASSERT(m_InsideString);
7047 m_SB.AddNumber(n);
7048}
7049
7050void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7051{
7052 VMA_ASSERT(m_InsideString);
7053 m_SB.AddPointer(ptr);
7054}
7055
7056void VmaJsonWriter::EndString(const char* pStr)
7057{
7058 VMA_ASSERT(m_InsideString);
7059 if(pStr != VMA_NULL && pStr[0] != '\0')
7060 {
7061 ContinueString(pStr);
7062 }
7063 m_SB.Add('"');
7064 m_InsideString = false;
7065}
7066
7067void VmaJsonWriter::WriteNumber(uint32_t n)
7068{
7069 VMA_ASSERT(!m_InsideString);
7070 BeginValue(false);
7071 m_SB.AddNumber(n);
7072}
7073
7074void VmaJsonWriter::WriteNumber(uint64_t n)
7075{
7076 VMA_ASSERT(!m_InsideString);
7077 BeginValue(false);
7078 m_SB.AddNumber(n);
7079}
7080
7081void VmaJsonWriter::WriteBool(bool b)
7082{
7083 VMA_ASSERT(!m_InsideString);
7084 BeginValue(false);
7085 m_SB.Add(b ? "true" : "false");
7086}
7087
7088void VmaJsonWriter::WriteNull()
7089{
7090 VMA_ASSERT(!m_InsideString);
7091 BeginValue(false);
7092 m_SB.Add("null");
7093}
7094
7095void VmaJsonWriter::BeginValue(bool isString)
7096{
7097 if(!m_Stack.empty())
7098 {
7099 StackItem& currItem = m_Stack.back();
7100 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7101 currItem.valueCount % 2 == 0)
7102 {
7103 VMA_ASSERT(isString);
7104 }
7105
7106 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7107 currItem.valueCount % 2 != 0)
7108 {
7109 m_SB.Add(": ");
7110 }
7111 else if(currItem.valueCount > 0)
7112 {
7113 m_SB.Add(", ");
7114 WriteIndent();
7115 }
7116 else
7117 {
7118 WriteIndent();
7119 }
7120 ++currItem.valueCount;
7121 }
7122}
7123
7124void VmaJsonWriter::WriteIndent(bool oneLess)
7125{
7126 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7127 {
7128 m_SB.AddNewLine();
7129
7130 size_t count = m_Stack.size();
7131 if(count > 0 && oneLess)
7132 {
7133 --count;
7134 }
7135 for(size_t i = 0; i < count; ++i)
7136 {
7137 m_SB.Add(INDENT);
7138 }
7139 }
7140}
7141
7142#endif // #if VMA_STATS_STRING_ENABLED
7143
7144////////////////////////////////////////////////////////////////////////////////
7145
7146void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7147{
7148 if(IsUserDataString())
7149 {
7150 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7151
7152 FreeUserDataString(hAllocator);
7153
7154 if(pUserData != VMA_NULL)
7155 {
7156 const char* const newStrSrc = (char*)pUserData;
7157 const size_t newStrLen = strlen(newStrSrc);
7158 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7159 memcpy(newStrDst, newStrSrc, newStrLen + 1);
7160 m_pUserData = newStrDst;
7161 }
7162 }
7163 else
7164 {
7165 m_pUserData = pUserData;
7166 }
7167}
7168
7169void VmaAllocation_T::ChangeBlockAllocation(
7170 VmaAllocator hAllocator,
7171 VmaDeviceMemoryBlock* block,
7172 VkDeviceSize offset)
7173{
7174 VMA_ASSERT(block != VMA_NULL);
7175 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7176
7177 // Move mapping reference counter from old block to new block.
7178 if(block != m_BlockAllocation.m_Block)
7179 {
7180 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7181 if(IsPersistentMap())
7182 ++mapRefCount;
7183 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7184 block->Map(hAllocator, mapRefCount, VMA_NULL);
7185 }
7186
7187 m_BlockAllocation.m_Block = block;
7188 m_BlockAllocation.m_Offset = offset;
7189}
7190
7191void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7192{
7193 VMA_ASSERT(newSize > 0);
7194 m_Size = newSize;
7195}
7196
7197void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7198{
7199 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7200 m_BlockAllocation.m_Offset = newOffset;
7201}
7202
7203VkDeviceSize VmaAllocation_T::GetOffset() const
7204{
7205 switch(m_Type)
7206 {
7207 case ALLOCATION_TYPE_BLOCK:
7208 return m_BlockAllocation.m_Offset;
7209 case ALLOCATION_TYPE_DEDICATED:
7210 return 0;
7211 default:
7212 VMA_ASSERT(0);
7213 return 0;
7214 }
7215}
7216
7217VkDeviceMemory VmaAllocation_T::GetMemory() const
7218{
7219 switch(m_Type)
7220 {
7221 case ALLOCATION_TYPE_BLOCK:
7222 return m_BlockAllocation.m_Block->GetDeviceMemory();
7223 case ALLOCATION_TYPE_DEDICATED:
7224 return m_DedicatedAllocation.m_hMemory;
7225 default:
7226 VMA_ASSERT(0);
7227 return VK_NULL_HANDLE;
7228 }
7229}
7230
7231uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7232{
7233 switch(m_Type)
7234 {
7235 case ALLOCATION_TYPE_BLOCK:
7236 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7237 case ALLOCATION_TYPE_DEDICATED:
7238 return m_DedicatedAllocation.m_MemoryTypeIndex;
7239 default:
7240 VMA_ASSERT(0);
7241 return UINT32_MAX;
7242 }
7243}
7244
7245void* VmaAllocation_T::GetMappedData() const
7246{
7247 switch(m_Type)
7248 {
7249 case ALLOCATION_TYPE_BLOCK:
7250 if(m_MapCount != 0)
7251 {
7252 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7253 VMA_ASSERT(pBlockData != VMA_NULL);
7254 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7255 }
7256 else
7257 {
7258 return VMA_NULL;
7259 }
7260 break;
7261 case ALLOCATION_TYPE_DEDICATED:
7262 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7263 return m_DedicatedAllocation.m_pMappedData;
7264 default:
7265 VMA_ASSERT(0);
7266 return VMA_NULL;
7267 }
7268}
7269
7270bool VmaAllocation_T::CanBecomeLost() const
7271{
7272 switch(m_Type)
7273 {
7274 case ALLOCATION_TYPE_BLOCK:
7275 return m_BlockAllocation.m_CanBecomeLost;
7276 case ALLOCATION_TYPE_DEDICATED:
7277 return false;
7278 default:
7279 VMA_ASSERT(0);
7280 return false;
7281 }
7282}
7283
7284VmaPool VmaAllocation_T::GetPool() const
7285{
7286 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7287 return m_BlockAllocation.m_hPool;
7288}
7289
7290bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7291{
7292 VMA_ASSERT(CanBecomeLost());
7293
7294 /*
7295 Warning: This is a carefully designed algorithm.
7296 Do not modify unless you really know what you're doing :)
7297 */
7298 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7299 for(;;)
7300 {
7301 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7302 {
7303 VMA_ASSERT(0);
7304 return false;
7305 }
7306 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7307 {
7308 return false;
7309 }
7310 else // Last use time earlier than current time.
7311 {
7312 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7313 {
7314 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7315 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7316 return true;
7317 }
7318 }
7319 }
7320}
7321
7322#if VMA_STATS_STRING_ENABLED
7323
7324// Correspond to values of enum VmaSuballocationType.
7325static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7326 "FREE",
7327 "UNKNOWN",
7328 "BUFFER",
7329 "IMAGE_UNKNOWN",
7330 "IMAGE_LINEAR",
7331 "IMAGE_OPTIMAL",
7332};
7333
7334void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7335{
7336 json.WriteString("Type");
7337 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7338
7339 json.WriteString("Size");
7340 json.WriteNumber(m_Size);
7341
7342 if(m_pUserData != VMA_NULL)
7343 {
7344 json.WriteString("UserData");
7345 if(IsUserDataString())
7346 {
7347 json.WriteString((const char*)m_pUserData);
7348 }
7349 else
7350 {
7351 json.BeginString();
7352 json.ContinueString_Pointer(m_pUserData);
7353 json.EndString();
7354 }
7355 }
7356
7357 json.WriteString("CreationFrameIndex");
7358 json.WriteNumber(m_CreationFrameIndex);
7359
7360 json.WriteString("LastUseFrameIndex");
7361 json.WriteNumber(GetLastUseFrameIndex());
7362
7363 if(m_BufferImageUsage != 0)
7364 {
7365 json.WriteString("Usage");
7366 json.WriteNumber(m_BufferImageUsage);
7367 }
7368}
7369
7370#endif
7371
7372void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7373{
7374 VMA_ASSERT(IsUserDataString());
7375 if(m_pUserData != VMA_NULL)
7376 {
7377 char* const oldStr = (char*)m_pUserData;
7378 const size_t oldStrLen = strlen(oldStr);
7379 vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7380 m_pUserData = VMA_NULL;
7381 }
7382}
7383
7384void VmaAllocation_T::BlockAllocMap()
7385{
7386 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7387
7388 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7389 {
7390 ++m_MapCount;
7391 }
7392 else
7393 {
7394 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7395 }
7396}
7397
7398void VmaAllocation_T::BlockAllocUnmap()
7399{
7400 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7401
7402 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7403 {
7404 --m_MapCount;
7405 }
7406 else
7407 {
7408 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7409 }
7410}
7411
7412VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7413{
7414 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7415
7416 if(m_MapCount != 0)
7417 {
7418 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7419 {
7420 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7421 *ppData = m_DedicatedAllocation.m_pMappedData;
7422 ++m_MapCount;
7423 return VK_SUCCESS;
7424 }
7425 else
7426 {
7427 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7428 return VK_ERROR_MEMORY_MAP_FAILED;
7429 }
7430 }
7431 else
7432 {
7433 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7434 hAllocator->m_hDevice,
7435 m_DedicatedAllocation.m_hMemory,
7436 0, // offset
7437 VK_WHOLE_SIZE,
7438 0, // flags
7439 ppData);
7440 if(result == VK_SUCCESS)
7441 {
7442 m_DedicatedAllocation.m_pMappedData = *ppData;
7443 m_MapCount = 1;
7444 }
7445 return result;
7446 }
7447}
7448
7449void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7450{
7451 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7452
7453 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7454 {
7455 --m_MapCount;
7456 if(m_MapCount == 0)
7457 {
7458 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7459 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7460 hAllocator->m_hDevice,
7461 m_DedicatedAllocation.m_hMemory);
7462 }
7463 }
7464 else
7465 {
7466 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7467 }
7468}
7469
7470#if VMA_STATS_STRING_ENABLED
7471
7472static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7473{
7474 json.BeginObject();
7475
7476 json.WriteString("Blocks");
7477 json.WriteNumber(stat.blockCount);
7478
7479 json.WriteString("Allocations");
7480 json.WriteNumber(stat.allocationCount);
7481
7482 json.WriteString("UnusedRanges");
7483 json.WriteNumber(stat.unusedRangeCount);
7484
7485 json.WriteString("UsedBytes");
7486 json.WriteNumber(stat.usedBytes);
7487
7488 json.WriteString("UnusedBytes");
7489 json.WriteNumber(stat.unusedBytes);
7490
7491 if(stat.allocationCount > 1)
7492 {
7493 json.WriteString("AllocationSize");
7494 json.BeginObject(true);
7495 json.WriteString("Min");
7496 json.WriteNumber(stat.allocationSizeMin);
7497 json.WriteString("Avg");
7498 json.WriteNumber(stat.allocationSizeAvg);
7499 json.WriteString("Max");
7500 json.WriteNumber(stat.allocationSizeMax);
7501 json.EndObject();
7502 }
7503
7504 if(stat.unusedRangeCount > 1)
7505 {
7506 json.WriteString("UnusedRangeSize");
7507 json.BeginObject(true);
7508 json.WriteString("Min");
7509 json.WriteNumber(stat.unusedRangeSizeMin);
7510 json.WriteString("Avg");
7511 json.WriteNumber(stat.unusedRangeSizeAvg);
7512 json.WriteString("Max");
7513 json.WriteNumber(stat.unusedRangeSizeMax);
7514 json.EndObject();
7515 }
7516
7517 json.EndObject();
7518}
7519
7520#endif // #if VMA_STATS_STRING_ENABLED
7521
7522struct VmaSuballocationItemSizeLess
7523{
7524 bool operator()(
7525 const VmaSuballocationList::iterator lhs,
7526 const VmaSuballocationList::iterator rhs) const
7527 {
7528 return lhs->size < rhs->size;
7529 }
7530 bool operator()(
7531 const VmaSuballocationList::iterator lhs,
7532 VkDeviceSize rhsSize) const
7533 {
7534 return lhs->size < rhsSize;
7535 }
7536};
7537
7538
7539////////////////////////////////////////////////////////////////////////////////
7540// class VmaBlockMetadata
7541
7542VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7543 m_Size(0),
7544 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7545{
7546}
7547
7548#if VMA_STATS_STRING_ENABLED
7549
7550void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7551 VkDeviceSize unusedBytes,
7552 size_t allocationCount,
7553 size_t unusedRangeCount) const
7554{
7555 json.BeginObject();
7556
7557 json.WriteString("TotalBytes");
7558 json.WriteNumber(GetSize());
7559
7560 json.WriteString("UnusedBytes");
7561 json.WriteNumber(unusedBytes);
7562
7563 json.WriteString("Allocations");
7564 json.WriteNumber((uint64_t)allocationCount);
7565
7566 json.WriteString("UnusedRanges");
7567 json.WriteNumber((uint64_t)unusedRangeCount);
7568
7569 json.WriteString("Suballocations");
7570 json.BeginArray();
7571}
7572
7573void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7574 VkDeviceSize offset,
7575 VmaAllocation hAllocation) const
7576{
7577 json.BeginObject(true);
7578
7579 json.WriteString("Offset");
7580 json.WriteNumber(offset);
7581
7582 hAllocation->PrintParameters(json);
7583
7584 json.EndObject();
7585}
7586
7587void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7588 VkDeviceSize offset,
7589 VkDeviceSize size) const
7590{
7591 json.BeginObject(true);
7592
7593 json.WriteString("Offset");
7594 json.WriteNumber(offset);
7595
7596 json.WriteString("Type");
7597 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7598
7599 json.WriteString("Size");
7600 json.WriteNumber(size);
7601
7602 json.EndObject();
7603}
7604
7605void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7606{
7607 json.EndArray();
7608 json.EndObject();
7609}
7610
7611#endif // #if VMA_STATS_STRING_ENABLED
7612
7613////////////////////////////////////////////////////////////////////////////////
7614// class VmaBlockMetadata_Generic
7615
7616VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7617 VmaBlockMetadata(hAllocator),
7618 m_FreeCount(0),
7619 m_SumFreeSize(0),
7620 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7621 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7622{
7623}
7624
7625VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7626{
7627}
7628
7629void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7630{
7631 VmaBlockMetadata::Init(size);
7632
7633 m_FreeCount = 1;
7634 m_SumFreeSize = size;
7635
7636 VmaSuballocation suballoc = {};
7637 suballoc.offset = 0;
7638 suballoc.size = size;
7639 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7640 suballoc.hAllocation = VK_NULL_HANDLE;
7641
7642 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7643 m_Suballocations.push_back(suballoc);
7644 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7645 --suballocItem;
7646 m_FreeSuballocationsBySize.push_back(suballocItem);
7647}
7648
7649bool VmaBlockMetadata_Generic::Validate() const
7650{
7651 VMA_VALIDATE(!m_Suballocations.empty());
7652
7653 // Expected offset of new suballocation as calculated from previous ones.
7654 VkDeviceSize calculatedOffset = 0;
7655 // Expected number of free suballocations as calculated from traversing their list.
7656 uint32_t calculatedFreeCount = 0;
7657 // Expected sum size of free suballocations as calculated from traversing their list.
7658 VkDeviceSize calculatedSumFreeSize = 0;
7659 // Expected number of free suballocations that should be registered in
7660 // m_FreeSuballocationsBySize calculated from traversing their list.
7661 size_t freeSuballocationsToRegister = 0;
7662 // True if previous visited suballocation was free.
7663 bool prevFree = false;
7664
7665 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7666 suballocItem != m_Suballocations.cend();
7667 ++suballocItem)
7668 {
7669 const VmaSuballocation& subAlloc = *suballocItem;
7670
7671 // Actual offset of this suballocation doesn't match expected one.
7672 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7673
7674 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7675 // Two adjacent free suballocations are invalid. They should be merged.
7676 VMA_VALIDATE(!prevFree || !currFree);
7677
7678 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7679
7680 if(currFree)
7681 {
7682 calculatedSumFreeSize += subAlloc.size;
7683 ++calculatedFreeCount;
7684 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7685 {
7686 ++freeSuballocationsToRegister;
7687 }
7688
7689 // Margin required between allocations - every free space must be at least that large.
7690 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7691 }
7692 else
7693 {
7694 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7695 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7696
7697 // Margin required between allocations - previous allocation must be free.
7698 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7699 }
7700
7701 calculatedOffset += subAlloc.size;
7702 prevFree = currFree;
7703 }
7704
7705 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7706 // match expected one.
7707 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7708
7709 VkDeviceSize lastSize = 0;
7710 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7711 {
7712 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7713
7714 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7715 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7716 // They must be sorted by size ascending.
7717 VMA_VALIDATE(suballocItem->size >= lastSize);
7718
7719 lastSize = suballocItem->size;
7720 }
7721
7722 // Check if totals match calculacted values.
7723 VMA_VALIDATE(ValidateFreeSuballocationList());
7724 VMA_VALIDATE(calculatedOffset == GetSize());
7725 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7726 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7727
7728 return true;
7729}
7730
7731VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7732{
7733 if(!m_FreeSuballocationsBySize.empty())
7734 {
7735 return m_FreeSuballocationsBySize.back()->size;
7736 }
7737 else
7738 {
7739 return 0;
7740 }
7741}
7742
7743bool VmaBlockMetadata_Generic::IsEmpty() const
7744{
7745 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7746}
7747
7748void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7749{
7750 outInfo.blockCount = 1;
7751
7752 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7753 outInfo.allocationCount = rangeCount - m_FreeCount;
7754 outInfo.unusedRangeCount = m_FreeCount;
7755
7756 outInfo.unusedBytes = m_SumFreeSize;
7757 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7758
7759 outInfo.allocationSizeMin = UINT64_MAX;
7760 outInfo.allocationSizeMax = 0;
7761 outInfo.unusedRangeSizeMin = UINT64_MAX;
7762 outInfo.unusedRangeSizeMax = 0;
7763
7764 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7765 suballocItem != m_Suballocations.cend();
7766 ++suballocItem)
7767 {
7768 const VmaSuballocation& suballoc = *suballocItem;
7769 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7770 {
7771 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7772 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7773 }
7774 else
7775 {
7776 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7777 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7778 }
7779 }
7780}
7781
7782void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7783{
7784 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7785
7786 inoutStats.size += GetSize();
7787 inoutStats.unusedSize += m_SumFreeSize;
7788 inoutStats.allocationCount += rangeCount - m_FreeCount;
7789 inoutStats.unusedRangeCount += m_FreeCount;
7790 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7791}
7792
7793#if VMA_STATS_STRING_ENABLED
7794
7795void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7796{
7797 PrintDetailedMap_Begin(json,
7798 m_SumFreeSize, // unusedBytes
7799 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7800 m_FreeCount); // unusedRangeCount
7801
7802 size_t i = 0;
7803 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7804 suballocItem != m_Suballocations.cend();
7805 ++suballocItem, ++i)
7806 {
7807 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7808 {
7809 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7810 }
7811 else
7812 {
7813 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7814 }
7815 }
7816
7817 PrintDetailedMap_End(json);
7818}
7819
7820#endif // #if VMA_STATS_STRING_ENABLED
7821
7822bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7823 uint32_t currentFrameIndex,
7824 uint32_t frameInUseCount,
7825 VkDeviceSize bufferImageGranularity,
7826 VkDeviceSize allocSize,
7827 VkDeviceSize allocAlignment,
7828 bool upperAddress,
7829 VmaSuballocationType allocType,
7830 bool canMakeOtherLost,
7831 uint32_t strategy,
7832 VmaAllocationRequest* pAllocationRequest)
7833{
7834 VMA_ASSERT(allocSize > 0);
7835 VMA_ASSERT(!upperAddress);
7836 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7837 VMA_ASSERT(pAllocationRequest != VMA_NULL);
7838 VMA_HEAVY_ASSERT(Validate());
7839
7840 // There is not enough total free space in this block to fullfill the request: Early return.
7841 if(canMakeOtherLost == false &&
7842 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7843 {
7844 return false;
7845 }
7846
7847 // New algorithm, efficiently searching freeSuballocationsBySize.
7848 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7849 if(freeSuballocCount > 0)
7850 {
7851 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7852 {
7853 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7854 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7855 m_FreeSuballocationsBySize.data(),
7856 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7857 allocSize + 2 * VMA_DEBUG_MARGIN,
7858 VmaSuballocationItemSizeLess());
7859 size_t index = it - m_FreeSuballocationsBySize.data();
7860 for(; index < freeSuballocCount; ++index)
7861 {
7862 if(CheckAllocation(
7863 currentFrameIndex,
7864 frameInUseCount,
7865 bufferImageGranularity,
7866 allocSize,
7867 allocAlignment,
7868 allocType,
7869 m_FreeSuballocationsBySize[index],
7870 false, // canMakeOtherLost
7871 &pAllocationRequest->offset,
7872 &pAllocationRequest->itemsToMakeLostCount,
7873 &pAllocationRequest->sumFreeSize,
7874 &pAllocationRequest->sumItemSize))
7875 {
7876 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7877 return true;
7878 }
7879 }
7880 }
7881 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7882 {
7883 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7884 it != m_Suballocations.end();
7885 ++it)
7886 {
7887 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7888 currentFrameIndex,
7889 frameInUseCount,
7890 bufferImageGranularity,
7891 allocSize,
7892 allocAlignment,
7893 allocType,
7894 it,
7895 false, // canMakeOtherLost
7896 &pAllocationRequest->offset,
7897 &pAllocationRequest->itemsToMakeLostCount,
7898 &pAllocationRequest->sumFreeSize,
7899 &pAllocationRequest->sumItemSize))
7900 {
7901 pAllocationRequest->item = it;
7902 return true;
7903 }
7904 }
7905 }
7906 else // WORST_FIT, FIRST_FIT
7907 {
7908 // Search staring from biggest suballocations.
7909 for(size_t index = freeSuballocCount; index--; )
7910 {
7911 if(CheckAllocation(
7912 currentFrameIndex,
7913 frameInUseCount,
7914 bufferImageGranularity,
7915 allocSize,
7916 allocAlignment,
7917 allocType,
7918 m_FreeSuballocationsBySize[index],
7919 false, // canMakeOtherLost
7920 &pAllocationRequest->offset,
7921 &pAllocationRequest->itemsToMakeLostCount,
7922 &pAllocationRequest->sumFreeSize,
7923 &pAllocationRequest->sumItemSize))
7924 {
7925 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7926 return true;
7927 }
7928 }
7929 }
7930 }
7931
7932 if(canMakeOtherLost)
7933 {
7934 // Brute-force algorithm. TODO: Come up with something better.
7935
7936 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7937 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7938
7939 VmaAllocationRequest tmpAllocRequest = {};
7940 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7941 suballocIt != m_Suballocations.end();
7942 ++suballocIt)
7943 {
7944 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7945 suballocIt->hAllocation->CanBecomeLost())
7946 {
7947 if(CheckAllocation(
7948 currentFrameIndex,
7949 frameInUseCount,
7950 bufferImageGranularity,
7951 allocSize,
7952 allocAlignment,
7953 allocType,
7954 suballocIt,
7955 canMakeOtherLost,
7956 &tmpAllocRequest.offset,
7957 &tmpAllocRequest.itemsToMakeLostCount,
7958 &tmpAllocRequest.sumFreeSize,
7959 &tmpAllocRequest.sumItemSize))
7960 {
7961 tmpAllocRequest.item = suballocIt;
7962
7963 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7964 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7965 {
7966 *pAllocationRequest = tmpAllocRequest;
7967 }
7968 }
7969 }
7970 }
7971
7972 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7973 {
7974 return true;
7975 }
7976 }
7977
7978 return false;
7979}
7980
7981bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7982 uint32_t currentFrameIndex,
7983 uint32_t frameInUseCount,
7984 VmaAllocationRequest* pAllocationRequest)
7985{
7986 while(pAllocationRequest->itemsToMakeLostCount > 0)
7987 {
7988 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7989 {
7990 ++pAllocationRequest->item;
7991 }
7992 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
7993 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
7994 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
7995 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
7996 {
7997 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
7998 --pAllocationRequest->itemsToMakeLostCount;
7999 }
8000 else
8001 {
8002 return false;
8003 }
8004 }
8005
8006 VMA_HEAVY_ASSERT(Validate());
8007 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8008 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8009
8010 return true;
8011}
8012
8013uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8014{
8015 uint32_t lostAllocationCount = 0;
8016 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8017 it != m_Suballocations.end();
8018 ++it)
8019 {
8020 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8021 it->hAllocation->CanBecomeLost() &&
8022 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8023 {
8024 it = FreeSuballocation(it);
8025 ++lostAllocationCount;
8026 }
8027 }
8028 return lostAllocationCount;
8029}
8030
8031VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8032{
8033 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8034 it != m_Suballocations.end();
8035 ++it)
8036 {
8037 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8038 {
8039 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8040 {
8041 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8042 return VK_ERROR_VALIDATION_FAILED_EXT;
8043 }
8044 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8045 {
8046 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8047 return VK_ERROR_VALIDATION_FAILED_EXT;
8048 }
8049 }
8050 }
8051
8052 return VK_SUCCESS;
8053}
8054
8055void VmaBlockMetadata_Generic::Alloc(
8056 const VmaAllocationRequest& request,
8057 VmaSuballocationType type,
8058 VkDeviceSize allocSize,
8059 bool upperAddress,
8060 VmaAllocation hAllocation)
8061{
8062 VMA_ASSERT(!upperAddress);
8063 VMA_ASSERT(request.item != m_Suballocations.end());
8064 VmaSuballocation& suballoc = *request.item;
8065 // Given suballocation is a free block.
8066 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8067 // Given offset is inside this suballocation.
8068 VMA_ASSERT(request.offset >= suballoc.offset);
8069 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8070 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8071 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8072
8073 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8074 // it to become used.
8075 UnregisterFreeSuballocation(request.item);
8076
8077 suballoc.offset = request.offset;
8078 suballoc.size = allocSize;
8079 suballoc.type = type;
8080 suballoc.hAllocation = hAllocation;
8081
8082 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8083 if(paddingEnd)
8084 {
8085 VmaSuballocation paddingSuballoc = {};
8086 paddingSuballoc.offset = request.offset + allocSize;
8087 paddingSuballoc.size = paddingEnd;
8088 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8089 VmaSuballocationList::iterator next = request.item;
8090 ++next;
8091 const VmaSuballocationList::iterator paddingEndItem =
8092 m_Suballocations.insert(next, paddingSuballoc);
8093 RegisterFreeSuballocation(paddingEndItem);
8094 }
8095
8096 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8097 if(paddingBegin)
8098 {
8099 VmaSuballocation paddingSuballoc = {};
8100 paddingSuballoc.offset = request.offset - paddingBegin;
8101 paddingSuballoc.size = paddingBegin;
8102 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8103 const VmaSuballocationList::iterator paddingBeginItem =
8104 m_Suballocations.insert(request.item, paddingSuballoc);
8105 RegisterFreeSuballocation(paddingBeginItem);
8106 }
8107
8108 // Update totals.
8109 m_FreeCount = m_FreeCount - 1;
8110 if(paddingBegin > 0)
8111 {
8112 ++m_FreeCount;
8113 }
8114 if(paddingEnd > 0)
8115 {
8116 ++m_FreeCount;
8117 }
8118 m_SumFreeSize -= allocSize;
8119}
8120
8121void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8122{
8123 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8124 suballocItem != m_Suballocations.end();
8125 ++suballocItem)
8126 {
8127 VmaSuballocation& suballoc = *suballocItem;
8128 if(suballoc.hAllocation == allocation)
8129 {
8130 FreeSuballocation(suballocItem);
8131 VMA_HEAVY_ASSERT(Validate());
8132 return;
8133 }
8134 }
8135 VMA_ASSERT(0 && "Not found!");
8136}
8137
8138void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8139{
8140 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8141 suballocItem != m_Suballocations.end();
8142 ++suballocItem)
8143 {
8144 VmaSuballocation& suballoc = *suballocItem;
8145 if(suballoc.offset == offset)
8146 {
8147 FreeSuballocation(suballocItem);
8148 return;
8149 }
8150 }
8151 VMA_ASSERT(0 && "Not found!");
8152}
8153
8154bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8155{
8156 typedef VmaSuballocationList::iterator iter_type;
8157 for(iter_type suballocItem = m_Suballocations.begin();
8158 suballocItem != m_Suballocations.end();
8159 ++suballocItem)
8160 {
8161 VmaSuballocation& suballoc = *suballocItem;
8162 if(suballoc.hAllocation == alloc)
8163 {
8164 iter_type nextItem = suballocItem;
8165 ++nextItem;
8166
8167 // Should have been ensured on higher level.
8168 VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8169
8170 // Shrinking.
8171 if(newSize < alloc->GetSize())
8172 {
8173 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8174
8175 // There is next item.
8176 if(nextItem != m_Suballocations.end())
8177 {
8178 // Next item is free.
8179 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8180 {
8181 // Grow this next item backward.
8182 UnregisterFreeSuballocation(nextItem);
8183 nextItem->offset -= sizeDiff;
8184 nextItem->size += sizeDiff;
8185 RegisterFreeSuballocation(nextItem);
8186 }
8187 // Next item is not free.
8188 else
8189 {
8190 // Create free item after current one.
8191 VmaSuballocation newFreeSuballoc;
8192 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8193 newFreeSuballoc.offset = suballoc.offset + newSize;
8194 newFreeSuballoc.size = sizeDiff;
8195 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8196 iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8197 RegisterFreeSuballocation(newFreeSuballocIt);
8198
8199 ++m_FreeCount;
8200 }
8201 }
8202 // This is the last item.
8203 else
8204 {
8205 // Create free item at the end.
8206 VmaSuballocation newFreeSuballoc;
8207 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8208 newFreeSuballoc.offset = suballoc.offset + newSize;
8209 newFreeSuballoc.size = sizeDiff;
8210 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8211 m_Suballocations.push_back(newFreeSuballoc);
8212
8213 iter_type newFreeSuballocIt = m_Suballocations.end();
8214 RegisterFreeSuballocation(--newFreeSuballocIt);
8215
8216 ++m_FreeCount;
8217 }
8218
8219 suballoc.size = newSize;
8220 m_SumFreeSize += sizeDiff;
8221 }
8222 // Growing.
8223 else
8224 {
8225 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8226
8227 // There is next item.
8228 if(nextItem != m_Suballocations.end())
8229 {
8230 // Next item is free.
8231 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8232 {
8233 // There is not enough free space, including margin.
8234 if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8235 {
8236 return false;
8237 }
8238
8239 // There is more free space than required.
8240 if(nextItem->size > sizeDiff)
8241 {
8242 // Move and shrink this next item.
8243 UnregisterFreeSuballocation(nextItem);
8244 nextItem->offset += sizeDiff;
8245 nextItem->size -= sizeDiff;
8246 RegisterFreeSuballocation(nextItem);
8247 }
8248 // There is exactly the amount of free space required.
8249 else
8250 {
8251 // Remove this next free item.
8252 UnregisterFreeSuballocation(nextItem);
8253 m_Suballocations.erase(nextItem);
8254 --m_FreeCount;
8255 }
8256 }
8257 // Next item is not free - there is no space to grow.
8258 else
8259 {
8260 return false;
8261 }
8262 }
8263 // This is the last item - there is no space to grow.
8264 else
8265 {
8266 return false;
8267 }
8268
8269 suballoc.size = newSize;
8270 m_SumFreeSize -= sizeDiff;
8271 }
8272
8273 // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8274 return true;
8275 }
8276 }
8277 VMA_ASSERT(0 && "Not found!");
8278 return false;
8279}
8280
8281bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8282{
8283 VkDeviceSize lastSize = 0;
8284 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8285 {
8286 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8287
8288 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8289 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8290 VMA_VALIDATE(it->size >= lastSize);
8291 lastSize = it->size;
8292 }
8293 return true;
8294}
8295
8296bool VmaBlockMetadata_Generic::CheckAllocation(
8297 uint32_t currentFrameIndex,
8298 uint32_t frameInUseCount,
8299 VkDeviceSize bufferImageGranularity,
8300 VkDeviceSize allocSize,
8301 VkDeviceSize allocAlignment,
8302 VmaSuballocationType allocType,
8303 VmaSuballocationList::const_iterator suballocItem,
8304 bool canMakeOtherLost,
8305 VkDeviceSize* pOffset,
8306 size_t* itemsToMakeLostCount,
8307 VkDeviceSize* pSumFreeSize,
8308 VkDeviceSize* pSumItemSize) const
8309{
8310 VMA_ASSERT(allocSize > 0);
8311 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8312 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8313 VMA_ASSERT(pOffset != VMA_NULL);
8314
8315 *itemsToMakeLostCount = 0;
8316 *pSumFreeSize = 0;
8317 *pSumItemSize = 0;
8318
8319 if(canMakeOtherLost)
8320 {
8321 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8322 {
8323 *pSumFreeSize = suballocItem->size;
8324 }
8325 else
8326 {
8327 if(suballocItem->hAllocation->CanBecomeLost() &&
8328 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8329 {
8330 ++*itemsToMakeLostCount;
8331 *pSumItemSize = suballocItem->size;
8332 }
8333 else
8334 {
8335 return false;
8336 }
8337 }
8338
8339 // Remaining size is too small for this request: Early return.
8340 if(GetSize() - suballocItem->offset < allocSize)
8341 {
8342 return false;
8343 }
8344
8345 // Start from offset equal to beginning of this suballocation.
8346 *pOffset = suballocItem->offset;
8347
8348 // Apply VMA_DEBUG_MARGIN at the beginning.
8349 if(VMA_DEBUG_MARGIN > 0)
8350 {
8351 *pOffset += VMA_DEBUG_MARGIN;
8352 }
8353
8354 // Apply alignment.
8355 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8356
8357 // Check previous suballocations for BufferImageGranularity conflicts.
8358 // Make bigger alignment if necessary.
8359 if(bufferImageGranularity > 1)
8360 {
8361 bool bufferImageGranularityConflict = false;
8362 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8363 while(prevSuballocItem != m_Suballocations.cbegin())
8364 {
8365 --prevSuballocItem;
8366 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8367 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8368 {
8369 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8370 {
8371 bufferImageGranularityConflict = true;
8372 break;
8373 }
8374 }
8375 else
8376 // Already on previous page.
8377 break;
8378 }
8379 if(bufferImageGranularityConflict)
8380 {
8381 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8382 }
8383 }
8384
8385 // Now that we have final *pOffset, check if we are past suballocItem.
8386 // If yes, return false - this function should be called for another suballocItem as starting point.
8387 if(*pOffset >= suballocItem->offset + suballocItem->size)
8388 {
8389 return false;
8390 }
8391
8392 // Calculate padding at the beginning based on current offset.
8393 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8394
8395 // Calculate required margin at the end.
8396 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8397
8398 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8399 // Another early return check.
8400 if(suballocItem->offset + totalSize > GetSize())
8401 {
8402 return false;
8403 }
8404
8405 // Advance lastSuballocItem until desired size is reached.
8406 // Update itemsToMakeLostCount.
8407 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8408 if(totalSize > suballocItem->size)
8409 {
8410 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8411 while(remainingSize > 0)
8412 {
8413 ++lastSuballocItem;
8414 if(lastSuballocItem == m_Suballocations.cend())
8415 {
8416 return false;
8417 }
8418 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8419 {
8420 *pSumFreeSize += lastSuballocItem->size;
8421 }
8422 else
8423 {
8424 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8425 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8426 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8427 {
8428 ++*itemsToMakeLostCount;
8429 *pSumItemSize += lastSuballocItem->size;
8430 }
8431 else
8432 {
8433 return false;
8434 }
8435 }
8436 remainingSize = (lastSuballocItem->size < remainingSize) ?
8437 remainingSize - lastSuballocItem->size : 0;
8438 }
8439 }
8440
8441 // Check next suballocations for BufferImageGranularity conflicts.
8442 // If conflict exists, we must mark more allocations lost or fail.
8443 if(bufferImageGranularity > 1)
8444 {
8445 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8446 ++nextSuballocItem;
8447 while(nextSuballocItem != m_Suballocations.cend())
8448 {
8449 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8450 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8451 {
8452 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8453 {
8454 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8455 if(nextSuballoc.hAllocation->CanBecomeLost() &&
8456 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8457 {
8458 ++*itemsToMakeLostCount;
8459 }
8460 else
8461 {
8462 return false;
8463 }
8464 }
8465 }
8466 else
8467 {
8468 // Already on next page.
8469 break;
8470 }
8471 ++nextSuballocItem;
8472 }
8473 }
8474 }
8475 else
8476 {
8477 const VmaSuballocation& suballoc = *suballocItem;
8478 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8479
8480 *pSumFreeSize = suballoc.size;
8481
8482 // Size of this suballocation is too small for this request: Early return.
8483 if(suballoc.size < allocSize)
8484 {
8485 return false;
8486 }
8487
8488 // Start from offset equal to beginning of this suballocation.
8489 *pOffset = suballoc.offset;
8490
8491 // Apply VMA_DEBUG_MARGIN at the beginning.
8492 if(VMA_DEBUG_MARGIN > 0)
8493 {
8494 *pOffset += VMA_DEBUG_MARGIN;
8495 }
8496
8497 // Apply alignment.
8498 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8499
8500 // Check previous suballocations for BufferImageGranularity conflicts.
8501 // Make bigger alignment if necessary.
8502 if(bufferImageGranularity > 1)
8503 {
8504 bool bufferImageGranularityConflict = false;
8505 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8506 while(prevSuballocItem != m_Suballocations.cbegin())
8507 {
8508 --prevSuballocItem;
8509 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8510 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8511 {
8512 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8513 {
8514 bufferImageGranularityConflict = true;
8515 break;
8516 }
8517 }
8518 else
8519 // Already on previous page.
8520 break;
8521 }
8522 if(bufferImageGranularityConflict)
8523 {
8524 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8525 }
8526 }
8527
8528 // Calculate padding at the beginning based on current offset.
8529 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8530
8531 // Calculate required margin at the end.
8532 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8533
8534 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8535 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8536 {
8537 return false;
8538 }
8539
8540 // Check next suballocations for BufferImageGranularity conflicts.
8541 // If conflict exists, allocation cannot be made here.
8542 if(bufferImageGranularity > 1)
8543 {
8544 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8545 ++nextSuballocItem;
8546 while(nextSuballocItem != m_Suballocations.cend())
8547 {
8548 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8549 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8550 {
8551 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8552 {
8553 return false;
8554 }
8555 }
8556 else
8557 {
8558 // Already on next page.
8559 break;
8560 }
8561 ++nextSuballocItem;
8562 }
8563 }
8564 }
8565
8566 // All tests passed: Success. pOffset is already filled.
8567 return true;
8568}
8569
8570void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8571{
8572 VMA_ASSERT(item != m_Suballocations.end());
8573 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8574
8575 VmaSuballocationList::iterator nextItem = item;
8576 ++nextItem;
8577 VMA_ASSERT(nextItem != m_Suballocations.end());
8578 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8579
8580 item->size += nextItem->size;
8581 --m_FreeCount;
8582 m_Suballocations.erase(nextItem);
8583}
8584
8585VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8586{
8587 // Change this suballocation to be marked as free.
8588 VmaSuballocation& suballoc = *suballocItem;
8589 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8590 suballoc.hAllocation = VK_NULL_HANDLE;
8591
8592 // Update totals.
8593 ++m_FreeCount;
8594 m_SumFreeSize += suballoc.size;
8595
8596 // Merge with previous and/or next suballocation if it's also free.
8597 bool mergeWithNext = false;
8598 bool mergeWithPrev = false;
8599
8600 VmaSuballocationList::iterator nextItem = suballocItem;
8601 ++nextItem;
8602 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8603 {
8604 mergeWithNext = true;
8605 }
8606
8607 VmaSuballocationList::iterator prevItem = suballocItem;
8608 if(suballocItem != m_Suballocations.begin())
8609 {
8610 --prevItem;
8611 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8612 {
8613 mergeWithPrev = true;
8614 }
8615 }
8616
8617 if(mergeWithNext)
8618 {
8619 UnregisterFreeSuballocation(nextItem);
8620 MergeFreeWithNext(suballocItem);
8621 }
8622
8623 if(mergeWithPrev)
8624 {
8625 UnregisterFreeSuballocation(prevItem);
8626 MergeFreeWithNext(prevItem);
8627 RegisterFreeSuballocation(prevItem);
8628 return prevItem;
8629 }
8630 else
8631 {
8632 RegisterFreeSuballocation(suballocItem);
8633 return suballocItem;
8634 }
8635}
8636
8637void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8638{
8639 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8640 VMA_ASSERT(item->size > 0);
8641
8642 // You may want to enable this validation at the beginning or at the end of
8643 // this function, depending on what do you want to check.
8644 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8645
8646 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8647 {
8648 if(m_FreeSuballocationsBySize.empty())
8649 {
8650 m_FreeSuballocationsBySize.push_back(item);
8651 }
8652 else
8653 {
8654 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8655 }
8656 }
8657
8658 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8659}
8660
8661
8662void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8663{
8664 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8665 VMA_ASSERT(item->size > 0);
8666
8667 // You may want to enable this validation at the beginning or at the end of
8668 // this function, depending on what do you want to check.
8669 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8670
8671 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8672 {
8673 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8674 m_FreeSuballocationsBySize.data(),
8675 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8676 item,
8677 VmaSuballocationItemSizeLess());
8678 for(size_t index = it - m_FreeSuballocationsBySize.data();
8679 index < m_FreeSuballocationsBySize.size();
8680 ++index)
8681 {
8682 if(m_FreeSuballocationsBySize[index] == item)
8683 {
8684 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8685 return;
8686 }
8687 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8688 }
8689 VMA_ASSERT(0 && "Not found.");
8690 }
8691
8692 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8693}
8694
8695bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8696 VkDeviceSize bufferImageGranularity,
8697 VmaSuballocationType& inOutPrevSuballocType) const
8698{
8699 if(bufferImageGranularity == 1 || IsEmpty())
8700 {
8701 return false;
8702 }
8703
8704 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8705 bool typeConflictFound = false;
8706 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8707 it != m_Suballocations.cend();
8708 ++it)
8709 {
8710 const VmaSuballocationType suballocType = it->type;
8711 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8712 {
8713 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8714 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8715 {
8716 typeConflictFound = true;
8717 }
8718 inOutPrevSuballocType = suballocType;
8719 }
8720 }
8721
8722 return typeConflictFound || minAlignment >= bufferImageGranularity;
8723}
8724
8725////////////////////////////////////////////////////////////////////////////////
8726// class VmaBlockMetadata_Linear
8727
8728VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8729 VmaBlockMetadata(hAllocator),
8730 m_SumFreeSize(0),
8731 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8732 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8733 m_1stVectorIndex(0),
8734 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8735 m_1stNullItemsBeginCount(0),
8736 m_1stNullItemsMiddleCount(0),
8737 m_2ndNullItemsCount(0)
8738{
8739}
8740
8741VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8742{
8743}
8744
8745void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8746{
8747 VmaBlockMetadata::Init(size);
8748 m_SumFreeSize = size;
8749}
8750
8751bool VmaBlockMetadata_Linear::Validate() const
8752{
8753 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8754 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8755
8756 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8757 VMA_VALIDATE(!suballocations1st.empty() ||
8758 suballocations2nd.empty() ||
8759 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8760
8761 if(!suballocations1st.empty())
8762 {
8763 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8764 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8765 // Null item at the end should be just pop_back().
8766 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8767 }
8768 if(!suballocations2nd.empty())
8769 {
8770 // Null item at the end should be just pop_back().
8771 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8772 }
8773
8774 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8775 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8776
8777 VkDeviceSize sumUsedSize = 0;
8778 const size_t suballoc1stCount = suballocations1st.size();
8779 VkDeviceSize offset = VMA_DEBUG_MARGIN;
8780
8781 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8782 {
8783 const size_t suballoc2ndCount = suballocations2nd.size();
8784 size_t nullItem2ndCount = 0;
8785 for(size_t i = 0; i < suballoc2ndCount; ++i)
8786 {
8787 const VmaSuballocation& suballoc = suballocations2nd[i];
8788 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8789
8790 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8791 VMA_VALIDATE(suballoc.offset >= offset);
8792
8793 if(!currFree)
8794 {
8795 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8796 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8797 sumUsedSize += suballoc.size;
8798 }
8799 else
8800 {
8801 ++nullItem2ndCount;
8802 }
8803
8804 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8805 }
8806
8807 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8808 }
8809
8810 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8811 {
8812 const VmaSuballocation& suballoc = suballocations1st[i];
8813 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8814 suballoc.hAllocation == VK_NULL_HANDLE);
8815 }
8816
8817 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8818
8819 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8820 {
8821 const VmaSuballocation& suballoc = suballocations1st[i];
8822 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8823
8824 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8825 VMA_VALIDATE(suballoc.offset >= offset);
8826 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8827
8828 if(!currFree)
8829 {
8830 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8831 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8832 sumUsedSize += suballoc.size;
8833 }
8834 else
8835 {
8836 ++nullItem1stCount;
8837 }
8838
8839 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8840 }
8841 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8842
8843 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8844 {
8845 const size_t suballoc2ndCount = suballocations2nd.size();
8846 size_t nullItem2ndCount = 0;
8847 for(size_t i = suballoc2ndCount; i--; )
8848 {
8849 const VmaSuballocation& suballoc = suballocations2nd[i];
8850 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8851
8852 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8853 VMA_VALIDATE(suballoc.offset >= offset);
8854
8855 if(!currFree)
8856 {
8857 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8858 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8859 sumUsedSize += suballoc.size;
8860 }
8861 else
8862 {
8863 ++nullItem2ndCount;
8864 }
8865
8866 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8867 }
8868
8869 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8870 }
8871
8872 VMA_VALIDATE(offset <= GetSize());
8873 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8874
8875 return true;
8876}
8877
8878size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8879{
8880 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8881 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8882}
8883
8884VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8885{
8886 const VkDeviceSize size = GetSize();
8887
8888 /*
8889 We don't consider gaps inside allocation vectors with freed allocations because
8890 they are not suitable for reuse in linear allocator. We consider only space that
8891 is available for new allocations.
8892 */
8893 if(IsEmpty())
8894 {
8895 return size;
8896 }
8897
8898 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8899
8900 switch(m_2ndVectorMode)
8901 {
8902 case SECOND_VECTOR_EMPTY:
8903 /*
8904 Available space is after end of 1st, as well as before beginning of 1st (which
8905 whould make it a ring buffer).
8906 */
8907 {
8908 const size_t suballocations1stCount = suballocations1st.size();
8909 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8910 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8911 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8912 return VMA_MAX(
8913 firstSuballoc.offset,
8914 size - (lastSuballoc.offset + lastSuballoc.size));
8915 }
8916 break;
8917
8918 case SECOND_VECTOR_RING_BUFFER:
8919 /*
8920 Available space is only between end of 2nd and beginning of 1st.
8921 */
8922 {
8923 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8924 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8925 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8926 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8927 }
8928 break;
8929
8930 case SECOND_VECTOR_DOUBLE_STACK:
8931 /*
8932 Available space is only between end of 1st and top of 2nd.
8933 */
8934 {
8935 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8936 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8937 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8938 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8939 }
8940 break;
8941
8942 default:
8943 VMA_ASSERT(0);
8944 return 0;
8945 }
8946}
8947
8948void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8949{
8950 const VkDeviceSize size = GetSize();
8951 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8952 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8953 const size_t suballoc1stCount = suballocations1st.size();
8954 const size_t suballoc2ndCount = suballocations2nd.size();
8955
8956 outInfo.blockCount = 1;
8957 outInfo.allocationCount = (uint32_t)GetAllocationCount();
8958 outInfo.unusedRangeCount = 0;
8959 outInfo.usedBytes = 0;
8960 outInfo.allocationSizeMin = UINT64_MAX;
8961 outInfo.allocationSizeMax = 0;
8962 outInfo.unusedRangeSizeMin = UINT64_MAX;
8963 outInfo.unusedRangeSizeMax = 0;
8964
8965 VkDeviceSize lastOffset = 0;
8966
8967 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8968 {
8969 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8970 size_t nextAlloc2ndIndex = 0;
8971 while(lastOffset < freeSpace2ndTo1stEnd)
8972 {
8973 // Find next non-null allocation or move nextAllocIndex to the end.
8974 while(nextAlloc2ndIndex < suballoc2ndCount &&
8975 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8976 {
8977 ++nextAlloc2ndIndex;
8978 }
8979
8980 // Found non-null allocation.
8981 if(nextAlloc2ndIndex < suballoc2ndCount)
8982 {
8983 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8984
8985 // 1. Process free space before this allocation.
8986 if(lastOffset < suballoc.offset)
8987 {
8988 // There is free space from lastOffset to suballoc.offset.
8989 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8990 ++outInfo.unusedRangeCount;
8991 outInfo.unusedBytes += unusedRangeSize;
8992 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
8993 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
8994 }
8995
8996 // 2. Process this allocation.
8997 // There is allocation with suballoc.offset, suballoc.size.
8998 outInfo.usedBytes += suballoc.size;
8999 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9000 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9001
9002 // 3. Prepare for next iteration.
9003 lastOffset = suballoc.offset + suballoc.size;
9004 ++nextAlloc2ndIndex;
9005 }
9006 // We are at the end.
9007 else
9008 {
9009 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9010 if(lastOffset < freeSpace2ndTo1stEnd)
9011 {
9012 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9013 ++outInfo.unusedRangeCount;
9014 outInfo.unusedBytes += unusedRangeSize;
9015 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9016 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9017 }
9018
9019 // End of loop.
9020 lastOffset = freeSpace2ndTo1stEnd;
9021 }
9022 }
9023 }
9024
9025 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9026 const VkDeviceSize freeSpace1stTo2ndEnd =
9027 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9028 while(lastOffset < freeSpace1stTo2ndEnd)
9029 {
9030 // Find next non-null allocation or move nextAllocIndex to the end.
9031 while(nextAlloc1stIndex < suballoc1stCount &&
9032 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9033 {
9034 ++nextAlloc1stIndex;
9035 }
9036
9037 // Found non-null allocation.
9038 if(nextAlloc1stIndex < suballoc1stCount)
9039 {
9040 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9041
9042 // 1. Process free space before this allocation.
9043 if(lastOffset < suballoc.offset)
9044 {
9045 // There is free space from lastOffset to suballoc.offset.
9046 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9047 ++outInfo.unusedRangeCount;
9048 outInfo.unusedBytes += unusedRangeSize;
9049 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9050 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9051 }
9052
9053 // 2. Process this allocation.
9054 // There is allocation with suballoc.offset, suballoc.size.
9055 outInfo.usedBytes += suballoc.size;
9056 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9057 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9058
9059 // 3. Prepare for next iteration.
9060 lastOffset = suballoc.offset + suballoc.size;
9061 ++nextAlloc1stIndex;
9062 }
9063 // We are at the end.
9064 else
9065 {
9066 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9067 if(lastOffset < freeSpace1stTo2ndEnd)
9068 {
9069 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9070 ++outInfo.unusedRangeCount;
9071 outInfo.unusedBytes += unusedRangeSize;
9072 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9073 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9074 }
9075
9076 // End of loop.
9077 lastOffset = freeSpace1stTo2ndEnd;
9078 }
9079 }
9080
9081 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9082 {
9083 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9084 while(lastOffset < size)
9085 {
9086 // Find next non-null allocation or move nextAllocIndex to the end.
9087 while(nextAlloc2ndIndex != SIZE_MAX &&
9088 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9089 {
9090 --nextAlloc2ndIndex;
9091 }
9092
9093 // Found non-null allocation.
9094 if(nextAlloc2ndIndex != SIZE_MAX)
9095 {
9096 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9097
9098 // 1. Process free space before this allocation.
9099 if(lastOffset < suballoc.offset)
9100 {
9101 // There is free space from lastOffset to suballoc.offset.
9102 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9103 ++outInfo.unusedRangeCount;
9104 outInfo.unusedBytes += unusedRangeSize;
9105 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9106 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9107 }
9108
9109 // 2. Process this allocation.
9110 // There is allocation with suballoc.offset, suballoc.size.
9111 outInfo.usedBytes += suballoc.size;
9112 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9113 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9114
9115 // 3. Prepare for next iteration.
9116 lastOffset = suballoc.offset + suballoc.size;
9117 --nextAlloc2ndIndex;
9118 }
9119 // We are at the end.
9120 else
9121 {
9122 // There is free space from lastOffset to size.
9123 if(lastOffset < size)
9124 {
9125 const VkDeviceSize unusedRangeSize = size - lastOffset;
9126 ++outInfo.unusedRangeCount;
9127 outInfo.unusedBytes += unusedRangeSize;
9128 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9129 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9130 }
9131
9132 // End of loop.
9133 lastOffset = size;
9134 }
9135 }
9136 }
9137
9138 outInfo.unusedBytes = size - outInfo.usedBytes;
9139}
9140
9141void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9142{
9143 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9144 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9145 const VkDeviceSize size = GetSize();
9146 const size_t suballoc1stCount = suballocations1st.size();
9147 const size_t suballoc2ndCount = suballocations2nd.size();
9148
9149 inoutStats.size += size;
9150
9151 VkDeviceSize lastOffset = 0;
9152
9153 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9154 {
9155 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9156 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9157 while(lastOffset < freeSpace2ndTo1stEnd)
9158 {
9159 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9160 while(nextAlloc2ndIndex < suballoc2ndCount &&
9161 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9162 {
9163 ++nextAlloc2ndIndex;
9164 }
9165
9166 // Found non-null allocation.
9167 if(nextAlloc2ndIndex < suballoc2ndCount)
9168 {
9169 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9170
9171 // 1. Process free space before this allocation.
9172 if(lastOffset < suballoc.offset)
9173 {
9174 // There is free space from lastOffset to suballoc.offset.
9175 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9176 inoutStats.unusedSize += unusedRangeSize;
9177 ++inoutStats.unusedRangeCount;
9178 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9179 }
9180
9181 // 2. Process this allocation.
9182 // There is allocation with suballoc.offset, suballoc.size.
9183 ++inoutStats.allocationCount;
9184
9185 // 3. Prepare for next iteration.
9186 lastOffset = suballoc.offset + suballoc.size;
9187 ++nextAlloc2ndIndex;
9188 }
9189 // We are at the end.
9190 else
9191 {
9192 if(lastOffset < freeSpace2ndTo1stEnd)
9193 {
9194 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9195 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9196 inoutStats.unusedSize += unusedRangeSize;
9197 ++inoutStats.unusedRangeCount;
9198 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9199 }
9200
9201 // End of loop.
9202 lastOffset = freeSpace2ndTo1stEnd;
9203 }
9204 }
9205 }
9206
9207 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9208 const VkDeviceSize freeSpace1stTo2ndEnd =
9209 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9210 while(lastOffset < freeSpace1stTo2ndEnd)
9211 {
9212 // Find next non-null allocation or move nextAllocIndex to the end.
9213 while(nextAlloc1stIndex < suballoc1stCount &&
9214 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9215 {
9216 ++nextAlloc1stIndex;
9217 }
9218
9219 // Found non-null allocation.
9220 if(nextAlloc1stIndex < suballoc1stCount)
9221 {
9222 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9223
9224 // 1. Process free space before this allocation.
9225 if(lastOffset < suballoc.offset)
9226 {
9227 // There is free space from lastOffset to suballoc.offset.
9228 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9229 inoutStats.unusedSize += unusedRangeSize;
9230 ++inoutStats.unusedRangeCount;
9231 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9232 }
9233
9234 // 2. Process this allocation.
9235 // There is allocation with suballoc.offset, suballoc.size.
9236 ++inoutStats.allocationCount;
9237
9238 // 3. Prepare for next iteration.
9239 lastOffset = suballoc.offset + suballoc.size;
9240 ++nextAlloc1stIndex;
9241 }
9242 // We are at the end.
9243 else
9244 {
9245 if(lastOffset < freeSpace1stTo2ndEnd)
9246 {
9247 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9248 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9249 inoutStats.unusedSize += unusedRangeSize;
9250 ++inoutStats.unusedRangeCount;
9251 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9252 }
9253
9254 // End of loop.
9255 lastOffset = freeSpace1stTo2ndEnd;
9256 }
9257 }
9258
9259 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9260 {
9261 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9262 while(lastOffset < size)
9263 {
9264 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9265 while(nextAlloc2ndIndex != SIZE_MAX &&
9266 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9267 {
9268 --nextAlloc2ndIndex;
9269 }
9270
9271 // Found non-null allocation.
9272 if(nextAlloc2ndIndex != SIZE_MAX)
9273 {
9274 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9275
9276 // 1. Process free space before this allocation.
9277 if(lastOffset < suballoc.offset)
9278 {
9279 // There is free space from lastOffset to suballoc.offset.
9280 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9281 inoutStats.unusedSize += unusedRangeSize;
9282 ++inoutStats.unusedRangeCount;
9283 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9284 }
9285
9286 // 2. Process this allocation.
9287 // There is allocation with suballoc.offset, suballoc.size.
9288 ++inoutStats.allocationCount;
9289
9290 // 3. Prepare for next iteration.
9291 lastOffset = suballoc.offset + suballoc.size;
9292 --nextAlloc2ndIndex;
9293 }
9294 // We are at the end.
9295 else
9296 {
9297 if(lastOffset < size)
9298 {
9299 // There is free space from lastOffset to size.
9300 const VkDeviceSize unusedRangeSize = size - lastOffset;
9301 inoutStats.unusedSize += unusedRangeSize;
9302 ++inoutStats.unusedRangeCount;
9303 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9304 }
9305
9306 // End of loop.
9307 lastOffset = size;
9308 }
9309 }
9310 }
9311}
9312
9313#if VMA_STATS_STRING_ENABLED
9314void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9315{
9316 const VkDeviceSize size = GetSize();
9317 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9318 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9319 const size_t suballoc1stCount = suballocations1st.size();
9320 const size_t suballoc2ndCount = suballocations2nd.size();
9321
9322 // FIRST PASS
9323
9324 size_t unusedRangeCount = 0;
9325 VkDeviceSize usedBytes = 0;
9326
9327 VkDeviceSize lastOffset = 0;
9328
9329 size_t alloc2ndCount = 0;
9330 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9331 {
9332 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9333 size_t nextAlloc2ndIndex = 0;
9334 while(lastOffset < freeSpace2ndTo1stEnd)
9335 {
9336 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9337 while(nextAlloc2ndIndex < suballoc2ndCount &&
9338 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9339 {
9340 ++nextAlloc2ndIndex;
9341 }
9342
9343 // Found non-null allocation.
9344 if(nextAlloc2ndIndex < suballoc2ndCount)
9345 {
9346 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9347
9348 // 1. Process free space before this allocation.
9349 if(lastOffset < suballoc.offset)
9350 {
9351 // There is free space from lastOffset to suballoc.offset.
9352 ++unusedRangeCount;
9353 }
9354
9355 // 2. Process this allocation.
9356 // There is allocation with suballoc.offset, suballoc.size.
9357 ++alloc2ndCount;
9358 usedBytes += suballoc.size;
9359
9360 // 3. Prepare for next iteration.
9361 lastOffset = suballoc.offset + suballoc.size;
9362 ++nextAlloc2ndIndex;
9363 }
9364 // We are at the end.
9365 else
9366 {
9367 if(lastOffset < freeSpace2ndTo1stEnd)
9368 {
9369 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9370 ++unusedRangeCount;
9371 }
9372
9373 // End of loop.
9374 lastOffset = freeSpace2ndTo1stEnd;
9375 }
9376 }
9377 }
9378
9379 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9380 size_t alloc1stCount = 0;
9381 const VkDeviceSize freeSpace1stTo2ndEnd =
9382 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9383 while(lastOffset < freeSpace1stTo2ndEnd)
9384 {
9385 // Find next non-null allocation or move nextAllocIndex to the end.
9386 while(nextAlloc1stIndex < suballoc1stCount &&
9387 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9388 {
9389 ++nextAlloc1stIndex;
9390 }
9391
9392 // Found non-null allocation.
9393 if(nextAlloc1stIndex < suballoc1stCount)
9394 {
9395 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9396
9397 // 1. Process free space before this allocation.
9398 if(lastOffset < suballoc.offset)
9399 {
9400 // There is free space from lastOffset to suballoc.offset.
9401 ++unusedRangeCount;
9402 }
9403
9404 // 2. Process this allocation.
9405 // There is allocation with suballoc.offset, suballoc.size.
9406 ++alloc1stCount;
9407 usedBytes += suballoc.size;
9408
9409 // 3. Prepare for next iteration.
9410 lastOffset = suballoc.offset + suballoc.size;
9411 ++nextAlloc1stIndex;
9412 }
9413 // We are at the end.
9414 else
9415 {
9416 if(lastOffset < size)
9417 {
9418 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9419 ++unusedRangeCount;
9420 }
9421
9422 // End of loop.
9423 lastOffset = freeSpace1stTo2ndEnd;
9424 }
9425 }
9426
9427 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9428 {
9429 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9430 while(lastOffset < size)
9431 {
9432 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9433 while(nextAlloc2ndIndex != SIZE_MAX &&
9434 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9435 {
9436 --nextAlloc2ndIndex;
9437 }
9438
9439 // Found non-null allocation.
9440 if(nextAlloc2ndIndex != SIZE_MAX)
9441 {
9442 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9443
9444 // 1. Process free space before this allocation.
9445 if(lastOffset < suballoc.offset)
9446 {
9447 // There is free space from lastOffset to suballoc.offset.
9448 ++unusedRangeCount;
9449 }
9450
9451 // 2. Process this allocation.
9452 // There is allocation with suballoc.offset, suballoc.size.
9453 ++alloc2ndCount;
9454 usedBytes += suballoc.size;
9455
9456 // 3. Prepare for next iteration.
9457 lastOffset = suballoc.offset + suballoc.size;
9458 --nextAlloc2ndIndex;
9459 }
9460 // We are at the end.
9461 else
9462 {
9463 if(lastOffset < size)
9464 {
9465 // There is free space from lastOffset to size.
9466 ++unusedRangeCount;
9467 }
9468
9469 // End of loop.
9470 lastOffset = size;
9471 }
9472 }
9473 }
9474
9475 const VkDeviceSize unusedBytes = size - usedBytes;
9476 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9477
9478 // SECOND PASS
9479 lastOffset = 0;
9480
9481 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9482 {
9483 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9484 size_t nextAlloc2ndIndex = 0;
9485 while(lastOffset < freeSpace2ndTo1stEnd)
9486 {
9487 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9488 while(nextAlloc2ndIndex < suballoc2ndCount &&
9489 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9490 {
9491 ++nextAlloc2ndIndex;
9492 }
9493
9494 // Found non-null allocation.
9495 if(nextAlloc2ndIndex < suballoc2ndCount)
9496 {
9497 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9498
9499 // 1. Process free space before this allocation.
9500 if(lastOffset < suballoc.offset)
9501 {
9502 // There is free space from lastOffset to suballoc.offset.
9503 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9504 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9505 }
9506
9507 // 2. Process this allocation.
9508 // There is allocation with suballoc.offset, suballoc.size.
9509 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9510
9511 // 3. Prepare for next iteration.
9512 lastOffset = suballoc.offset + suballoc.size;
9513 ++nextAlloc2ndIndex;
9514 }
9515 // We are at the end.
9516 else
9517 {
9518 if(lastOffset < freeSpace2ndTo1stEnd)
9519 {
9520 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9521 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9522 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9523 }
9524
9525 // End of loop.
9526 lastOffset = freeSpace2ndTo1stEnd;
9527 }
9528 }
9529 }
9530
9531 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9532 while(lastOffset < freeSpace1stTo2ndEnd)
9533 {
9534 // Find next non-null allocation or move nextAllocIndex to the end.
9535 while(nextAlloc1stIndex < suballoc1stCount &&
9536 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9537 {
9538 ++nextAlloc1stIndex;
9539 }
9540
9541 // Found non-null allocation.
9542 if(nextAlloc1stIndex < suballoc1stCount)
9543 {
9544 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9545
9546 // 1. Process free space before this allocation.
9547 if(lastOffset < suballoc.offset)
9548 {
9549 // There is free space from lastOffset to suballoc.offset.
9550 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9551 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9552 }
9553
9554 // 2. Process this allocation.
9555 // There is allocation with suballoc.offset, suballoc.size.
9556 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9557
9558 // 3. Prepare for next iteration.
9559 lastOffset = suballoc.offset + suballoc.size;
9560 ++nextAlloc1stIndex;
9561 }
9562 // We are at the end.
9563 else
9564 {
9565 if(lastOffset < freeSpace1stTo2ndEnd)
9566 {
9567 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9568 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9569 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9570 }
9571
9572 // End of loop.
9573 lastOffset = freeSpace1stTo2ndEnd;
9574 }
9575 }
9576
9577 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9578 {
9579 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9580 while(lastOffset < size)
9581 {
9582 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9583 while(nextAlloc2ndIndex != SIZE_MAX &&
9584 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9585 {
9586 --nextAlloc2ndIndex;
9587 }
9588
9589 // Found non-null allocation.
9590 if(nextAlloc2ndIndex != SIZE_MAX)
9591 {
9592 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9593
9594 // 1. Process free space before this allocation.
9595 if(lastOffset < suballoc.offset)
9596 {
9597 // There is free space from lastOffset to suballoc.offset.
9598 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9599 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9600 }
9601
9602 // 2. Process this allocation.
9603 // There is allocation with suballoc.offset, suballoc.size.
9604 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9605
9606 // 3. Prepare for next iteration.
9607 lastOffset = suballoc.offset + suballoc.size;
9608 --nextAlloc2ndIndex;
9609 }
9610 // We are at the end.
9611 else
9612 {
9613 if(lastOffset < size)
9614 {
9615 // There is free space from lastOffset to size.
9616 const VkDeviceSize unusedRangeSize = size - lastOffset;
9617 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9618 }
9619
9620 // End of loop.
9621 lastOffset = size;
9622 }
9623 }
9624 }
9625
9626 PrintDetailedMap_End(json);
9627}
9628#endif // #if VMA_STATS_STRING_ENABLED
9629
9630bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9631 uint32_t currentFrameIndex,
9632 uint32_t frameInUseCount,
9633 VkDeviceSize bufferImageGranularity,
9634 VkDeviceSize allocSize,
9635 VkDeviceSize allocAlignment,
9636 bool upperAddress,
9637 VmaSuballocationType allocType,
9638 bool canMakeOtherLost,
9639 uint32_t strategy,
9640 VmaAllocationRequest* pAllocationRequest)
9641{
9642 VMA_ASSERT(allocSize > 0);
9643 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9644 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9645 VMA_HEAVY_ASSERT(Validate());
9646
9647 const VkDeviceSize size = GetSize();
9648 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9649 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9650
9651 if(upperAddress)
9652 {
9653 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9654 {
9655 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9656 return false;
9657 }
9658
9659 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9660 if(allocSize > size)
9661 {
9662 return false;
9663 }
9664 VkDeviceSize resultBaseOffset = size - allocSize;
9665 if(!suballocations2nd.empty())
9666 {
9667 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9668 resultBaseOffset = lastSuballoc.offset - allocSize;
9669 if(allocSize > lastSuballoc.offset)
9670 {
9671 return false;
9672 }
9673 }
9674
9675 // Start from offset equal to end of free space.
9676 VkDeviceSize resultOffset = resultBaseOffset;
9677
9678 // Apply VMA_DEBUG_MARGIN at the end.
9679 if(VMA_DEBUG_MARGIN > 0)
9680 {
9681 if(resultOffset < VMA_DEBUG_MARGIN)
9682 {
9683 return false;
9684 }
9685 resultOffset -= VMA_DEBUG_MARGIN;
9686 }
9687
9688 // Apply alignment.
9689 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9690
9691 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9692 // Make bigger alignment if necessary.
9693 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9694 {
9695 bool bufferImageGranularityConflict = false;
9696 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9697 {
9698 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9699 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9700 {
9701 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9702 {
9703 bufferImageGranularityConflict = true;
9704 break;
9705 }
9706 }
9707 else
9708 // Already on previous page.
9709 break;
9710 }
9711 if(bufferImageGranularityConflict)
9712 {
9713 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9714 }
9715 }
9716
9717 // There is enough free space.
9718 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9719 suballocations1st.back().offset + suballocations1st.back().size :
9720 0;
9721 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9722 {
9723 // Check previous suballocations for BufferImageGranularity conflicts.
9724 // If conflict exists, allocation cannot be made here.
9725 if(bufferImageGranularity > 1)
9726 {
9727 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9728 {
9729 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9730 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9731 {
9732 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9733 {
9734 return false;
9735 }
9736 }
9737 else
9738 {
9739 // Already on next page.
9740 break;
9741 }
9742 }
9743 }
9744
9745 // All tests passed: Success.
9746 pAllocationRequest->offset = resultOffset;
9747 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9748 pAllocationRequest->sumItemSize = 0;
9749 // pAllocationRequest->item unused.
9750 pAllocationRequest->itemsToMakeLostCount = 0;
9751 return true;
9752 }
9753 }
9754 else // !upperAddress
9755 {
9756 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9757 {
9758 // Try to allocate at the end of 1st vector.
9759
9760 VkDeviceSize resultBaseOffset = 0;
9761 if(!suballocations1st.empty())
9762 {
9763 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9764 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9765 }
9766
9767 // Start from offset equal to beginning of free space.
9768 VkDeviceSize resultOffset = resultBaseOffset;
9769
9770 // Apply VMA_DEBUG_MARGIN at the beginning.
9771 if(VMA_DEBUG_MARGIN > 0)
9772 {
9773 resultOffset += VMA_DEBUG_MARGIN;
9774 }
9775
9776 // Apply alignment.
9777 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9778
9779 // Check previous suballocations for BufferImageGranularity conflicts.
9780 // Make bigger alignment if necessary.
9781 if(bufferImageGranularity > 1 && !suballocations1st.empty())
9782 {
9783 bool bufferImageGranularityConflict = false;
9784 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9785 {
9786 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9787 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9788 {
9789 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9790 {
9791 bufferImageGranularityConflict = true;
9792 break;
9793 }
9794 }
9795 else
9796 // Already on previous page.
9797 break;
9798 }
9799 if(bufferImageGranularityConflict)
9800 {
9801 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9802 }
9803 }
9804
9805 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9806 suballocations2nd.back().offset : size;
9807
9808 // There is enough free space at the end after alignment.
9809 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9810 {
9811 // Check next suballocations for BufferImageGranularity conflicts.
9812 // If conflict exists, allocation cannot be made here.
9813 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9814 {
9815 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9816 {
9817 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9818 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9819 {
9820 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9821 {
9822 return false;
9823 }
9824 }
9825 else
9826 {
9827 // Already on previous page.
9828 break;
9829 }
9830 }
9831 }
9832
9833 // All tests passed: Success.
9834 pAllocationRequest->offset = resultOffset;
9835 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9836 pAllocationRequest->sumItemSize = 0;
9837 // pAllocationRequest->item unused.
9838 pAllocationRequest->itemsToMakeLostCount = 0;
9839 return true;
9840 }
9841 }
9842
9843 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9844 // beginning of 1st vector as the end of free space.
9845 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9846 {
9847 VMA_ASSERT(!suballocations1st.empty());
9848
9849 VkDeviceSize resultBaseOffset = 0;
9850 if(!suballocations2nd.empty())
9851 {
9852 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9853 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9854 }
9855
9856 // Start from offset equal to beginning of free space.
9857 VkDeviceSize resultOffset = resultBaseOffset;
9858
9859 // Apply VMA_DEBUG_MARGIN at the beginning.
9860 if(VMA_DEBUG_MARGIN > 0)
9861 {
9862 resultOffset += VMA_DEBUG_MARGIN;
9863 }
9864
9865 // Apply alignment.
9866 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9867
9868 // Check previous suballocations for BufferImageGranularity conflicts.
9869 // Make bigger alignment if necessary.
9870 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9871 {
9872 bool bufferImageGranularityConflict = false;
9873 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9874 {
9875 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9876 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9877 {
9878 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9879 {
9880 bufferImageGranularityConflict = true;
9881 break;
9882 }
9883 }
9884 else
9885 // Already on previous page.
9886 break;
9887 }
9888 if(bufferImageGranularityConflict)
9889 {
9890 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9891 }
9892 }
9893
9894 pAllocationRequest->itemsToMakeLostCount = 0;
9895 pAllocationRequest->sumItemSize = 0;
9896 size_t index1st = m_1stNullItemsBeginCount;
9897
9898 if(canMakeOtherLost)
9899 {
9900 while(index1st < suballocations1st.size() &&
9901 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9902 {
9903 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9904 const VmaSuballocation& suballoc = suballocations1st[index1st];
9905 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9906 {
9907 // No problem.
9908 }
9909 else
9910 {
9911 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9912 if(suballoc.hAllocation->CanBecomeLost() &&
9913 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9914 {
9915 ++pAllocationRequest->itemsToMakeLostCount;
9916 pAllocationRequest->sumItemSize += suballoc.size;
9917 }
9918 else
9919 {
9920 return false;
9921 }
9922 }
9923 ++index1st;
9924 }
9925
9926 // Check next suballocations for BufferImageGranularity conflicts.
9927 // If conflict exists, we must mark more allocations lost or fail.
9928 if(bufferImageGranularity > 1)
9929 {
9930 while(index1st < suballocations1st.size())
9931 {
9932 const VmaSuballocation& suballoc = suballocations1st[index1st];
9933 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9934 {
9935 if(suballoc.hAllocation != VK_NULL_HANDLE)
9936 {
9937 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9938 if(suballoc.hAllocation->CanBecomeLost() &&
9939 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9940 {
9941 ++pAllocationRequest->itemsToMakeLostCount;
9942 pAllocationRequest->sumItemSize += suballoc.size;
9943 }
9944 else
9945 {
9946 return false;
9947 }
9948 }
9949 }
9950 else
9951 {
9952 // Already on next page.
9953 break;
9954 }
9955 ++index1st;
9956 }
9957 }
9958 }
9959
9960 // There is enough free space at the end after alignment.
9961 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9962 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9963 {
9964 // Check next suballocations for BufferImageGranularity conflicts.
9965 // If conflict exists, allocation cannot be made here.
9966 if(bufferImageGranularity > 1)
9967 {
9968 for(size_t nextSuballocIndex = index1st;
9969 nextSuballocIndex < suballocations1st.size();
9970 nextSuballocIndex++)
9971 {
9972 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9973 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9974 {
9975 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9976 {
9977 return false;
9978 }
9979 }
9980 else
9981 {
9982 // Already on next page.
9983 break;
9984 }
9985 }
9986 }
9987
9988 // All tests passed: Success.
9989 pAllocationRequest->offset = resultOffset;
9990 pAllocationRequest->sumFreeSize =
9991 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
9992 - resultBaseOffset
9993 - pAllocationRequest->sumItemSize;
9994 // pAllocationRequest->item unused.
9995 return true;
9996 }
9997 }
9998 }
9999
10000 return false;
10001}
10002
10003bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10004 uint32_t currentFrameIndex,
10005 uint32_t frameInUseCount,
10006 VmaAllocationRequest* pAllocationRequest)
10007{
10008 if(pAllocationRequest->itemsToMakeLostCount == 0)
10009 {
10010 return true;
10011 }
10012
10013 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10014
10015 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10016 size_t index1st = m_1stNullItemsBeginCount;
10017 size_t madeLostCount = 0;
10018 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10019 {
10020 VMA_ASSERT(index1st < suballocations1st.size());
10021 VmaSuballocation& suballoc = suballocations1st[index1st];
10022 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10023 {
10024 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10025 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10026 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10027 {
10028 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10029 suballoc.hAllocation = VK_NULL_HANDLE;
10030 m_SumFreeSize += suballoc.size;
10031 ++m_1stNullItemsMiddleCount;
10032 ++madeLostCount;
10033 }
10034 else
10035 {
10036 return false;
10037 }
10038 }
10039 ++index1st;
10040 }
10041
10042 CleanupAfterFree();
10043 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10044
10045 return true;
10046}
10047
10048uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10049{
10050 uint32_t lostAllocationCount = 0;
10051
10052 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10053 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10054 {
10055 VmaSuballocation& suballoc = suballocations1st[i];
10056 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10057 suballoc.hAllocation->CanBecomeLost() &&
10058 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10059 {
10060 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10061 suballoc.hAllocation = VK_NULL_HANDLE;
10062 ++m_1stNullItemsMiddleCount;
10063 m_SumFreeSize += suballoc.size;
10064 ++lostAllocationCount;
10065 }
10066 }
10067
10068 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10069 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10070 {
10071 VmaSuballocation& suballoc = suballocations2nd[i];
10072 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10073 suballoc.hAllocation->CanBecomeLost() &&
10074 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10075 {
10076 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10077 suballoc.hAllocation = VK_NULL_HANDLE;
10078 ++m_2ndNullItemsCount;
10079 ++lostAllocationCount;
10080 }
10081 }
10082
10083 if(lostAllocationCount)
10084 {
10085 CleanupAfterFree();
10086 }
10087
10088 return lostAllocationCount;
10089}
10090
10091VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10092{
10093 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10094 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10095 {
10096 const VmaSuballocation& suballoc = suballocations1st[i];
10097 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10098 {
10099 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10100 {
10101 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10102 return VK_ERROR_VALIDATION_FAILED_EXT;
10103 }
10104 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10105 {
10106 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10107 return VK_ERROR_VALIDATION_FAILED_EXT;
10108 }
10109 }
10110 }
10111
10112 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10113 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10114 {
10115 const VmaSuballocation& suballoc = suballocations2nd[i];
10116 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10117 {
10118 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10119 {
10120 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10121 return VK_ERROR_VALIDATION_FAILED_EXT;
10122 }
10123 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10124 {
10125 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10126 return VK_ERROR_VALIDATION_FAILED_EXT;
10127 }
10128 }
10129 }
10130
10131 return VK_SUCCESS;
10132}
10133
10134void VmaBlockMetadata_Linear::Alloc(
10135 const VmaAllocationRequest& request,
10136 VmaSuballocationType type,
10137 VkDeviceSize allocSize,
10138 bool upperAddress,
10139 VmaAllocation hAllocation)
10140{
10141 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10142
10143 if(upperAddress)
10144 {
10145 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10146 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10147 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10148 suballocations2nd.push_back(newSuballoc);
10149 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10150 }
10151 else
10152 {
10153 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10154
10155 // First allocation.
10156 if(suballocations1st.empty())
10157 {
10158 suballocations1st.push_back(newSuballoc);
10159 }
10160 else
10161 {
10162 // New allocation at the end of 1st vector.
10163 if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10164 {
10165 // Check if it fits before the end of the block.
10166 VMA_ASSERT(request.offset + allocSize <= GetSize());
10167 suballocations1st.push_back(newSuballoc);
10168 }
10169 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10170 else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10171 {
10172 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10173
10174 switch(m_2ndVectorMode)
10175 {
10176 case SECOND_VECTOR_EMPTY:
10177 // First allocation from second part ring buffer.
10178 VMA_ASSERT(suballocations2nd.empty());
10179 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10180 break;
10181 case SECOND_VECTOR_RING_BUFFER:
10182 // 2-part ring buffer is already started.
10183 VMA_ASSERT(!suballocations2nd.empty());
10184 break;
10185 case SECOND_VECTOR_DOUBLE_STACK:
10186 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10187 break;
10188 default:
10189 VMA_ASSERT(0);
10190 }
10191
10192 suballocations2nd.push_back(newSuballoc);
10193 }
10194 else
10195 {
10196 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10197 }
10198 }
10199 }
10200
10201 m_SumFreeSize -= newSuballoc.size;
10202}
10203
10204void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10205{
10206 FreeAtOffset(allocation->GetOffset());
10207}
10208
10209void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10210{
10211 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10212 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10213
10214 if(!suballocations1st.empty())
10215 {
10216 // First allocation: Mark it as next empty at the beginning.
10217 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10218 if(firstSuballoc.offset == offset)
10219 {
10220 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10221 firstSuballoc.hAllocation = VK_NULL_HANDLE;
10222 m_SumFreeSize += firstSuballoc.size;
10223 ++m_1stNullItemsBeginCount;
10224 CleanupAfterFree();
10225 return;
10226 }
10227 }
10228
10229 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10230 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10231 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10232 {
10233 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10234 if(lastSuballoc.offset == offset)
10235 {
10236 m_SumFreeSize += lastSuballoc.size;
10237 suballocations2nd.pop_back();
10238 CleanupAfterFree();
10239 return;
10240 }
10241 }
10242 // Last allocation in 1st vector.
10243 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10244 {
10245 VmaSuballocation& lastSuballoc = suballocations1st.back();
10246 if(lastSuballoc.offset == offset)
10247 {
10248 m_SumFreeSize += lastSuballoc.size;
10249 suballocations1st.pop_back();
10250 CleanupAfterFree();
10251 return;
10252 }
10253 }
10254
10255 // Item from the middle of 1st vector.
10256 {
10257 VmaSuballocation refSuballoc;
10258 refSuballoc.offset = offset;
10259 // Rest of members stays uninitialized intentionally for better performance.
10260 SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10261 suballocations1st.begin() + m_1stNullItemsBeginCount,
10262 suballocations1st.end(),
10263 refSuballoc);
10264 if(it != suballocations1st.end())
10265 {
10266 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10267 it->hAllocation = VK_NULL_HANDLE;
10268 ++m_1stNullItemsMiddleCount;
10269 m_SumFreeSize += it->size;
10270 CleanupAfterFree();
10271 return;
10272 }
10273 }
10274
10275 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10276 {
10277 // Item from the middle of 2nd vector.
10278 VmaSuballocation refSuballoc;
10279 refSuballoc.offset = offset;
10280 // Rest of members stays uninitialized intentionally for better performance.
10281 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10282 VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10283 VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10284 if(it != suballocations2nd.end())
10285 {
10286 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10287 it->hAllocation = VK_NULL_HANDLE;
10288 ++m_2ndNullItemsCount;
10289 m_SumFreeSize += it->size;
10290 CleanupAfterFree();
10291 return;
10292 }
10293 }
10294
10295 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10296}
10297
10298bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10299{
10300 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10301 const size_t suballocCount = AccessSuballocations1st().size();
10302 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10303}
10304
10305void VmaBlockMetadata_Linear::CleanupAfterFree()
10306{
10307 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10308 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10309
10310 if(IsEmpty())
10311 {
10312 suballocations1st.clear();
10313 suballocations2nd.clear();
10314 m_1stNullItemsBeginCount = 0;
10315 m_1stNullItemsMiddleCount = 0;
10316 m_2ndNullItemsCount = 0;
10317 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10318 }
10319 else
10320 {
10321 const size_t suballoc1stCount = suballocations1st.size();
10322 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10323 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10324
10325 // Find more null items at the beginning of 1st vector.
10326 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10327 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10328 {
10329 ++m_1stNullItemsBeginCount;
10330 --m_1stNullItemsMiddleCount;
10331 }
10332
10333 // Find more null items at the end of 1st vector.
10334 while(m_1stNullItemsMiddleCount > 0 &&
10335 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10336 {
10337 --m_1stNullItemsMiddleCount;
10338 suballocations1st.pop_back();
10339 }
10340
10341 // Find more null items at the end of 2nd vector.
10342 while(m_2ndNullItemsCount > 0 &&
10343 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10344 {
10345 --m_2ndNullItemsCount;
10346 suballocations2nd.pop_back();
10347 }
10348
10349 if(ShouldCompact1st())
10350 {
10351 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10352 size_t srcIndex = m_1stNullItemsBeginCount;
10353 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10354 {
10355 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10356 {
10357 ++srcIndex;
10358 }
10359 if(dstIndex != srcIndex)
10360 {
10361 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10362 }
10363 ++srcIndex;
10364 }
10365 suballocations1st.resize(nonNullItemCount);
10366 m_1stNullItemsBeginCount = 0;
10367 m_1stNullItemsMiddleCount = 0;
10368 }
10369
10370 // 2nd vector became empty.
10371 if(suballocations2nd.empty())
10372 {
10373 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10374 }
10375
10376 // 1st vector became empty.
10377 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10378 {
10379 suballocations1st.clear();
10380 m_1stNullItemsBeginCount = 0;
10381
10382 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10383 {
10384 // Swap 1st with 2nd. Now 2nd is empty.
10385 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10386 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10387 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10388 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10389 {
10390 ++m_1stNullItemsBeginCount;
10391 --m_1stNullItemsMiddleCount;
10392 }
10393 m_2ndNullItemsCount = 0;
10394 m_1stVectorIndex ^= 1;
10395 }
10396 }
10397 }
10398
10399 VMA_HEAVY_ASSERT(Validate());
10400}
10401
10402
10403////////////////////////////////////////////////////////////////////////////////
10404// class VmaBlockMetadata_Buddy
10405
10406VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10407 VmaBlockMetadata(hAllocator),
10408 m_Root(VMA_NULL),
10409 m_AllocationCount(0),
10410 m_FreeCount(1),
10411 m_SumFreeSize(0)
10412{
10413 memset(m_FreeList, 0, sizeof(m_FreeList));
10414}
10415
10416VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10417{
10418 DeleteNode(m_Root);
10419}
10420
10421void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10422{
10423 VmaBlockMetadata::Init(size);
10424
10425 m_UsableSize = VmaPrevPow2(size);
10426 m_SumFreeSize = m_UsableSize;
10427
10428 // Calculate m_LevelCount.
10429 m_LevelCount = 1;
10430 while(m_LevelCount < MAX_LEVELS &&
10431 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10432 {
10433 ++m_LevelCount;
10434 }
10435
10436 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10437 rootNode->offset = 0;
10438 rootNode->type = Node::TYPE_FREE;
10439 rootNode->parent = VMA_NULL;
10440 rootNode->buddy = VMA_NULL;
10441
10442 m_Root = rootNode;
10443 AddToFreeListFront(0, rootNode);
10444}
10445
10446bool VmaBlockMetadata_Buddy::Validate() const
10447{
10448 // Validate tree.
10449 ValidationContext ctx;
10450 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10451 {
10452 VMA_VALIDATE(false && "ValidateNode failed.");
10453 }
10454 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10455 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10456
10457 // Validate free node lists.
10458 for(uint32_t level = 0; level < m_LevelCount; ++level)
10459 {
10460 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10461 m_FreeList[level].front->free.prev == VMA_NULL);
10462
10463 for(Node* node = m_FreeList[level].front;
10464 node != VMA_NULL;
10465 node = node->free.next)
10466 {
10467 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10468
10469 if(node->free.next == VMA_NULL)
10470 {
10471 VMA_VALIDATE(m_FreeList[level].back == node);
10472 }
10473 else
10474 {
10475 VMA_VALIDATE(node->free.next->free.prev == node);
10476 }
10477 }
10478 }
10479
10480 // Validate that free lists ar higher levels are empty.
10481 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10482 {
10483 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10484 }
10485
10486 return true;
10487}
10488
10489VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10490{
10491 for(uint32_t level = 0; level < m_LevelCount; ++level)
10492 {
10493 if(m_FreeList[level].front != VMA_NULL)
10494 {
10495 return LevelToNodeSize(level);
10496 }
10497 }
10498 return 0;
10499}
10500
10501void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10502{
10503 const VkDeviceSize unusableSize = GetUnusableSize();
10504
10505 outInfo.blockCount = 1;
10506
10507 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10508 outInfo.usedBytes = outInfo.unusedBytes = 0;
10509
10510 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10511 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10512 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10513
10514 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10515
10516 if(unusableSize > 0)
10517 {
10518 ++outInfo.unusedRangeCount;
10519 outInfo.unusedBytes += unusableSize;
10520 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10521 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10522 }
10523}
10524
10525void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10526{
10527 const VkDeviceSize unusableSize = GetUnusableSize();
10528
10529 inoutStats.size += GetSize();
10530 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10531 inoutStats.allocationCount += m_AllocationCount;
10532 inoutStats.unusedRangeCount += m_FreeCount;
10533 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10534
10535 if(unusableSize > 0)
10536 {
10537 ++inoutStats.unusedRangeCount;
10538 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10539 }
10540}
10541
10542#if VMA_STATS_STRING_ENABLED
10543
10544void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10545{
10546 // TODO optimize
10547 VmaStatInfo stat;
10548 CalcAllocationStatInfo(stat);
10549
10550 PrintDetailedMap_Begin(
10551 json,
10552 stat.unusedBytes,
10553 stat.allocationCount,
10554 stat.unusedRangeCount);
10555
10556 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10557
10558 const VkDeviceSize unusableSize = GetUnusableSize();
10559 if(unusableSize > 0)
10560 {
10561 PrintDetailedMap_UnusedRange(json,
10562 m_UsableSize, // offset
10563 unusableSize); // size
10564 }
10565
10566 PrintDetailedMap_End(json);
10567}
10568
10569#endif // #if VMA_STATS_STRING_ENABLED
10570
10571bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10572 uint32_t currentFrameIndex,
10573 uint32_t frameInUseCount,
10574 VkDeviceSize bufferImageGranularity,
10575 VkDeviceSize allocSize,
10576 VkDeviceSize allocAlignment,
10577 bool upperAddress,
10578 VmaSuballocationType allocType,
10579 bool canMakeOtherLost,
10580 uint32_t strategy,
10581 VmaAllocationRequest* pAllocationRequest)
10582{
10583 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
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(currLevel, 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(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(childrenLevel, rightChild);
10690 AddToFreeListFront(childrenLevel, 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(currLevel, 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->split.leftChild->buddy);
10723 DeleteNode(node->split.leftChild);
10724 }
10725
10726 vma_delete(GetAllocationCallbacks(), 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, curr, leftChild, childrenLevel, 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, curr, rightChild, childrenLevel, 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(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->buddy);
10824 Node* const parent = node->parent;
10825
10826 vma_delete(GetAllocationCallbacks(), node->buddy);
10827 vma_delete(GetAllocationCallbacks(), 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, leftChild, childrenNodeSize);
10872 const Node* const rightChild = leftChild->buddy;
10873 CalcAllocationStatInfoNode(outInfo, rightChild, 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, node->offset, levelNodeSize);
10941 break;
10942 case Node::TYPE_ALLOCATION:
10943 {
10944 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10945 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10946 if(allocSize < levelNodeSize)
10947 {
10948 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, 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, leftChild, childrenNodeSize);
10957 const Node* const rightChild = leftChild->buddy;
10958 PrintDetailedMapNode(json, rightChild, 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(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(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11020 m_hMemory = VK_NULL_HANDLE;
11021
11022 vma_delete(allocator, 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, 1, &pData);
11038 if(res != VK_SUCCESS)
11039 {
11040 return res;
11041 }
11042
11043 res = m_pMetadata->CheckCorruption(pData);
11044
11045 Unmap(hAllocator, 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, 1, &pData);
11119 if(res != VK_SUCCESS)
11120 {
11121 return res;
11122 }
11123
11124 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11125 VmaWriteMagicValue(pData, allocOffset + allocSize);
11126
11127 Unmap(hAllocator, 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, 1, &pData);
11139 if(res != VK_SUCCESS)
11140 {
11141 return res;
11142 }
11143
11144 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11145 {
11146 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11147 }
11148 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11149 {
11150 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11151 }
11152
11153 Unmap(hAllocator, 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(&outInfo, 0, 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>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11215 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11216 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, 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(m_hAllocator);
11278 vma_delete(m_hAllocator, 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(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(*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 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(pAllocations[allocIndex]);
11365 }
11366 memset(pAllocations, 0, 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 pCurrBlock,
11444 hCurrentPool,
11445 currentFrameIndex,
11446 size,
11447 alignment,
11448 allocFlagsCopy,
11449 createInfo.pUserData,
11450 suballocType,
11451 strategy,
11452 pAllocation);
11453 if(res == VK_SUCCESS)
11454 {
11455 VMA_DEBUG_LOG(" Returned from 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 pCurrBlock,
11471 hCurrentPool,
11472 currentFrameIndex,
11473 size,
11474 alignment,
11475 allocFlagsCopy,
11476 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 pCurrBlock,
11496 hCurrentPool,
11497 currentFrameIndex,
11498 size,
11499 alignment,
11500 allocFlagsCopy,
11501 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(newBlockSize, &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(newBlockSize, &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 allocFlagsCopy,
11574 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 m_FrameInUseCount,
11614 m_BufferImageGranularity,
11615 size,
11616 alignment,
11617 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11618 suballocType,
11619 canMakeOtherLost,
11620 strategy,
11621 &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 m_FrameInUseCount,
11650 m_BufferImageGranularity,
11651 size,
11652 alignment,
11653 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11654 suballocType,
11655 canMakeOtherLost,
11656 strategy,
11657 &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(m_hAllocator, 1, VMA_NULL);
11683 if(res != VK_SUCCESS)
11684 {
11685 return res;
11686 }
11687 }
11688
11689 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11690 currentFrameIndex,
11691 m_FrameInUseCount,
11692 &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(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11702 (*pAllocation)->InitBlockAllocation(
11703 hCurrentPool,
11704 pBestRequestBlock,
11705 bestRequest.offset,
11706 alignment,
11707 size,
11708 suballocType,
11709 mapped,
11710 (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(m_hAllocator, createInfo.pUserData);
11714 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11715 {
11716 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11717 }
11718 if(IsCorruptionDetectionEnabled())
11719 {
11720 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11721 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11722 }
11723 return VK_SUCCESS;
11724 }
11725 // else: Some allocations must have been touched while we are here. Next try.
11726 }
11727 else
11728 {
11729 // Could not find place in any of the blocks - break outer loop.
11730 break;
11731 }
11732 }
11733 /* Maximum number of tries exceeded - a very unlike event when many other
11734 threads are simultaneously touching allocations making it impossible to make
11735 lost at the same time as we try to allocate. */
11736 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11737 {
11738 return VK_ERROR_TOO_MANY_OBJECTS;
11739 }
11740 }
11741
11742 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11743}
11744
11745void VmaBlockVector::Free(
11746 VmaAllocation hAllocation)
11747{
11748 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11749
11750 // Scope for lock.
11751 {
11752 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11753
11754 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11755
11756 if(IsCorruptionDetectionEnabled())
11757 {
11758 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11759 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11760 }
11761
11762 if(hAllocation->IsPersistentMap())
11763 {
11764 pBlock->Unmap(m_hAllocator, 1);
11765 }
11766
11767 pBlock->m_pMetadata->Free(hAllocation);
11768 VMA_HEAVY_ASSERT(pBlock->Validate());
11769
11770 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11771
11772 // pBlock became empty after this deallocation.
11773 if(pBlock->m_pMetadata->IsEmpty())
11774 {
11775 // Already has empty Allocation. We don't want to have two, so delete this one.
11776 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11777 {
11778 pBlockToDelete = pBlock;
11779 Remove(pBlock);
11780 }
11781 // We now have first empty block.
11782 else
11783 {
11784 m_HasEmptyBlock = true;
11785 }
11786 }
11787 // pBlock didn't become empty, but we have another empty block - find and free that one.
11788 // (This is optional, heuristics.)
11789 else if(m_HasEmptyBlock)
11790 {
11791 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11792 if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11793 {
11794 pBlockToDelete = pLastBlock;
11795 m_Blocks.pop_back();
11796 m_HasEmptyBlock = false;
11797 }
11798 }
11799
11800 IncrementallySortBlocks();
11801 }
11802
11803 // Destruction of a free Allocation. Deferred until this point, outside of mutex
11804 // lock, for performance reason.
11805 if(pBlockToDelete != VMA_NULL)
11806 {
11807 VMA_DEBUG_LOG(" Deleted empty allocation");
11808 pBlockToDelete->Destroy(m_hAllocator);
11809 vma_delete(m_hAllocator, pBlockToDelete);
11810 }
11811}
11812
11813VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11814{
11815 VkDeviceSize result = 0;
11816 for(size_t i = m_Blocks.size(); i--; )
11817 {
11818 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11819 if(result >= m_PreferredBlockSize)
11820 {
11821 break;
11822 }
11823 }
11824 return result;
11825}
11826
11827void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11828{
11829 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11830 {
11831 if(m_Blocks[blockIndex] == pBlock)
11832 {
11833 VmaVectorRemove(m_Blocks, blockIndex);
11834 return;
11835 }
11836 }
11837 VMA_ASSERT(0);
11838}
11839
11840void VmaBlockVector::IncrementallySortBlocks()
11841{
11842 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11843 {
11844 // Bubble sort only until first swap.
11845 for(size_t i = 1; i < m_Blocks.size(); ++i)
11846 {
11847 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11848 {
11849 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11850 return;
11851 }
11852 }
11853 }
11854}
11855
11856VkResult VmaBlockVector::AllocateFromBlock(
11857 VmaDeviceMemoryBlock* pBlock,
11858 VmaPool hCurrentPool,
11859 uint32_t currentFrameIndex,
11860 VkDeviceSize size,
11861 VkDeviceSize alignment,
11862 VmaAllocationCreateFlags allocFlags,
11863 void* pUserData,
11864 VmaSuballocationType suballocType,
11865 uint32_t strategy,
11866 VmaAllocation* pAllocation)
11867{
11868 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11869 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11870 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11871 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11872
11873 VmaAllocationRequest currRequest = {};
11874 if(pBlock->m_pMetadata->CreateAllocationRequest(
11875 currentFrameIndex,
11876 m_FrameInUseCount,
11877 m_BufferImageGranularity,
11878 size,
11879 alignment,
11880 isUpperAddress,
11881 suballocType,
11882 false, // canMakeOtherLost
11883 strategy,
11884 &currRequest))
11885 {
11886 // Allocate from pCurrBlock.
11887 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11888
11889 if(mapped)
11890 {
11891 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11892 if(res != VK_SUCCESS)
11893 {
11894 return res;
11895 }
11896 }
11897
11898 // We no longer have an empty Allocation.
11899 if(pBlock->m_pMetadata->IsEmpty())
11900 {
11901 m_HasEmptyBlock = false;
11902 }
11903
11904 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11905 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11906 (*pAllocation)->InitBlockAllocation(
11907 hCurrentPool,
11908 pBlock,
11909 currRequest.offset,
11910 alignment,
11911 size,
11912 suballocType,
11913 mapped,
11914 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11915 VMA_HEAVY_ASSERT(pBlock->Validate());
11916 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11917 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11918 {
11919 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11920 }
11921 if(IsCorruptionDetectionEnabled())
11922 {
11923 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11924 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11925 }
11926 return VK_SUCCESS;
11927 }
11928 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11929}
11930
11931VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11932{
11933 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11934 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11935 allocInfo.allocationSize = blockSize;
11936 VkDeviceMemory mem = VK_NULL_HANDLE;
11937 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11938 if(res < 0)
11939 {
11940 return res;
11941 }
11942
11943 // New VkDeviceMemory successfully created.
11944
11945 // Create new Allocation for it.
11946 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11947 pBlock->Init(
11948 m_hAllocator,
11949 m_MemoryTypeIndex,
11950 mem,
11951 allocInfo.allocationSize,
11952 m_NextBlockId++,
11953 m_Algorithm);
11954
11955 m_Blocks.push_back(pBlock);
11956 if(pNewBlockIndex != VMA_NULL)
11957 {
11958 *pNewBlockIndex = m_Blocks.size() - 1;
11959 }
11960
11961 return VK_SUCCESS;
11962}
11963
11964void VmaBlockVector::ApplyDefragmentationMovesCpu(
11965 class VmaBlockVectorDefragmentationContext* pDefragCtx,
11966 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11967{
11968 const size_t blockCount = m_Blocks.size();
11969 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11970
11971 enum BLOCK_FLAG
11972 {
11973 BLOCK_FLAG_USED = 0x00000001,
11974 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11975 };
11976
11977 struct BlockInfo
11978 {
11979 uint32_t flags;
11980 void* pMappedData;
11981 };
11982 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11983 blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11984 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
11985
11986 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11987 const size_t moveCount = moves.size();
11988 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11989 {
11990 const VmaDefragmentationMove& move = moves[moveIndex];
11991 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
11992 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
11993 }
11994
11995 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
11996
11997 // Go over all blocks. Get mapped pointer or map if necessary.
11998 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
11999 {
12000 BlockInfo& currBlockInfo = blockInfo[blockIndex];
12001 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12002 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12003 {
12004 currBlockInfo.pMappedData = pBlock->GetMappedData();
12005 // It is not originally mapped - map it.
12006 if(currBlockInfo.pMappedData == VMA_NULL)
12007 {
12008 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12009 if(pDefragCtx->res == VK_SUCCESS)
12010 {
12011 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12012 }
12013 }
12014 }
12015 }
12016
12017 // Go over all moves. Do actual data transfer.
12018 if(pDefragCtx->res == VK_SUCCESS)
12019 {
12020 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12021 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12022
12023 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12024 {
12025 const VmaDefragmentationMove& move = moves[moveIndex];
12026
12027 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12028 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12029
12030 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12031
12032 // Invalidate source.
12033 if(isNonCoherent)
12034 {
12035 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12036 memRange.memory = pSrcBlock->GetDeviceMemory();
12037 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12038 memRange.size = VMA_MIN(
12039 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12040 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12041 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12042 }
12043
12044 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12045 memmove(
12046 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12047 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12048 static_cast<size_t>(move.size));
12049
12050 if(IsCorruptionDetectionEnabled())
12051 {
12052 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12053 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12054 }
12055
12056 // Flush destination.
12057 if(isNonCoherent)
12058 {
12059 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12060 memRange.memory = pDstBlock->GetDeviceMemory();
12061 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12062 memRange.size = VMA_MIN(
12063 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12064 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12065 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12066 }
12067 }
12068 }
12069
12070 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12071 // Regardless of pCtx->res == VK_SUCCESS.
12072 for(size_t blockIndex = blockCount; blockIndex--; )
12073 {
12074 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12075 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12076 {
12077 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12078 pBlock->Unmap(m_hAllocator, 1);
12079 }
12080 }
12081}
12082
12083void VmaBlockVector::ApplyDefragmentationMovesGpu(
12084 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12085 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12086 VkCommandBuffer commandBuffer)
12087{
12088 const size_t blockCount = m_Blocks.size();
12089
12090 pDefragCtx->blockContexts.resize(blockCount);
12091 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12092
12093 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12094 const size_t moveCount = moves.size();
12095 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12096 {
12097 const VmaDefragmentationMove& move = moves[moveIndex];
12098 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12099 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12100 }
12101
12102 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12103
12104 // Go over all blocks. Create and bind buffer for whole block if necessary.
12105 {
12106 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12107 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12108 VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12109
12110 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12111 {
12112 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12113 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12114 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12115 {
12116 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12117 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12118 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12119 if(pDefragCtx->res == VK_SUCCESS)
12120 {
12121 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12122 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12123 }
12124 }
12125 }
12126 }
12127
12128 // Go over all moves. Post data transfer commands to command buffer.
12129 if(pDefragCtx->res == VK_SUCCESS)
12130 {
12131 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12132 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12133
12134 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12135 {
12136 const VmaDefragmentationMove& move = moves[moveIndex];
12137
12138 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12139 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12140
12141 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12142
12143 VkBufferCopy region = {
12144 move.srcOffset,
12145 move.dstOffset,
12146 move.size };
12147 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12148 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12149 }
12150 }
12151
12152 // Save buffers to defrag context for later destruction.
12153 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12154 {
12155 pDefragCtx->res = VK_NOT_READY;
12156 }
12157}
12158
12159void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12160{
12161 m_HasEmptyBlock = false;
12162 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12163 {
12164 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12165 if(pBlock->m_pMetadata->IsEmpty())
12166 {
12167 if(m_Blocks.size() > m_MinBlockCount)
12168 {
12169 if(pDefragmentationStats != VMA_NULL)
12170 {
12171 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12172 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12173 }
12174
12175 VmaVectorRemove(m_Blocks, blockIndex);
12176 pBlock->Destroy(m_hAllocator);
12177 vma_delete(m_hAllocator, pBlock);
12178 }
12179 else
12180 {
12181 m_HasEmptyBlock = true;
12182 }
12183 }
12184 }
12185}
12186
12187#if VMA_STATS_STRING_ENABLED
12188
12189void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12190{
12191 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12192
12193 json.BeginObject();
12194
12195 if(m_IsCustomPool)
12196 {
12197 json.WriteString("MemoryTypeIndex");
12198 json.WriteNumber(m_MemoryTypeIndex);
12199
12200 json.WriteString("BlockSize");
12201 json.WriteNumber(m_PreferredBlockSize);
12202
12203 json.WriteString("BlockCount");
12204 json.BeginObject(true);
12205 if(m_MinBlockCount > 0)
12206 {
12207 json.WriteString("Min");
12208 json.WriteNumber((uint64_t)m_MinBlockCount);
12209 }
12210 if(m_MaxBlockCount < SIZE_MAX)
12211 {
12212 json.WriteString("Max");
12213 json.WriteNumber((uint64_t)m_MaxBlockCount);
12214 }
12215 json.WriteString("Cur");
12216 json.WriteNumber((uint64_t)m_Blocks.size());
12217 json.EndObject();
12218
12219 if(m_FrameInUseCount > 0)
12220 {
12221 json.WriteString("FrameInUseCount");
12222 json.WriteNumber(m_FrameInUseCount);
12223 }
12224
12225 if(m_Algorithm != 0)
12226 {
12227 json.WriteString("Algorithm");
12228 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12229 }
12230 }
12231 else
12232 {
12233 json.WriteString("PreferredBlockSize");
12234 json.WriteNumber(m_PreferredBlockSize);
12235 }
12236
12237 json.WriteString("Blocks");
12238 json.BeginObject();
12239 for(size_t i = 0; i < m_Blocks.size(); ++i)
12240 {
12241 json.BeginString();
12242 json.ContinueString(m_Blocks[i]->GetId());
12243 json.EndString();
12244
12245 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12246 }
12247 json.EndObject();
12248
12249 json.EndObject();
12250}
12251
12252#endif // #if VMA_STATS_STRING_ENABLED
12253
12254void VmaBlockVector::Defragment(
12255 class VmaBlockVectorDefragmentationContext* pCtx,
12256 VmaDefragmentationStats* pStats,
12257 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12258 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12259 VkCommandBuffer commandBuffer)
12260{
12261 pCtx->res = VK_SUCCESS;
12262
12263 const VkMemoryPropertyFlags memPropFlags =
12264 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12265 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12266 const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12267
12268 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12269 isHostVisible;
12270 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12271 (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12272
12273 // There are options to defragment this memory type.
12274 if(canDefragmentOnCpu || canDefragmentOnGpu)
12275 {
12276 bool defragmentOnGpu;
12277 // There is only one option to defragment this memory type.
12278 if(canDefragmentOnGpu != canDefragmentOnCpu)
12279 {
12280 defragmentOnGpu = canDefragmentOnGpu;
12281 }
12282 // Both options are available: Heuristics to choose the best one.
12283 else
12284 {
12285 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12286 m_hAllocator->IsIntegratedGpu();
12287 }
12288
12289 bool overlappingMoveSupported = !defragmentOnGpu;
12290
12291 if(m_hAllocator->m_UseMutex)
12292 {
12293 m_Mutex.LockWrite();
12294 pCtx->mutexLocked = true;
12295 }
12296
12297 pCtx->Begin(overlappingMoveSupported);
12298
12299 // Defragment.
12300
12301 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12302 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12303 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12304 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12305 pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12306
12307 // Accumulate statistics.
12308 if(pStats != VMA_NULL)
12309 {
12310 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12311 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12312 pStats->bytesMoved += bytesMoved;
12313 pStats->allocationsMoved += allocationsMoved;
12314 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12315 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12316 if(defragmentOnGpu)
12317 {
12318 maxGpuBytesToMove -= bytesMoved;
12319 maxGpuAllocationsToMove -= allocationsMoved;
12320 }
12321 else
12322 {
12323 maxCpuBytesToMove -= bytesMoved;
12324 maxCpuAllocationsToMove -= allocationsMoved;
12325 }
12326 }
12327
12328 if(pCtx->res >= VK_SUCCESS)
12329 {
12330 if(defragmentOnGpu)
12331 {
12332 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12333 }
12334 else
12335 {
12336 ApplyDefragmentationMovesCpu(pCtx, moves);
12337 }
12338 }
12339 }
12340}
12341
12342void VmaBlockVector::DefragmentationEnd(
12343 class VmaBlockVectorDefragmentationContext* pCtx,
12344 VmaDefragmentationStats* pStats)
12345{
12346 // Destroy buffers.
12347 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12348 {
12349 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12350 if(blockCtx.hBuffer)
12351 {
12352 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12353 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12354 }
12355 }
12356
12357 if(pCtx->res >= VK_SUCCESS)
12358 {
12359 FreeEmptyBlocks(pStats);
12360 }
12361
12362 if(pCtx->mutexLocked)
12363 {
12364 VMA_ASSERT(m_hAllocator->m_UseMutex);
12365 m_Mutex.UnlockWrite();
12366 }
12367}
12368
12369size_t VmaBlockVector::CalcAllocationCount() const
12370{
12371 size_t result = 0;
12372 for(size_t i = 0; i < m_Blocks.size(); ++i)
12373 {
12374 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12375 }
12376 return result;
12377}
12378
12379bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12380{
12381 if(m_BufferImageGranularity == 1)
12382 {
12383 return false;
12384 }
12385 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12386 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12387 {
12388 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12389 VMA_ASSERT(m_Algorithm == 0);
12390 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12391 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12392 {
12393 return true;
12394 }
12395 }
12396 return false;
12397}
12398
12399void VmaBlockVector::MakePoolAllocationsLost(
12400 uint32_t currentFrameIndex,
12401 size_t* pLostAllocationCount)
12402{
12403 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12404 size_t lostAllocationCount = 0;
12405 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12406 {
12407 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12408 VMA_ASSERT(pBlock);
12409 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12410 }
12411 if(pLostAllocationCount != VMA_NULL)
12412 {
12413 *pLostAllocationCount = lostAllocationCount;
12414 }
12415}
12416
12417VkResult VmaBlockVector::CheckCorruption()
12418{
12419 if(!IsCorruptionDetectionEnabled())
12420 {
12421 return VK_ERROR_FEATURE_NOT_PRESENT;
12422 }
12423
12424 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12425 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12426 {
12427 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12428 VMA_ASSERT(pBlock);
12429 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12430 if(res != VK_SUCCESS)
12431 {
12432 return res;
12433 }
12434 }
12435 return VK_SUCCESS;
12436}
12437
12438void VmaBlockVector::AddStats(VmaStats* pStats)
12439{
12440 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12441 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12442
12443 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12444
12445 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12446 {
12447 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12448 VMA_ASSERT(pBlock);
12449 VMA_HEAVY_ASSERT(pBlock->Validate());
12450 VmaStatInfo allocationStatInfo;
12451 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12452 VmaAddStatInfo(pStats->total, allocationStatInfo);
12453 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12454 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12455 }
12456}
12457
12458////////////////////////////////////////////////////////////////////////////////
12459// VmaDefragmentationAlgorithm_Generic members definition
12460
12461VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12462 VmaAllocator hAllocator,
12463 VmaBlockVector* pBlockVector,
12464 uint32_t currentFrameIndex,
12465 bool overlappingMoveSupported) :
12466 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060012467 m_AllocationCount(0),
Tony-LunarG390319b2019-03-18 15:54:16 -060012468 m_AllAllocations(false),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060012469 m_BytesMoved(0),
12470 m_AllocationsMoved(0),
12471 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12472{
12473 // Create block info for each block.
12474 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12475 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12476 {
12477 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12478 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12479 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12480 m_Blocks.push_back(pBlockInfo);
12481 }
12482
12483 // Sort them by m_pBlock pointer value.
12484 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12485}
12486
12487VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12488{
12489 for(size_t i = m_Blocks.size(); i--; )
12490 {
12491 vma_delete(m_hAllocator, m_Blocks[i]);
12492 }
12493}
12494
12495void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12496{
12497 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12498 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12499 {
12500 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12501 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12502 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12503 {
12504 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12505 (*it)->m_Allocations.push_back(allocInfo);
12506 }
12507 else
12508 {
12509 VMA_ASSERT(0);
12510 }
12511
12512 ++m_AllocationCount;
12513 }
12514}
12515
12516VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12517 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12518 VkDeviceSize maxBytesToMove,
12519 uint32_t maxAllocationsToMove)
12520{
12521 if(m_Blocks.empty())
12522 {
12523 return VK_SUCCESS;
12524 }
12525
12526 // This is a choice based on research.
12527 // Option 1:
12528 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12529 // Option 2:
12530 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12531 // Option 3:
12532 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12533
12534 size_t srcBlockMinIndex = 0;
12535 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12536 /*
12537 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12538 {
12539 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12540 if(blocksWithNonMovableCount > 0)
12541 {
12542 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12543 }
12544 }
12545 */
12546
12547 size_t srcBlockIndex = m_Blocks.size() - 1;
12548 size_t srcAllocIndex = SIZE_MAX;
12549 for(;;)
12550 {
12551 // 1. Find next allocation to move.
12552 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12553 // 1.2. Then start from last to first m_Allocations.
12554 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12555 {
12556 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12557 {
12558 // Finished: no more allocations to process.
12559 if(srcBlockIndex == srcBlockMinIndex)
12560 {
12561 return VK_SUCCESS;
12562 }
12563 else
12564 {
12565 --srcBlockIndex;
12566 srcAllocIndex = SIZE_MAX;
12567 }
12568 }
12569 else
12570 {
12571 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12572 }
12573 }
12574
12575 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12576 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12577
12578 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12579 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12580 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12581 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12582
12583 // 2. Try to find new place for this allocation in preceding or current block.
12584 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12585 {
12586 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12587 VmaAllocationRequest dstAllocRequest;
12588 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12589 m_CurrentFrameIndex,
12590 m_pBlockVector->GetFrameInUseCount(),
12591 m_pBlockVector->GetBufferImageGranularity(),
12592 size,
12593 alignment,
12594 false, // upperAddress
12595 suballocType,
12596 false, // canMakeOtherLost
12597 strategy,
12598 &dstAllocRequest) &&
12599 MoveMakesSense(
12600 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12601 {
12602 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12603
12604 // Reached limit on number of allocations or bytes to move.
12605 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12606 (m_BytesMoved + size > maxBytesToMove))
12607 {
12608 return VK_SUCCESS;
12609 }
12610
12611 VmaDefragmentationMove move;
12612 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12613 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12614 move.srcOffset = srcOffset;
12615 move.dstOffset = dstAllocRequest.offset;
12616 move.size = size;
12617 moves.push_back(move);
12618
12619 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12620 dstAllocRequest,
12621 suballocType,
12622 size,
12623 false, // upperAddress
12624 allocInfo.m_hAllocation);
12625 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12626
12627 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12628
12629 if(allocInfo.m_pChanged != VMA_NULL)
12630 {
12631 *allocInfo.m_pChanged = VK_TRUE;
12632 }
12633
12634 ++m_AllocationsMoved;
12635 m_BytesMoved += size;
12636
12637 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12638
12639 break;
12640 }
12641 }
12642
12643 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12644
12645 if(srcAllocIndex > 0)
12646 {
12647 --srcAllocIndex;
12648 }
12649 else
12650 {
12651 if(srcBlockIndex > 0)
12652 {
12653 --srcBlockIndex;
12654 srcAllocIndex = SIZE_MAX;
12655 }
12656 else
12657 {
12658 return VK_SUCCESS;
12659 }
12660 }
12661 }
12662}
12663
12664size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12665{
12666 size_t result = 0;
12667 for(size_t i = 0; i < m_Blocks.size(); ++i)
12668 {
12669 if(m_Blocks[i]->m_HasNonMovableAllocations)
12670 {
12671 ++result;
12672 }
12673 }
12674 return result;
12675}
12676
12677VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12678 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12679 VkDeviceSize maxBytesToMove,
12680 uint32_t maxAllocationsToMove)
12681{
12682 if(!m_AllAllocations && m_AllocationCount == 0)
12683 {
12684 return VK_SUCCESS;
12685 }
12686
12687 const size_t blockCount = m_Blocks.size();
12688 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12689 {
12690 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12691
12692 if(m_AllAllocations)
12693 {
12694 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12695 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12696 it != pMetadata->m_Suballocations.end();
12697 ++it)
12698 {
12699 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12700 {
12701 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12702 pBlockInfo->m_Allocations.push_back(allocInfo);
12703 }
12704 }
12705 }
12706
12707 pBlockInfo->CalcHasNonMovableAllocations();
12708
12709 // This is a choice based on research.
12710 // Option 1:
12711 pBlockInfo->SortAllocationsByOffsetDescending();
12712 // Option 2:
12713 //pBlockInfo->SortAllocationsBySizeDescending();
12714 }
12715
12716 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12717 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12718
12719 // This is a choice based on research.
12720 const uint32_t roundCount = 2;
12721
12722 // Execute defragmentation rounds (the main part).
12723 VkResult result = VK_SUCCESS;
12724 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12725 {
12726 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12727 }
12728
12729 return result;
12730}
12731
12732bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12733 size_t dstBlockIndex, VkDeviceSize dstOffset,
12734 size_t srcBlockIndex, VkDeviceSize srcOffset)
12735{
12736 if(dstBlockIndex < srcBlockIndex)
12737 {
12738 return true;
12739 }
12740 if(dstBlockIndex > srcBlockIndex)
12741 {
12742 return false;
12743 }
12744 if(dstOffset < srcOffset)
12745 {
12746 return true;
12747 }
12748 return false;
12749}
12750
12751////////////////////////////////////////////////////////////////////////////////
12752// VmaDefragmentationAlgorithm_Fast
12753
12754VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12755 VmaAllocator hAllocator,
12756 VmaBlockVector* pBlockVector,
12757 uint32_t currentFrameIndex,
12758 bool overlappingMoveSupported) :
12759 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12760 m_OverlappingMoveSupported(overlappingMoveSupported),
12761 m_AllocationCount(0),
12762 m_AllAllocations(false),
12763 m_BytesMoved(0),
12764 m_AllocationsMoved(0),
12765 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12766{
12767 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12768
12769}
12770
12771VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12772{
12773}
12774
12775VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12776 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12777 VkDeviceSize maxBytesToMove,
12778 uint32_t maxAllocationsToMove)
12779{
12780 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12781
12782 const size_t blockCount = m_pBlockVector->GetBlockCount();
12783 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12784 {
12785 return VK_SUCCESS;
12786 }
12787
12788 PreprocessMetadata();
12789
12790 // Sort blocks in order from most destination.
12791
12792 m_BlockInfos.resize(blockCount);
12793 for(size_t i = 0; i < blockCount; ++i)
12794 {
12795 m_BlockInfos[i].origBlockIndex = i;
12796 }
12797
12798 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12799 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12800 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12801 });
12802
12803 // THE MAIN ALGORITHM
12804
12805 FreeSpaceDatabase freeSpaceDb;
12806
12807 size_t dstBlockInfoIndex = 0;
12808 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12809 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12810 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12811 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12812 VkDeviceSize dstOffset = 0;
12813
12814 bool end = false;
12815 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12816 {
12817 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12818 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12819 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12820 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12821 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12822 {
12823 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12824 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12825 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12826 if(m_AllocationsMoved == maxAllocationsToMove ||
12827 m_BytesMoved + srcAllocSize > maxBytesToMove)
12828 {
12829 end = true;
12830 break;
12831 }
12832 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12833
12834 // Try to place it in one of free spaces from the database.
12835 size_t freeSpaceInfoIndex;
12836 VkDeviceSize dstAllocOffset;
12837 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12838 freeSpaceInfoIndex, dstAllocOffset))
12839 {
12840 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12841 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12842 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12843 VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12844
12845 // Same block
12846 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12847 {
12848 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12849
12850 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12851
12852 VmaSuballocation suballoc = *srcSuballocIt;
12853 suballoc.offset = dstAllocOffset;
12854 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12855 m_BytesMoved += srcAllocSize;
12856 ++m_AllocationsMoved;
12857
12858 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12859 ++nextSuballocIt;
12860 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12861 srcSuballocIt = nextSuballocIt;
12862
12863 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12864
12865 VmaDefragmentationMove move = {
12866 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12867 srcAllocOffset, dstAllocOffset,
12868 srcAllocSize };
12869 moves.push_back(move);
12870 }
12871 // Different block
12872 else
12873 {
12874 // MOVE OPTION 2: Move the allocation to a different block.
12875
12876 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12877
12878 VmaSuballocation suballoc = *srcSuballocIt;
12879 suballoc.offset = dstAllocOffset;
12880 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12881 m_BytesMoved += srcAllocSize;
12882 ++m_AllocationsMoved;
12883
12884 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12885 ++nextSuballocIt;
12886 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12887 srcSuballocIt = nextSuballocIt;
12888
12889 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12890
12891 VmaDefragmentationMove move = {
12892 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12893 srcAllocOffset, dstAllocOffset,
12894 srcAllocSize };
12895 moves.push_back(move);
12896 }
12897 }
12898 else
12899 {
12900 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12901
12902 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12903 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12904 dstAllocOffset + srcAllocSize > dstBlockSize)
12905 {
12906 // But before that, register remaining free space at the end of dst block.
12907 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12908
12909 ++dstBlockInfoIndex;
12910 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12911 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12912 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12913 dstBlockSize = pDstMetadata->GetSize();
12914 dstOffset = 0;
12915 dstAllocOffset = 0;
12916 }
12917
12918 // Same block
12919 if(dstBlockInfoIndex == srcBlockInfoIndex)
12920 {
12921 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12922
12923 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12924
12925 bool skipOver = overlap;
12926 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12927 {
12928 // If destination and source place overlap, skip if it would move it
12929 // by only < 1/64 of its size.
12930 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12931 }
12932
12933 if(skipOver)
12934 {
12935 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12936
12937 dstOffset = srcAllocOffset + srcAllocSize;
12938 ++srcSuballocIt;
12939 }
12940 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12941 else
12942 {
12943 srcSuballocIt->offset = dstAllocOffset;
12944 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12945 dstOffset = dstAllocOffset + srcAllocSize;
12946 m_BytesMoved += srcAllocSize;
12947 ++m_AllocationsMoved;
12948 ++srcSuballocIt;
12949 VmaDefragmentationMove move = {
12950 srcOrigBlockIndex, dstOrigBlockIndex,
12951 srcAllocOffset, dstAllocOffset,
12952 srcAllocSize };
12953 moves.push_back(move);
12954 }
12955 }
12956 // Different block
12957 else
12958 {
12959 // MOVE OPTION 2: Move the allocation to a different block.
12960
12961 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12962 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12963
12964 VmaSuballocation suballoc = *srcSuballocIt;
12965 suballoc.offset = dstAllocOffset;
12966 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12967 dstOffset = dstAllocOffset + srcAllocSize;
12968 m_BytesMoved += srcAllocSize;
12969 ++m_AllocationsMoved;
12970
12971 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12972 ++nextSuballocIt;
12973 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12974 srcSuballocIt = nextSuballocIt;
12975
12976 pDstMetadata->m_Suballocations.push_back(suballoc);
12977
12978 VmaDefragmentationMove move = {
12979 srcOrigBlockIndex, dstOrigBlockIndex,
12980 srcAllocOffset, dstAllocOffset,
12981 srcAllocSize };
12982 moves.push_back(move);
12983 }
12984 }
12985 }
12986 }
12987
12988 m_BlockInfos.clear();
12989
12990 PostprocessMetadata();
12991
12992 return VK_SUCCESS;
12993}
12994
12995void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
12996{
12997 const size_t blockCount = m_pBlockVector->GetBlockCount();
12998 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12999 {
13000 VmaBlockMetadata_Generic* const pMetadata =
13001 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13002 pMetadata->m_FreeCount = 0;
13003 pMetadata->m_SumFreeSize = pMetadata->GetSize();
13004 pMetadata->m_FreeSuballocationsBySize.clear();
13005 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13006 it != pMetadata->m_Suballocations.end(); )
13007 {
13008 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13009 {
13010 VmaSuballocationList::iterator nextIt = it;
13011 ++nextIt;
13012 pMetadata->m_Suballocations.erase(it);
13013 it = nextIt;
13014 }
13015 else
13016 {
13017 ++it;
13018 }
13019 }
13020 }
13021}
13022
13023void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13024{
13025 const size_t blockCount = m_pBlockVector->GetBlockCount();
13026 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13027 {
13028 VmaBlockMetadata_Generic* const pMetadata =
13029 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13030 const VkDeviceSize blockSize = pMetadata->GetSize();
13031
13032 // No allocations in this block - entire area is free.
13033 if(pMetadata->m_Suballocations.empty())
13034 {
13035 pMetadata->m_FreeCount = 1;
13036 //pMetadata->m_SumFreeSize is already set to blockSize.
13037 VmaSuballocation suballoc = {
13038 0, // offset
13039 blockSize, // size
13040 VMA_NULL, // hAllocation
13041 VMA_SUBALLOCATION_TYPE_FREE };
13042 pMetadata->m_Suballocations.push_back(suballoc);
13043 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13044 }
13045 // There are some allocations in this block.
13046 else
13047 {
13048 VkDeviceSize offset = 0;
13049 VmaSuballocationList::iterator it;
13050 for(it = pMetadata->m_Suballocations.begin();
13051 it != pMetadata->m_Suballocations.end();
13052 ++it)
13053 {
13054 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13055 VMA_ASSERT(it->offset >= offset);
13056
13057 // Need to insert preceding free space.
13058 if(it->offset > offset)
13059 {
13060 ++pMetadata->m_FreeCount;
13061 const VkDeviceSize freeSize = it->offset - offset;
13062 VmaSuballocation suballoc = {
13063 offset, // offset
13064 freeSize, // size
13065 VMA_NULL, // hAllocation
13066 VMA_SUBALLOCATION_TYPE_FREE };
13067 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13068 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13069 {
13070 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13071 }
13072 }
13073
13074 pMetadata->m_SumFreeSize -= it->size;
13075 offset = it->offset + it->size;
13076 }
13077
13078 // Need to insert trailing free space.
13079 if(offset < blockSize)
13080 {
13081 ++pMetadata->m_FreeCount;
13082 const VkDeviceSize freeSize = blockSize - offset;
13083 VmaSuballocation suballoc = {
13084 offset, // offset
13085 freeSize, // size
13086 VMA_NULL, // hAllocation
13087 VMA_SUBALLOCATION_TYPE_FREE };
13088 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13089 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13090 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13091 {
13092 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13093 }
13094 }
13095
13096 VMA_SORT(
13097 pMetadata->m_FreeSuballocationsBySize.begin(),
13098 pMetadata->m_FreeSuballocationsBySize.end(),
13099 VmaSuballocationItemSizeLess());
13100 }
13101
13102 VMA_HEAVY_ASSERT(pMetadata->Validate());
13103 }
13104}
13105
13106void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13107{
13108 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13109 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13110 while(it != pMetadata->m_Suballocations.end())
13111 {
13112 if(it->offset < suballoc.offset)
13113 {
13114 ++it;
13115 }
13116 }
13117 pMetadata->m_Suballocations.insert(it, suballoc);
13118}
13119
13120////////////////////////////////////////////////////////////////////////////////
13121// VmaBlockVectorDefragmentationContext
13122
13123VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13124 VmaAllocator hAllocator,
13125 VmaPool hCustomPool,
13126 VmaBlockVector* pBlockVector,
13127 uint32_t currFrameIndex,
13128 uint32_t algorithmFlags) :
13129 res(VK_SUCCESS),
13130 mutexLocked(false),
13131 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13132 m_hAllocator(hAllocator),
13133 m_hCustomPool(hCustomPool),
13134 m_pBlockVector(pBlockVector),
13135 m_CurrFrameIndex(currFrameIndex),
Mike Schuchardte48dc142019-04-18 09:12:03 -070013136 //m_AlgorithmFlags(algorithmFlags),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060013137 m_pAlgorithm(VMA_NULL),
13138 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13139 m_AllAllocations(false)
13140{
13141}
13142
13143VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13144{
13145 vma_delete(m_hAllocator, m_pAlgorithm);
13146}
13147
13148void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13149{
13150 AllocInfo info = { hAlloc, pChanged };
13151 m_Allocations.push_back(info);
13152}
13153
13154void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13155{
13156 const bool allAllocations = m_AllAllocations ||
13157 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13158
13159 /********************************
13160 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13161 ********************************/
13162
13163 /*
13164 Fast algorithm is supported only when certain criteria are met:
13165 - VMA_DEBUG_MARGIN is 0.
13166 - All allocations in this block vector are moveable.
13167 - There is no possibility of image/buffer granularity conflict.
13168 */
13169 if(VMA_DEBUG_MARGIN == 0 &&
13170 allAllocations &&
13171 !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13172 {
13173 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13174 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13175 }
13176 else
13177 {
13178 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13179 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13180 }
13181
13182 if(allAllocations)
13183 {
13184 m_pAlgorithm->AddAll();
13185 }
13186 else
13187 {
13188 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13189 {
13190 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13191 }
13192 }
13193}
13194
13195////////////////////////////////////////////////////////////////////////////////
13196// VmaDefragmentationContext
13197
13198VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13199 VmaAllocator hAllocator,
13200 uint32_t currFrameIndex,
13201 uint32_t flags,
13202 VmaDefragmentationStats* pStats) :
13203 m_hAllocator(hAllocator),
13204 m_CurrFrameIndex(currFrameIndex),
13205 m_Flags(flags),
13206 m_pStats(pStats),
13207 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13208{
13209 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13210}
13211
13212VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13213{
13214 for(size_t i = m_CustomPoolContexts.size(); i--; )
13215 {
13216 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13217 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13218 vma_delete(m_hAllocator, pBlockVectorCtx);
13219 }
13220 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13221 {
13222 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13223 if(pBlockVectorCtx)
13224 {
13225 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13226 vma_delete(m_hAllocator, pBlockVectorCtx);
13227 }
13228 }
13229}
13230
13231void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13232{
13233 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13234 {
13235 VmaPool pool = pPools[poolIndex];
13236 VMA_ASSERT(pool);
13237 // Pools with algorithm other than default are not defragmented.
13238 if(pool->m_BlockVector.GetAlgorithm() == 0)
13239 {
13240 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13241
13242 for(size_t i = m_CustomPoolContexts.size(); i--; )
13243 {
13244 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13245 {
13246 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13247 break;
13248 }
13249 }
13250
13251 if(!pBlockVectorDefragCtx)
13252 {
13253 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13254 m_hAllocator,
13255 pool,
13256 &pool->m_BlockVector,
13257 m_CurrFrameIndex,
13258 m_Flags);
13259 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13260 }
13261
13262 pBlockVectorDefragCtx->AddAll();
13263 }
13264 }
13265}
13266
13267void VmaDefragmentationContext_T::AddAllocations(
13268 uint32_t allocationCount,
13269 VmaAllocation* pAllocations,
13270 VkBool32* pAllocationsChanged)
13271{
13272 // Dispatch pAllocations among defragmentators. Create them when necessary.
13273 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13274 {
13275 const VmaAllocation hAlloc = pAllocations[allocIndex];
13276 VMA_ASSERT(hAlloc);
13277 // DedicatedAlloc cannot be defragmented.
13278 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13279 // Lost allocation cannot be defragmented.
13280 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13281 {
13282 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13283
13284 const VmaPool hAllocPool = hAlloc->GetPool();
13285 // This allocation belongs to custom pool.
13286 if(hAllocPool != VK_NULL_HANDLE)
13287 {
13288 // Pools with algorithm other than default are not defragmented.
13289 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13290 {
13291 for(size_t i = m_CustomPoolContexts.size(); i--; )
13292 {
13293 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13294 {
13295 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13296 break;
13297 }
13298 }
13299 if(!pBlockVectorDefragCtx)
13300 {
13301 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13302 m_hAllocator,
13303 hAllocPool,
13304 &hAllocPool->m_BlockVector,
13305 m_CurrFrameIndex,
13306 m_Flags);
13307 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13308 }
13309 }
13310 }
13311 // This allocation belongs to default pool.
13312 else
13313 {
13314 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13315 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13316 if(!pBlockVectorDefragCtx)
13317 {
13318 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13319 m_hAllocator,
13320 VMA_NULL, // hCustomPool
13321 m_hAllocator->m_pBlockVectors[memTypeIndex],
13322 m_CurrFrameIndex,
13323 m_Flags);
13324 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13325 }
13326 }
13327
13328 if(pBlockVectorDefragCtx)
13329 {
13330 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13331 &pAllocationsChanged[allocIndex] : VMA_NULL;
13332 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13333 }
13334 }
13335 }
13336}
13337
13338VkResult VmaDefragmentationContext_T::Defragment(
13339 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13340 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13341 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13342{
13343 if(pStats)
13344 {
13345 memset(pStats, 0, sizeof(VmaDefragmentationStats));
13346 }
13347
13348 if(commandBuffer == VK_NULL_HANDLE)
13349 {
13350 maxGpuBytesToMove = 0;
13351 maxGpuAllocationsToMove = 0;
13352 }
13353
13354 VkResult res = VK_SUCCESS;
13355
13356 // Process default pools.
13357 for(uint32_t memTypeIndex = 0;
13358 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13359 ++memTypeIndex)
13360 {
13361 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13362 if(pBlockVectorCtx)
13363 {
13364 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13365 pBlockVectorCtx->GetBlockVector()->Defragment(
13366 pBlockVectorCtx,
13367 pStats,
13368 maxCpuBytesToMove, maxCpuAllocationsToMove,
13369 maxGpuBytesToMove, maxGpuAllocationsToMove,
13370 commandBuffer);
13371 if(pBlockVectorCtx->res != VK_SUCCESS)
13372 {
13373 res = pBlockVectorCtx->res;
13374 }
13375 }
13376 }
13377
13378 // Process custom pools.
13379 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13380 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13381 ++customCtxIndex)
13382 {
13383 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13384 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13385 pBlockVectorCtx->GetBlockVector()->Defragment(
13386 pBlockVectorCtx,
13387 pStats,
13388 maxCpuBytesToMove, maxCpuAllocationsToMove,
13389 maxGpuBytesToMove, maxGpuAllocationsToMove,
13390 commandBuffer);
13391 if(pBlockVectorCtx->res != VK_SUCCESS)
13392 {
13393 res = pBlockVectorCtx->res;
13394 }
13395 }
13396
13397 return res;
13398}
13399
13400////////////////////////////////////////////////////////////////////////////////
13401// VmaRecorder
13402
13403#if VMA_RECORDING_ENABLED
13404
13405VmaRecorder::VmaRecorder() :
13406 m_UseMutex(true),
13407 m_Flags(0),
13408 m_File(VMA_NULL),
13409 m_Freq(INT64_MAX),
13410 m_StartCounter(INT64_MAX)
13411{
13412}
13413
13414VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13415{
13416 m_UseMutex = useMutex;
13417 m_Flags = settings.flags;
13418
13419 QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13420 QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13421
13422 // Open file for writing.
13423 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13424 if(err != 0)
13425 {
13426 return VK_ERROR_INITIALIZATION_FAILED;
13427 }
13428
13429 // Write header.
13430 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13431 fprintf(m_File, "%s\n", "1,5");
13432
13433 return VK_SUCCESS;
13434}
13435
13436VmaRecorder::~VmaRecorder()
13437{
13438 if(m_File != VMA_NULL)
13439 {
13440 fclose(m_File);
13441 }
13442}
13443
13444void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
13445{
13446 CallParams callParams;
13447 GetBasicParams(callParams);
13448
13449 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13450 fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13451 Flush();
13452}
13453
13454void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13455{
13456 CallParams callParams;
13457 GetBasicParams(callParams);
13458
13459 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13460 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13461 Flush();
13462}
13463
13464void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13465{
13466 CallParams callParams;
13467 GetBasicParams(callParams);
13468
13469 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13470 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13471 createInfo.memoryTypeIndex,
13472 createInfo.flags,
13473 createInfo.blockSize,
13474 (uint64_t)createInfo.minBlockCount,
13475 (uint64_t)createInfo.maxBlockCount,
13476 createInfo.frameInUseCount,
13477 pool);
13478 Flush();
13479}
13480
13481void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13482{
13483 CallParams callParams;
13484 GetBasicParams(callParams);
13485
13486 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13487 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13488 pool);
13489 Flush();
13490}
13491
13492void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13493 const VkMemoryRequirements& vkMemReq,
13494 const VmaAllocationCreateInfo& createInfo,
13495 VmaAllocation allocation)
13496{
13497 CallParams callParams;
13498 GetBasicParams(callParams);
13499
13500 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13501 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13502 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13503 vkMemReq.size,
13504 vkMemReq.alignment,
13505 vkMemReq.memoryTypeBits,
13506 createInfo.flags,
13507 createInfo.usage,
13508 createInfo.requiredFlags,
13509 createInfo.preferredFlags,
13510 createInfo.memoryTypeBits,
13511 createInfo.pool,
13512 allocation,
13513 userDataStr.GetString());
13514 Flush();
13515}
13516
13517void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13518 const VkMemoryRequirements& vkMemReq,
13519 const VmaAllocationCreateInfo& createInfo,
13520 uint64_t allocationCount,
13521 const VmaAllocation* pAllocations)
13522{
13523 CallParams callParams;
13524 GetBasicParams(callParams);
13525
13526 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13527 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13528 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13529 vkMemReq.size,
13530 vkMemReq.alignment,
13531 vkMemReq.memoryTypeBits,
13532 createInfo.flags,
13533 createInfo.usage,
13534 createInfo.requiredFlags,
13535 createInfo.preferredFlags,
13536 createInfo.memoryTypeBits,
13537 createInfo.pool);
13538 PrintPointerList(allocationCount, pAllocations);
13539 fprintf(m_File, ",%s\n", userDataStr.GetString());
13540 Flush();
13541}
13542
13543void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13544 const VkMemoryRequirements& vkMemReq,
13545 bool requiresDedicatedAllocation,
13546 bool prefersDedicatedAllocation,
13547 const VmaAllocationCreateInfo& createInfo,
13548 VmaAllocation allocation)
13549{
13550 CallParams callParams;
13551 GetBasicParams(callParams);
13552
13553 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13554 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13555 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,
13556 vkMemReq.size,
13557 vkMemReq.alignment,
13558 vkMemReq.memoryTypeBits,
13559 requiresDedicatedAllocation ? 1 : 0,
13560 prefersDedicatedAllocation ? 1 : 0,
13561 createInfo.flags,
13562 createInfo.usage,
13563 createInfo.requiredFlags,
13564 createInfo.preferredFlags,
13565 createInfo.memoryTypeBits,
13566 createInfo.pool,
13567 allocation,
13568 userDataStr.GetString());
13569 Flush();
13570}
13571
13572void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13573 const VkMemoryRequirements& vkMemReq,
13574 bool requiresDedicatedAllocation,
13575 bool prefersDedicatedAllocation,
13576 const VmaAllocationCreateInfo& createInfo,
13577 VmaAllocation allocation)
13578{
13579 CallParams callParams;
13580 GetBasicParams(callParams);
13581
13582 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13583 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13584 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,
13585 vkMemReq.size,
13586 vkMemReq.alignment,
13587 vkMemReq.memoryTypeBits,
13588 requiresDedicatedAllocation ? 1 : 0,
13589 prefersDedicatedAllocation ? 1 : 0,
13590 createInfo.flags,
13591 createInfo.usage,
13592 createInfo.requiredFlags,
13593 createInfo.preferredFlags,
13594 createInfo.memoryTypeBits,
13595 createInfo.pool,
13596 allocation,
13597 userDataStr.GetString());
13598 Flush();
13599}
13600
13601void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13602 VmaAllocation allocation)
13603{
13604 CallParams callParams;
13605 GetBasicParams(callParams);
13606
13607 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13608 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13609 allocation);
13610 Flush();
13611}
13612
13613void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13614 uint64_t allocationCount,
13615 const VmaAllocation* pAllocations)
13616{
13617 CallParams callParams;
13618 GetBasicParams(callParams);
13619
13620 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13621 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13622 PrintPointerList(allocationCount, pAllocations);
13623 fprintf(m_File, "\n");
13624 Flush();
13625}
13626
13627void VmaRecorder::RecordResizeAllocation(
13628 uint32_t frameIndex,
13629 VmaAllocation allocation,
13630 VkDeviceSize newSize)
13631{
13632 CallParams callParams;
13633 GetBasicParams(callParams);
13634
13635 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13636 fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13637 allocation, newSize);
13638 Flush();
13639}
13640
13641void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13642 VmaAllocation allocation,
13643 const void* pUserData)
13644{
13645 CallParams callParams;
13646 GetBasicParams(callParams);
13647
13648 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13649 UserDataString userDataStr(
13650 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13651 pUserData);
13652 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13653 allocation,
13654 userDataStr.GetString());
13655 Flush();
13656}
13657
13658void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13659 VmaAllocation allocation)
13660{
13661 CallParams callParams;
13662 GetBasicParams(callParams);
13663
13664 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13665 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13666 allocation);
13667 Flush();
13668}
13669
13670void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13671 VmaAllocation allocation)
13672{
13673 CallParams callParams;
13674 GetBasicParams(callParams);
13675
13676 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13677 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13678 allocation);
13679 Flush();
13680}
13681
13682void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13683 VmaAllocation allocation)
13684{
13685 CallParams callParams;
13686 GetBasicParams(callParams);
13687
13688 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13689 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13690 allocation);
13691 Flush();
13692}
13693
13694void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13695 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13696{
13697 CallParams callParams;
13698 GetBasicParams(callParams);
13699
13700 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13701 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13702 allocation,
13703 offset,
13704 size);
13705 Flush();
13706}
13707
13708void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13709 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13710{
13711 CallParams callParams;
13712 GetBasicParams(callParams);
13713
13714 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13715 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13716 allocation,
13717 offset,
13718 size);
13719 Flush();
13720}
13721
13722void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13723 const VkBufferCreateInfo& bufCreateInfo,
13724 const VmaAllocationCreateInfo& allocCreateInfo,
13725 VmaAllocation allocation)
13726{
13727 CallParams callParams;
13728 GetBasicParams(callParams);
13729
13730 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13731 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13732 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,
13733 bufCreateInfo.flags,
13734 bufCreateInfo.size,
13735 bufCreateInfo.usage,
13736 bufCreateInfo.sharingMode,
13737 allocCreateInfo.flags,
13738 allocCreateInfo.usage,
13739 allocCreateInfo.requiredFlags,
13740 allocCreateInfo.preferredFlags,
13741 allocCreateInfo.memoryTypeBits,
13742 allocCreateInfo.pool,
13743 allocation,
13744 userDataStr.GetString());
13745 Flush();
13746}
13747
13748void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13749 const VkImageCreateInfo& imageCreateInfo,
13750 const VmaAllocationCreateInfo& allocCreateInfo,
13751 VmaAllocation allocation)
13752{
13753 CallParams callParams;
13754 GetBasicParams(callParams);
13755
13756 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13757 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13758 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,
13759 imageCreateInfo.flags,
13760 imageCreateInfo.imageType,
13761 imageCreateInfo.format,
13762 imageCreateInfo.extent.width,
13763 imageCreateInfo.extent.height,
13764 imageCreateInfo.extent.depth,
13765 imageCreateInfo.mipLevels,
13766 imageCreateInfo.arrayLayers,
13767 imageCreateInfo.samples,
13768 imageCreateInfo.tiling,
13769 imageCreateInfo.usage,
13770 imageCreateInfo.sharingMode,
13771 imageCreateInfo.initialLayout,
13772 allocCreateInfo.flags,
13773 allocCreateInfo.usage,
13774 allocCreateInfo.requiredFlags,
13775 allocCreateInfo.preferredFlags,
13776 allocCreateInfo.memoryTypeBits,
13777 allocCreateInfo.pool,
13778 allocation,
13779 userDataStr.GetString());
13780 Flush();
13781}
13782
13783void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13784 VmaAllocation allocation)
13785{
13786 CallParams callParams;
13787 GetBasicParams(callParams);
13788
13789 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13790 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13791 allocation);
13792 Flush();
13793}
13794
13795void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13796 VmaAllocation allocation)
13797{
13798 CallParams callParams;
13799 GetBasicParams(callParams);
13800
13801 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13802 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13803 allocation);
13804 Flush();
13805}
13806
13807void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13808 VmaAllocation allocation)
13809{
13810 CallParams callParams;
13811 GetBasicParams(callParams);
13812
13813 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13814 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13815 allocation);
13816 Flush();
13817}
13818
13819void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13820 VmaAllocation allocation)
13821{
13822 CallParams callParams;
13823 GetBasicParams(callParams);
13824
13825 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13826 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13827 allocation);
13828 Flush();
13829}
13830
13831void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13832 VmaPool pool)
13833{
13834 CallParams callParams;
13835 GetBasicParams(callParams);
13836
13837 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13838 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13839 pool);
13840 Flush();
13841}
13842
13843void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13844 const VmaDefragmentationInfo2& info,
13845 VmaDefragmentationContext ctx)
13846{
13847 CallParams callParams;
13848 GetBasicParams(callParams);
13849
13850 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13851 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13852 info.flags);
13853 PrintPointerList(info.allocationCount, info.pAllocations);
13854 fprintf(m_File, ",");
13855 PrintPointerList(info.poolCount, info.pPools);
13856 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13857 info.maxCpuBytesToMove,
13858 info.maxCpuAllocationsToMove,
13859 info.maxGpuBytesToMove,
13860 info.maxGpuAllocationsToMove,
13861 info.commandBuffer,
13862 ctx);
13863 Flush();
13864}
13865
13866void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13867 VmaDefragmentationContext ctx)
13868{
13869 CallParams callParams;
13870 GetBasicParams(callParams);
13871
13872 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13873 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13874 ctx);
13875 Flush();
13876}
13877
13878VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13879{
13880 if(pUserData != VMA_NULL)
13881 {
13882 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13883 {
13884 m_Str = (const char*)pUserData;
13885 }
13886 else
13887 {
13888 sprintf_s(m_PtrStr, "%p", pUserData);
13889 m_Str = m_PtrStr;
13890 }
13891 }
13892 else
13893 {
13894 m_Str = "";
13895 }
13896}
13897
13898void VmaRecorder::WriteConfiguration(
13899 const VkPhysicalDeviceProperties& devProps,
13900 const VkPhysicalDeviceMemoryProperties& memProps,
13901 bool dedicatedAllocationExtensionEnabled)
13902{
13903 fprintf(m_File, "Config,Begin\n");
13904
13905 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13906 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13907 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13908 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13909 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13910 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13911
13912 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13913 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13914 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13915
13916 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13917 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13918 {
13919 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13920 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13921 }
13922 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13923 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13924 {
13925 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13926 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13927 }
13928
13929 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13930
13931 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13932 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13933 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13934 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13935 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13936 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13937 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13938 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13939 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13940
13941 fprintf(m_File, "Config,End\n");
13942}
13943
13944void VmaRecorder::GetBasicParams(CallParams& outParams)
13945{
13946 outParams.threadId = GetCurrentThreadId();
13947
13948 LARGE_INTEGER counter;
13949 QueryPerformanceCounter(&counter);
13950 outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13951}
13952
13953void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13954{
13955 if(count)
13956 {
13957 fprintf(m_File, "%p", pItems[0]);
13958 for(uint64_t i = 1; i < count; ++i)
13959 {
13960 fprintf(m_File, " %p", pItems[i]);
13961 }
13962 }
13963}
13964
13965void VmaRecorder::Flush()
13966{
13967 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13968 {
13969 fflush(m_File);
13970 }
13971}
13972
13973#endif // #if VMA_RECORDING_ENABLED
13974
13975////////////////////////////////////////////////////////////////////////////////
13976// VmaAllocator_T
13977
13978VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13979 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13980 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13981 m_hDevice(pCreateInfo->device),
13982 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13983 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13984 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13985 m_PreferredLargeHeapBlockSize(0),
13986 m_PhysicalDevice(pCreateInfo->physicalDevice),
13987 m_CurrentFrameIndex(0),
13988 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13989 m_NextPoolId(0)
13990#if VMA_RECORDING_ENABLED
13991 ,m_pRecorder(VMA_NULL)
13992#endif
13993{
13994 if(VMA_DEBUG_DETECT_CORRUPTION)
13995 {
13996 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
13997 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
13998 }
13999
14000 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
14001
14002#if !(VMA_DEDICATED_ALLOCATION)
14003 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14004 {
14005 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14006 }
14007#endif
14008
14009 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14010 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14011 memset(&m_MemProps, 0, sizeof(m_MemProps));
14012
14013 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14014 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14015
14016 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14017 {
14018 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14019 }
14020
14021 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14022 {
14023 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14024 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14025 }
14026
14027 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14028
14029 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14030 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14031
14032 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14033 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14034 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14035 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14036
14037 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14038 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14039
14040 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14041 {
14042 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14043 {
14044 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14045 if(limit != VK_WHOLE_SIZE)
14046 {
14047 m_HeapSizeLimit[heapIndex] = limit;
14048 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14049 {
14050 m_MemProps.memoryHeaps[heapIndex].size = limit;
14051 }
14052 }
14053 }
14054 }
14055
14056 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14057 {
14058 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14059
14060 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14061 this,
14062 memTypeIndex,
14063 preferredBlockSize,
14064 0,
14065 SIZE_MAX,
14066 GetBufferImageGranularity(),
14067 pCreateInfo->frameInUseCount,
14068 false, // isCustomPool
14069 false, // explicitBlockSize
14070 false); // linearAlgorithm
14071 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14072 // becase minBlockCount is 0.
14073 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14074
14075 }
14076}
14077
14078VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14079{
14080 VkResult res = VK_SUCCESS;
14081
14082 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14083 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14084 {
14085#if VMA_RECORDING_ENABLED
14086 m_pRecorder = vma_new(this, VmaRecorder)();
14087 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14088 if(res != VK_SUCCESS)
14089 {
14090 return res;
14091 }
14092 m_pRecorder->WriteConfiguration(
14093 m_PhysicalDeviceProperties,
14094 m_MemProps,
14095 m_UseKhrDedicatedAllocation);
14096 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14097#else
14098 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14099 return VK_ERROR_FEATURE_NOT_PRESENT;
14100#endif
14101 }
14102
14103 return res;
14104}
14105
14106VmaAllocator_T::~VmaAllocator_T()
14107{
14108#if VMA_RECORDING_ENABLED
14109 if(m_pRecorder != VMA_NULL)
14110 {
14111 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14112 vma_delete(this, m_pRecorder);
14113 }
14114#endif
14115
14116 VMA_ASSERT(m_Pools.empty());
14117
14118 for(size_t i = GetMemoryTypeCount(); i--; )
14119 {
14120 vma_delete(this, m_pDedicatedAllocations[i]);
14121 vma_delete(this, m_pBlockVectors[i]);
14122 }
14123}
14124
14125void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14126{
14127#if VMA_STATIC_VULKAN_FUNCTIONS == 1
14128 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14129 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14130 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14131 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14132 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14133 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14134 m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14135 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14136 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14137 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14138 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14139 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14140 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14141 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14142 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14143 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14144 m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14145#if VMA_DEDICATED_ALLOCATION
14146 if(m_UseKhrDedicatedAllocation)
14147 {
14148 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14149 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14150 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14151 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14152 }
14153#endif // #if VMA_DEDICATED_ALLOCATION
14154#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14155
14156#define VMA_COPY_IF_NOT_NULL(funcName) \
14157 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14158
14159 if(pVulkanFunctions != VMA_NULL)
14160 {
14161 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14162 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14163 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14164 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14165 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14166 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14167 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14168 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14169 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14170 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14171 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14172 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14173 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14174 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14175 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14176 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14177 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14178#if VMA_DEDICATED_ALLOCATION
14179 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14180 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14181#endif
14182 }
14183
14184#undef VMA_COPY_IF_NOT_NULL
14185
14186 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14187 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14188 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14189 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14190 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14191 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14192 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14193 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14194 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14195 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14196 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14197 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14198 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14199 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14200 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14201 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14202 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14203 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14204 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14205#if VMA_DEDICATED_ALLOCATION
14206 if(m_UseKhrDedicatedAllocation)
14207 {
14208 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14209 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14210 }
14211#endif
14212}
14213
14214VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14215{
14216 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14217 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14218 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14219 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14220}
14221
14222VkResult VmaAllocator_T::AllocateMemoryOfType(
14223 VkDeviceSize size,
14224 VkDeviceSize alignment,
14225 bool dedicatedAllocation,
14226 VkBuffer dedicatedBuffer,
14227 VkImage dedicatedImage,
14228 const VmaAllocationCreateInfo& createInfo,
14229 uint32_t memTypeIndex,
14230 VmaSuballocationType suballocType,
14231 size_t allocationCount,
14232 VmaAllocation* pAllocations)
14233{
14234 VMA_ASSERT(pAllocations != VMA_NULL);
14235 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14236
14237 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14238
14239 // If memory type is not HOST_VISIBLE, disable MAPPED.
14240 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14241 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14242 {
14243 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14244 }
14245
14246 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14247 VMA_ASSERT(blockVector);
14248
14249 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14250 bool preferDedicatedMemory =
14251 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14252 dedicatedAllocation ||
14253 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14254 size > preferredBlockSize / 2;
14255
14256 if(preferDedicatedMemory &&
14257 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14258 finalCreateInfo.pool == VK_NULL_HANDLE)
14259 {
14260 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14261 }
14262
14263 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14264 {
14265 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14266 {
14267 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14268 }
14269 else
14270 {
14271 return AllocateDedicatedMemory(
14272 size,
14273 suballocType,
14274 memTypeIndex,
14275 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14276 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14277 finalCreateInfo.pUserData,
14278 dedicatedBuffer,
14279 dedicatedImage,
14280 allocationCount,
14281 pAllocations);
14282 }
14283 }
14284 else
14285 {
14286 VkResult res = blockVector->Allocate(
14287 VK_NULL_HANDLE, // hCurrentPool
14288 m_CurrentFrameIndex.load(),
14289 size,
14290 alignment,
14291 finalCreateInfo,
14292 suballocType,
14293 allocationCount,
14294 pAllocations);
14295 if(res == VK_SUCCESS)
14296 {
14297 return res;
14298 }
14299
14300 // 5. Try dedicated memory.
14301 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14302 {
14303 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14304 }
14305 else
14306 {
14307 res = AllocateDedicatedMemory(
14308 size,
14309 suballocType,
14310 memTypeIndex,
14311 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14312 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14313 finalCreateInfo.pUserData,
14314 dedicatedBuffer,
14315 dedicatedImage,
14316 allocationCount,
14317 pAllocations);
14318 if(res == VK_SUCCESS)
14319 {
14320 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14321 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14322 return VK_SUCCESS;
14323 }
14324 else
14325 {
14326 // Everything failed: Return error code.
14327 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14328 return res;
14329 }
14330 }
14331 }
14332}
14333
14334VkResult VmaAllocator_T::AllocateDedicatedMemory(
14335 VkDeviceSize size,
14336 VmaSuballocationType suballocType,
14337 uint32_t memTypeIndex,
14338 bool map,
14339 bool isUserDataString,
14340 void* pUserData,
14341 VkBuffer dedicatedBuffer,
14342 VkImage dedicatedImage,
14343 size_t allocationCount,
14344 VmaAllocation* pAllocations)
14345{
14346 VMA_ASSERT(allocationCount > 0 && pAllocations);
14347
14348 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14349 allocInfo.memoryTypeIndex = memTypeIndex;
14350 allocInfo.allocationSize = size;
14351
14352#if VMA_DEDICATED_ALLOCATION
14353 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14354 if(m_UseKhrDedicatedAllocation)
14355 {
14356 if(dedicatedBuffer != VK_NULL_HANDLE)
14357 {
14358 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14359 dedicatedAllocInfo.buffer = dedicatedBuffer;
14360 allocInfo.pNext = &dedicatedAllocInfo;
14361 }
14362 else if(dedicatedImage != VK_NULL_HANDLE)
14363 {
14364 dedicatedAllocInfo.image = dedicatedImage;
14365 allocInfo.pNext = &dedicatedAllocInfo;
14366 }
14367 }
14368#endif // #if VMA_DEDICATED_ALLOCATION
14369
14370 size_t allocIndex;
Tony-LunarG390319b2019-03-18 15:54:16 -060014371 VkResult res = VK_SUCCESS;
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060014372 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14373 {
14374 res = AllocateDedicatedMemoryPage(
14375 size,
14376 suballocType,
14377 memTypeIndex,
14378 allocInfo,
14379 map,
14380 isUserDataString,
14381 pUserData,
14382 pAllocations + allocIndex);
14383 if(res != VK_SUCCESS)
14384 {
14385 break;
14386 }
14387 }
14388
14389 if(res == VK_SUCCESS)
14390 {
14391 // Register them in m_pDedicatedAllocations.
14392 {
14393 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14394 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14395 VMA_ASSERT(pDedicatedAllocations);
14396 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14397 {
14398 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14399 }
14400 }
14401
14402 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14403 }
14404 else
14405 {
14406 // Free all already created allocations.
14407 while(allocIndex--)
14408 {
14409 VmaAllocation currAlloc = pAllocations[allocIndex];
14410 VkDeviceMemory hMemory = currAlloc->GetMemory();
14411
14412 /*
14413 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14414 before vkFreeMemory.
14415
14416 if(currAlloc->GetMappedData() != VMA_NULL)
14417 {
14418 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14419 }
14420 */
14421
14422 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14423
14424 currAlloc->SetUserData(this, VMA_NULL);
14425 vma_delete(this, currAlloc);
14426 }
14427
14428 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14429 }
14430
14431 return res;
14432}
14433
14434VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14435 VkDeviceSize size,
14436 VmaSuballocationType suballocType,
14437 uint32_t memTypeIndex,
14438 const VkMemoryAllocateInfo& allocInfo,
14439 bool map,
14440 bool isUserDataString,
14441 void* pUserData,
14442 VmaAllocation* pAllocation)
14443{
14444 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14445 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14446 if(res < 0)
14447 {
14448 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14449 return res;
14450 }
14451
14452 void* pMappedData = VMA_NULL;
14453 if(map)
14454 {
14455 res = (*m_VulkanFunctions.vkMapMemory)(
14456 m_hDevice,
14457 hMemory,
14458 0,
14459 VK_WHOLE_SIZE,
14460 0,
14461 &pMappedData);
14462 if(res < 0)
14463 {
14464 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14465 FreeVulkanMemory(memTypeIndex, size, hMemory);
14466 return res;
14467 }
14468 }
14469
14470 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14471 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14472 (*pAllocation)->SetUserData(this, pUserData);
14473 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14474 {
14475 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14476 }
14477
14478 return VK_SUCCESS;
14479}
14480
14481void VmaAllocator_T::GetBufferMemoryRequirements(
14482 VkBuffer hBuffer,
14483 VkMemoryRequirements& memReq,
14484 bool& requiresDedicatedAllocation,
14485 bool& prefersDedicatedAllocation) const
14486{
14487#if VMA_DEDICATED_ALLOCATION
14488 if(m_UseKhrDedicatedAllocation)
14489 {
14490 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14491 memReqInfo.buffer = hBuffer;
14492
14493 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14494
14495 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14496 memReq2.pNext = &memDedicatedReq;
14497
14498 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14499
14500 memReq = memReq2.memoryRequirements;
14501 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14502 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14503 }
14504 else
14505#endif // #if VMA_DEDICATED_ALLOCATION
14506 {
14507 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14508 requiresDedicatedAllocation = false;
14509 prefersDedicatedAllocation = false;
14510 }
14511}
14512
14513void VmaAllocator_T::GetImageMemoryRequirements(
14514 VkImage hImage,
14515 VkMemoryRequirements& memReq,
14516 bool& requiresDedicatedAllocation,
14517 bool& prefersDedicatedAllocation) const
14518{
14519#if VMA_DEDICATED_ALLOCATION
14520 if(m_UseKhrDedicatedAllocation)
14521 {
14522 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14523 memReqInfo.image = hImage;
14524
14525 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14526
14527 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14528 memReq2.pNext = &memDedicatedReq;
14529
14530 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14531
14532 memReq = memReq2.memoryRequirements;
14533 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14534 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14535 }
14536 else
14537#endif // #if VMA_DEDICATED_ALLOCATION
14538 {
14539 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14540 requiresDedicatedAllocation = false;
14541 prefersDedicatedAllocation = false;
14542 }
14543}
14544
14545VkResult VmaAllocator_T::AllocateMemory(
14546 const VkMemoryRequirements& vkMemReq,
14547 bool requiresDedicatedAllocation,
14548 bool prefersDedicatedAllocation,
14549 VkBuffer dedicatedBuffer,
14550 VkImage dedicatedImage,
14551 const VmaAllocationCreateInfo& createInfo,
14552 VmaSuballocationType suballocType,
14553 size_t allocationCount,
14554 VmaAllocation* pAllocations)
14555{
14556 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14557
14558 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14559
14560 if(vkMemReq.size == 0)
14561 {
14562 return VK_ERROR_VALIDATION_FAILED_EXT;
14563 }
14564 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14565 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14566 {
14567 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14568 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14569 }
14570 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14571 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14572 {
14573 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14574 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14575 }
14576 if(requiresDedicatedAllocation)
14577 {
14578 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14579 {
14580 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14581 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14582 }
14583 if(createInfo.pool != VK_NULL_HANDLE)
14584 {
14585 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14586 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14587 }
14588 }
14589 if((createInfo.pool != VK_NULL_HANDLE) &&
14590 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14591 {
14592 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14593 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14594 }
14595
14596 if(createInfo.pool != VK_NULL_HANDLE)
14597 {
14598 const VkDeviceSize alignmentForPool = VMA_MAX(
14599 vkMemReq.alignment,
14600 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14601 return createInfo.pool->m_BlockVector.Allocate(
14602 createInfo.pool,
14603 m_CurrentFrameIndex.load(),
14604 vkMemReq.size,
14605 alignmentForPool,
14606 createInfo,
14607 suballocType,
14608 allocationCount,
14609 pAllocations);
14610 }
14611 else
14612 {
14613 // Bit mask of memory Vulkan types acceptable for this allocation.
14614 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14615 uint32_t memTypeIndex = UINT32_MAX;
14616 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14617 if(res == VK_SUCCESS)
14618 {
14619 VkDeviceSize alignmentForMemType = VMA_MAX(
14620 vkMemReq.alignment,
14621 GetMemoryTypeMinAlignment(memTypeIndex));
14622
14623 res = AllocateMemoryOfType(
14624 vkMemReq.size,
14625 alignmentForMemType,
14626 requiresDedicatedAllocation || prefersDedicatedAllocation,
14627 dedicatedBuffer,
14628 dedicatedImage,
14629 createInfo,
14630 memTypeIndex,
14631 suballocType,
14632 allocationCount,
14633 pAllocations);
14634 // Succeeded on first try.
14635 if(res == VK_SUCCESS)
14636 {
14637 return res;
14638 }
14639 // Allocation from this memory type failed. Try other compatible memory types.
14640 else
14641 {
14642 for(;;)
14643 {
14644 // Remove old memTypeIndex from list of possibilities.
14645 memoryTypeBits &= ~(1u << memTypeIndex);
14646 // Find alternative memTypeIndex.
14647 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14648 if(res == VK_SUCCESS)
14649 {
14650 alignmentForMemType = VMA_MAX(
14651 vkMemReq.alignment,
14652 GetMemoryTypeMinAlignment(memTypeIndex));
14653
14654 res = AllocateMemoryOfType(
14655 vkMemReq.size,
14656 alignmentForMemType,
14657 requiresDedicatedAllocation || prefersDedicatedAllocation,
14658 dedicatedBuffer,
14659 dedicatedImage,
14660 createInfo,
14661 memTypeIndex,
14662 suballocType,
14663 allocationCount,
14664 pAllocations);
14665 // Allocation from this alternative memory type succeeded.
14666 if(res == VK_SUCCESS)
14667 {
14668 return res;
14669 }
14670 // else: Allocation from this memory type failed. Try next one - next loop iteration.
14671 }
14672 // No other matching memory type index could be found.
14673 else
14674 {
14675 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14676 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14677 }
14678 }
14679 }
14680 }
14681 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14682 else
14683 return res;
14684 }
14685}
14686
14687void VmaAllocator_T::FreeMemory(
14688 size_t allocationCount,
14689 const VmaAllocation* pAllocations)
14690{
14691 VMA_ASSERT(pAllocations);
14692
14693 for(size_t allocIndex = allocationCount; allocIndex--; )
14694 {
14695 VmaAllocation allocation = pAllocations[allocIndex];
14696
14697 if(allocation != VK_NULL_HANDLE)
14698 {
14699 if(TouchAllocation(allocation))
14700 {
14701 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14702 {
14703 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14704 }
14705
14706 switch(allocation->GetType())
14707 {
14708 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14709 {
14710 VmaBlockVector* pBlockVector = VMA_NULL;
14711 VmaPool hPool = allocation->GetPool();
14712 if(hPool != VK_NULL_HANDLE)
14713 {
14714 pBlockVector = &hPool->m_BlockVector;
14715 }
14716 else
14717 {
14718 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14719 pBlockVector = m_pBlockVectors[memTypeIndex];
14720 }
14721 pBlockVector->Free(allocation);
14722 }
14723 break;
14724 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14725 FreeDedicatedMemory(allocation);
14726 break;
14727 default:
14728 VMA_ASSERT(0);
14729 }
14730 }
14731
14732 allocation->SetUserData(this, VMA_NULL);
14733 vma_delete(this, allocation);
14734 }
14735 }
14736}
14737
14738VkResult VmaAllocator_T::ResizeAllocation(
14739 const VmaAllocation alloc,
14740 VkDeviceSize newSize)
14741{
14742 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14743 {
14744 return VK_ERROR_VALIDATION_FAILED_EXT;
14745 }
14746 if(newSize == alloc->GetSize())
14747 {
14748 return VK_SUCCESS;
14749 }
14750
14751 switch(alloc->GetType())
14752 {
14753 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14754 return VK_ERROR_FEATURE_NOT_PRESENT;
14755 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14756 if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14757 {
14758 alloc->ChangeSize(newSize);
14759 VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14760 return VK_SUCCESS;
14761 }
14762 else
14763 {
14764 return VK_ERROR_OUT_OF_POOL_MEMORY;
14765 }
14766 default:
14767 VMA_ASSERT(0);
14768 return VK_ERROR_VALIDATION_FAILED_EXT;
14769 }
14770}
14771
14772void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14773{
14774 // Initialize.
14775 InitStatInfo(pStats->total);
14776 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14777 InitStatInfo(pStats->memoryType[i]);
14778 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14779 InitStatInfo(pStats->memoryHeap[i]);
14780
14781 // Process default pools.
14782 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14783 {
14784 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14785 VMA_ASSERT(pBlockVector);
14786 pBlockVector->AddStats(pStats);
14787 }
14788
14789 // Process custom pools.
14790 {
14791 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14792 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14793 {
14794 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14795 }
14796 }
14797
14798 // Process dedicated allocations.
14799 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14800 {
14801 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14802 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14803 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14804 VMA_ASSERT(pDedicatedAllocVector);
14805 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14806 {
14807 VmaStatInfo allocationStatInfo;
14808 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14809 VmaAddStatInfo(pStats->total, allocationStatInfo);
14810 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14811 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14812 }
14813 }
14814
14815 // Postprocess.
14816 VmaPostprocessCalcStatInfo(pStats->total);
14817 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14818 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14819 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14820 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14821}
14822
14823static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14824
14825VkResult VmaAllocator_T::DefragmentationBegin(
14826 const VmaDefragmentationInfo2& info,
14827 VmaDefragmentationStats* pStats,
14828 VmaDefragmentationContext* pContext)
14829{
14830 if(info.pAllocationsChanged != VMA_NULL)
14831 {
14832 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14833 }
14834
14835 *pContext = vma_new(this, VmaDefragmentationContext_T)(
14836 this, m_CurrentFrameIndex.load(), info.flags, pStats);
14837
14838 (*pContext)->AddPools(info.poolCount, info.pPools);
14839 (*pContext)->AddAllocations(
14840 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14841
14842 VkResult res = (*pContext)->Defragment(
14843 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14844 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14845 info.commandBuffer, pStats);
14846
14847 if(res != VK_NOT_READY)
14848 {
14849 vma_delete(this, *pContext);
14850 *pContext = VMA_NULL;
14851 }
14852
14853 return res;
14854}
14855
14856VkResult VmaAllocator_T::DefragmentationEnd(
14857 VmaDefragmentationContext context)
14858{
14859 vma_delete(this, context);
14860 return VK_SUCCESS;
14861}
14862
14863void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14864{
14865 if(hAllocation->CanBecomeLost())
14866 {
14867 /*
14868 Warning: This is a carefully designed algorithm.
14869 Do not modify unless you really know what you're doing :)
14870 */
14871 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14872 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14873 for(;;)
14874 {
14875 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14876 {
14877 pAllocationInfo->memoryType = UINT32_MAX;
14878 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14879 pAllocationInfo->offset = 0;
14880 pAllocationInfo->size = hAllocation->GetSize();
14881 pAllocationInfo->pMappedData = VMA_NULL;
14882 pAllocationInfo->pUserData = hAllocation->GetUserData();
14883 return;
14884 }
14885 else if(localLastUseFrameIndex == localCurrFrameIndex)
14886 {
14887 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14888 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14889 pAllocationInfo->offset = hAllocation->GetOffset();
14890 pAllocationInfo->size = hAllocation->GetSize();
14891 pAllocationInfo->pMappedData = VMA_NULL;
14892 pAllocationInfo->pUserData = hAllocation->GetUserData();
14893 return;
14894 }
14895 else // Last use time earlier than current time.
14896 {
14897 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14898 {
14899 localLastUseFrameIndex = localCurrFrameIndex;
14900 }
14901 }
14902 }
14903 }
14904 else
14905 {
14906#if VMA_STATS_STRING_ENABLED
14907 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14908 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14909 for(;;)
14910 {
14911 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14912 if(localLastUseFrameIndex == localCurrFrameIndex)
14913 {
14914 break;
14915 }
14916 else // Last use time earlier than current time.
14917 {
14918 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14919 {
14920 localLastUseFrameIndex = localCurrFrameIndex;
14921 }
14922 }
14923 }
14924#endif
14925
14926 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14927 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14928 pAllocationInfo->offset = hAllocation->GetOffset();
14929 pAllocationInfo->size = hAllocation->GetSize();
14930 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14931 pAllocationInfo->pUserData = hAllocation->GetUserData();
14932 }
14933}
14934
14935bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14936{
14937 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14938 if(hAllocation->CanBecomeLost())
14939 {
14940 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14941 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14942 for(;;)
14943 {
14944 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14945 {
14946 return false;
14947 }
14948 else if(localLastUseFrameIndex == localCurrFrameIndex)
14949 {
14950 return true;
14951 }
14952 else // Last use time earlier than current time.
14953 {
14954 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14955 {
14956 localLastUseFrameIndex = localCurrFrameIndex;
14957 }
14958 }
14959 }
14960 }
14961 else
14962 {
14963#if VMA_STATS_STRING_ENABLED
14964 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14965 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14966 for(;;)
14967 {
14968 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14969 if(localLastUseFrameIndex == localCurrFrameIndex)
14970 {
14971 break;
14972 }
14973 else // Last use time earlier than current time.
14974 {
14975 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14976 {
14977 localLastUseFrameIndex = localCurrFrameIndex;
14978 }
14979 }
14980 }
14981#endif
14982
14983 return true;
14984 }
14985}
14986
14987VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
14988{
14989 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
14990
14991 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
14992
14993 if(newCreateInfo.maxBlockCount == 0)
14994 {
14995 newCreateInfo.maxBlockCount = SIZE_MAX;
14996 }
14997 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
14998 {
14999 return VK_ERROR_INITIALIZATION_FAILED;
15000 }
15001
15002 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15003
15004 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15005
15006 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15007 if(res != VK_SUCCESS)
15008 {
15009 vma_delete(this, *pPool);
15010 *pPool = VMA_NULL;
15011 return res;
15012 }
15013
15014 // Add to m_Pools.
15015 {
15016 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15017 (*pPool)->SetId(m_NextPoolId++);
15018 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15019 }
15020
15021 return VK_SUCCESS;
15022}
15023
15024void VmaAllocator_T::DestroyPool(VmaPool pool)
15025{
15026 // Remove from m_Pools.
15027 {
15028 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15029 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15030 VMA_ASSERT(success && "Pool not found in Allocator.");
15031 }
15032
15033 vma_delete(this, pool);
15034}
15035
15036void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15037{
15038 pool->m_BlockVector.GetPoolStats(pPoolStats);
15039}
15040
15041void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15042{
15043 m_CurrentFrameIndex.store(frameIndex);
15044}
15045
15046void VmaAllocator_T::MakePoolAllocationsLost(
15047 VmaPool hPool,
15048 size_t* pLostAllocationCount)
15049{
15050 hPool->m_BlockVector.MakePoolAllocationsLost(
15051 m_CurrentFrameIndex.load(),
15052 pLostAllocationCount);
15053}
15054
15055VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15056{
15057 return hPool->m_BlockVector.CheckCorruption();
15058}
15059
15060VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15061{
15062 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15063
15064 // Process default pools.
15065 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15066 {
15067 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15068 {
15069 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15070 VMA_ASSERT(pBlockVector);
15071 VkResult localRes = pBlockVector->CheckCorruption();
15072 switch(localRes)
15073 {
15074 case VK_ERROR_FEATURE_NOT_PRESENT:
15075 break;
15076 case VK_SUCCESS:
15077 finalRes = VK_SUCCESS;
15078 break;
15079 default:
15080 return localRes;
15081 }
15082 }
15083 }
15084
15085 // Process custom pools.
15086 {
15087 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15088 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15089 {
15090 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15091 {
15092 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15093 switch(localRes)
15094 {
15095 case VK_ERROR_FEATURE_NOT_PRESENT:
15096 break;
15097 case VK_SUCCESS:
15098 finalRes = VK_SUCCESS;
15099 break;
15100 default:
15101 return localRes;
15102 }
15103 }
15104 }
15105 }
15106
15107 return finalRes;
15108}
15109
15110void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15111{
15112 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15113 (*pAllocation)->InitLost();
15114}
15115
15116VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15117{
15118 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15119
15120 VkResult res;
15121 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15122 {
15123 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15124 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15125 {
15126 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15127 if(res == VK_SUCCESS)
15128 {
15129 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15130 }
15131 }
15132 else
15133 {
15134 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15135 }
15136 }
15137 else
15138 {
15139 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15140 }
15141
15142 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15143 {
15144 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15145 }
15146
15147 return res;
15148}
15149
15150void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15151{
15152 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15153 {
15154 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15155 }
15156
15157 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15158
15159 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15160 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15161 {
15162 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15163 m_HeapSizeLimit[heapIndex] += size;
15164 }
15165}
15166
15167VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15168{
15169 if(hAllocation->CanBecomeLost())
15170 {
15171 return VK_ERROR_MEMORY_MAP_FAILED;
15172 }
15173
15174 switch(hAllocation->GetType())
15175 {
15176 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15177 {
15178 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15179 char *pBytes = VMA_NULL;
15180 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15181 if(res == VK_SUCCESS)
15182 {
15183 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15184 hAllocation->BlockAllocMap();
15185 }
15186 return res;
15187 }
15188 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15189 return hAllocation->DedicatedAllocMap(this, ppData);
15190 default:
15191 VMA_ASSERT(0);
15192 return VK_ERROR_MEMORY_MAP_FAILED;
15193 }
15194}
15195
15196void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15197{
15198 switch(hAllocation->GetType())
15199 {
15200 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15201 {
15202 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15203 hAllocation->BlockAllocUnmap();
15204 pBlock->Unmap(this, 1);
15205 }
15206 break;
15207 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15208 hAllocation->DedicatedAllocUnmap(this);
15209 break;
15210 default:
15211 VMA_ASSERT(0);
15212 }
15213}
15214
15215VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15216{
15217 VkResult res = VK_SUCCESS;
15218 switch(hAllocation->GetType())
15219 {
15220 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15221 res = GetVulkanFunctions().vkBindBufferMemory(
15222 m_hDevice,
15223 hBuffer,
15224 hAllocation->GetMemory(),
15225 0); //memoryOffset
15226 break;
15227 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15228 {
15229 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15230 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15231 res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15232 break;
15233 }
15234 default:
15235 VMA_ASSERT(0);
15236 }
15237 return res;
15238}
15239
15240VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15241{
15242 VkResult res = VK_SUCCESS;
15243 switch(hAllocation->GetType())
15244 {
15245 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15246 res = GetVulkanFunctions().vkBindImageMemory(
15247 m_hDevice,
15248 hImage,
15249 hAllocation->GetMemory(),
15250 0); //memoryOffset
15251 break;
15252 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15253 {
15254 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15255 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15256 res = pBlock->BindImageMemory(this, hAllocation, hImage);
15257 break;
15258 }
15259 default:
15260 VMA_ASSERT(0);
15261 }
15262 return res;
15263}
15264
15265void VmaAllocator_T::FlushOrInvalidateAllocation(
15266 VmaAllocation hAllocation,
15267 VkDeviceSize offset, VkDeviceSize size,
15268 VMA_CACHE_OPERATION op)
15269{
15270 const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15271 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15272 {
15273 const VkDeviceSize allocationSize = hAllocation->GetSize();
15274 VMA_ASSERT(offset <= allocationSize);
15275
15276 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15277
15278 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15279 memRange.memory = hAllocation->GetMemory();
15280
15281 switch(hAllocation->GetType())
15282 {
15283 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15284 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15285 if(size == VK_WHOLE_SIZE)
15286 {
15287 memRange.size = allocationSize - memRange.offset;
15288 }
15289 else
15290 {
15291 VMA_ASSERT(offset + size <= allocationSize);
15292 memRange.size = VMA_MIN(
15293 VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15294 allocationSize - memRange.offset);
15295 }
15296 break;
15297
15298 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15299 {
15300 // 1. Still within this allocation.
15301 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15302 if(size == VK_WHOLE_SIZE)
15303 {
15304 size = allocationSize - offset;
15305 }
15306 else
15307 {
15308 VMA_ASSERT(offset + size <= allocationSize);
15309 }
15310 memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15311
15312 // 2. Adjust to whole block.
15313 const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15314 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15315 const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15316 memRange.offset += allocationOffset;
15317 memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15318
15319 break;
15320 }
15321
15322 default:
15323 VMA_ASSERT(0);
15324 }
15325
15326 switch(op)
15327 {
15328 case VMA_CACHE_FLUSH:
15329 (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15330 break;
15331 case VMA_CACHE_INVALIDATE:
15332 (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15333 break;
15334 default:
15335 VMA_ASSERT(0);
15336 }
15337 }
15338 // else: Just ignore this call.
15339}
15340
15341void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15342{
15343 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15344
15345 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15346 {
15347 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15348 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15349 VMA_ASSERT(pDedicatedAllocations);
15350 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15351 VMA_ASSERT(success);
15352 }
15353
15354 VkDeviceMemory hMemory = allocation->GetMemory();
15355
15356 /*
15357 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15358 before vkFreeMemory.
15359
15360 if(allocation->GetMappedData() != VMA_NULL)
15361 {
15362 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15363 }
15364 */
15365
15366 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15367
15368 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15369}
15370
15371void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15372{
15373 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15374 !hAllocation->CanBecomeLost() &&
15375 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15376 {
15377 void* pData = VMA_NULL;
15378 VkResult res = Map(hAllocation, &pData);
15379 if(res == VK_SUCCESS)
15380 {
15381 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15382 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15383 Unmap(hAllocation);
15384 }
15385 else
15386 {
15387 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15388 }
15389 }
15390}
15391
15392#if VMA_STATS_STRING_ENABLED
15393
15394void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15395{
15396 bool dedicatedAllocationsStarted = false;
15397 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15398 {
15399 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15400 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15401 VMA_ASSERT(pDedicatedAllocVector);
15402 if(pDedicatedAllocVector->empty() == false)
15403 {
15404 if(dedicatedAllocationsStarted == false)
15405 {
15406 dedicatedAllocationsStarted = true;
15407 json.WriteString("DedicatedAllocations");
15408 json.BeginObject();
15409 }
15410
15411 json.BeginString("Type ");
15412 json.ContinueString(memTypeIndex);
15413 json.EndString();
15414
15415 json.BeginArray();
15416
15417 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15418 {
15419 json.BeginObject(true);
15420 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15421 hAlloc->PrintParameters(json);
15422 json.EndObject();
15423 }
15424
15425 json.EndArray();
15426 }
15427 }
15428 if(dedicatedAllocationsStarted)
15429 {
15430 json.EndObject();
15431 }
15432
15433 {
15434 bool allocationsStarted = false;
15435 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15436 {
15437 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15438 {
15439 if(allocationsStarted == false)
15440 {
15441 allocationsStarted = true;
15442 json.WriteString("DefaultPools");
15443 json.BeginObject();
15444 }
15445
15446 json.BeginString("Type ");
15447 json.ContinueString(memTypeIndex);
15448 json.EndString();
15449
15450 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15451 }
15452 }
15453 if(allocationsStarted)
15454 {
15455 json.EndObject();
15456 }
15457 }
15458
15459 // Custom pools
15460 {
15461 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15462 const size_t poolCount = m_Pools.size();
15463 if(poolCount > 0)
15464 {
15465 json.WriteString("Pools");
15466 json.BeginObject();
15467 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15468 {
15469 json.BeginString();
15470 json.ContinueString(m_Pools[poolIndex]->GetId());
15471 json.EndString();
15472
15473 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15474 }
15475 json.EndObject();
15476 }
15477 }
15478}
15479
15480#endif // #if VMA_STATS_STRING_ENABLED
15481
15482////////////////////////////////////////////////////////////////////////////////
15483// Public interface
15484
15485VkResult vmaCreateAllocator(
15486 const VmaAllocatorCreateInfo* pCreateInfo,
15487 VmaAllocator* pAllocator)
15488{
15489 VMA_ASSERT(pCreateInfo && pAllocator);
15490 VMA_DEBUG_LOG("vmaCreateAllocator");
15491 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15492 return (*pAllocator)->Init(pCreateInfo);
15493}
15494
15495void vmaDestroyAllocator(
15496 VmaAllocator allocator)
15497{
15498 if(allocator != VK_NULL_HANDLE)
15499 {
15500 VMA_DEBUG_LOG("vmaDestroyAllocator");
15501 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15502 vma_delete(&allocationCallbacks, allocator);
15503 }
15504}
15505
15506void vmaGetPhysicalDeviceProperties(
15507 VmaAllocator allocator,
15508 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15509{
15510 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15511 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15512}
15513
15514void vmaGetMemoryProperties(
15515 VmaAllocator allocator,
15516 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15517{
15518 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15519 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15520}
15521
15522void vmaGetMemoryTypeProperties(
15523 VmaAllocator allocator,
15524 uint32_t memoryTypeIndex,
15525 VkMemoryPropertyFlags* pFlags)
15526{
15527 VMA_ASSERT(allocator && pFlags);
15528 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15529 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15530}
15531
15532void vmaSetCurrentFrameIndex(
15533 VmaAllocator allocator,
15534 uint32_t frameIndex)
15535{
15536 VMA_ASSERT(allocator);
15537 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15538
15539 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15540
15541 allocator->SetCurrentFrameIndex(frameIndex);
15542}
15543
15544void vmaCalculateStats(
15545 VmaAllocator allocator,
15546 VmaStats* pStats)
15547{
15548 VMA_ASSERT(allocator && pStats);
15549 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15550 allocator->CalculateStats(pStats);
15551}
15552
15553#if VMA_STATS_STRING_ENABLED
15554
15555void vmaBuildStatsString(
15556 VmaAllocator allocator,
15557 char** ppStatsString,
15558 VkBool32 detailedMap)
15559{
15560 VMA_ASSERT(allocator && ppStatsString);
15561 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15562
15563 VmaStringBuilder sb(allocator);
15564 {
15565 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15566 json.BeginObject();
15567
15568 VmaStats stats;
15569 allocator->CalculateStats(&stats);
15570
15571 json.WriteString("Total");
15572 VmaPrintStatInfo(json, stats.total);
15573
15574 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15575 {
15576 json.BeginString("Heap ");
15577 json.ContinueString(heapIndex);
15578 json.EndString();
15579 json.BeginObject();
15580
15581 json.WriteString("Size");
15582 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15583
15584 json.WriteString("Flags");
15585 json.BeginArray(true);
15586 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15587 {
15588 json.WriteString("DEVICE_LOCAL");
15589 }
15590 json.EndArray();
15591
15592 if(stats.memoryHeap[heapIndex].blockCount > 0)
15593 {
15594 json.WriteString("Stats");
15595 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15596 }
15597
15598 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15599 {
15600 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15601 {
15602 json.BeginString("Type ");
15603 json.ContinueString(typeIndex);
15604 json.EndString();
15605
15606 json.BeginObject();
15607
15608 json.WriteString("Flags");
15609 json.BeginArray(true);
15610 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15611 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15612 {
15613 json.WriteString("DEVICE_LOCAL");
15614 }
15615 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15616 {
15617 json.WriteString("HOST_VISIBLE");
15618 }
15619 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15620 {
15621 json.WriteString("HOST_COHERENT");
15622 }
15623 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15624 {
15625 json.WriteString("HOST_CACHED");
15626 }
15627 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15628 {
15629 json.WriteString("LAZILY_ALLOCATED");
15630 }
15631 json.EndArray();
15632
15633 if(stats.memoryType[typeIndex].blockCount > 0)
15634 {
15635 json.WriteString("Stats");
15636 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15637 }
15638
15639 json.EndObject();
15640 }
15641 }
15642
15643 json.EndObject();
15644 }
15645 if(detailedMap == VK_TRUE)
15646 {
15647 allocator->PrintDetailedMap(json);
15648 }
15649
15650 json.EndObject();
15651 }
15652
15653 const size_t len = sb.GetLength();
15654 char* const pChars = vma_new_array(allocator, char, len + 1);
15655 if(len > 0)
15656 {
15657 memcpy(pChars, sb.GetData(), len);
15658 }
15659 pChars[len] = '\0';
15660 *ppStatsString = pChars;
15661}
15662
15663void vmaFreeStatsString(
15664 VmaAllocator allocator,
15665 char* pStatsString)
15666{
15667 if(pStatsString != VMA_NULL)
15668 {
15669 VMA_ASSERT(allocator);
15670 size_t len = strlen(pStatsString);
15671 vma_delete_array(allocator, pStatsString, len + 1);
15672 }
15673}
15674
15675#endif // #if VMA_STATS_STRING_ENABLED
15676
15677/*
15678This function is not protected by any mutex because it just reads immutable data.
15679*/
15680VkResult vmaFindMemoryTypeIndex(
15681 VmaAllocator allocator,
15682 uint32_t memoryTypeBits,
15683 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15684 uint32_t* pMemoryTypeIndex)
15685{
15686 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15687 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15688 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15689
15690 if(pAllocationCreateInfo->memoryTypeBits != 0)
15691 {
15692 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15693 }
15694
15695 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15696 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15697
15698 const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15699 if(mapped)
15700 {
15701 preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15702 }
15703
15704 // Convert usage to requiredFlags and preferredFlags.
15705 switch(pAllocationCreateInfo->usage)
15706 {
15707 case VMA_MEMORY_USAGE_UNKNOWN:
15708 break;
15709 case VMA_MEMORY_USAGE_GPU_ONLY:
15710 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15711 {
15712 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15713 }
15714 break;
15715 case VMA_MEMORY_USAGE_CPU_ONLY:
15716 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15717 break;
15718 case VMA_MEMORY_USAGE_CPU_TO_GPU:
15719 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15720 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15721 {
15722 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15723 }
15724 break;
15725 case VMA_MEMORY_USAGE_GPU_TO_CPU:
15726 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15727 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15728 break;
15729 default:
15730 break;
15731 }
15732
15733 *pMemoryTypeIndex = UINT32_MAX;
15734 uint32_t minCost = UINT32_MAX;
15735 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15736 memTypeIndex < allocator->GetMemoryTypeCount();
15737 ++memTypeIndex, memTypeBit <<= 1)
15738 {
15739 // This memory type is acceptable according to memoryTypeBits bitmask.
15740 if((memTypeBit & memoryTypeBits) != 0)
15741 {
15742 const VkMemoryPropertyFlags currFlags =
15743 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15744 // This memory type contains requiredFlags.
15745 if((requiredFlags & ~currFlags) == 0)
15746 {
15747 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15748 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15749 // Remember memory type with lowest cost.
15750 if(currCost < minCost)
15751 {
15752 *pMemoryTypeIndex = memTypeIndex;
15753 if(currCost == 0)
15754 {
15755 return VK_SUCCESS;
15756 }
15757 minCost = currCost;
15758 }
15759 }
15760 }
15761 }
15762 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15763}
15764
15765VkResult vmaFindMemoryTypeIndexForBufferInfo(
15766 VmaAllocator allocator,
15767 const VkBufferCreateInfo* pBufferCreateInfo,
15768 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15769 uint32_t* pMemoryTypeIndex)
15770{
15771 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15772 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15773 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15774 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15775
15776 const VkDevice hDev = allocator->m_hDevice;
15777 VkBuffer hBuffer = VK_NULL_HANDLE;
15778 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15779 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15780 if(res == VK_SUCCESS)
15781 {
15782 VkMemoryRequirements memReq = {};
15783 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15784 hDev, hBuffer, &memReq);
15785
15786 res = vmaFindMemoryTypeIndex(
15787 allocator,
15788 memReq.memoryTypeBits,
15789 pAllocationCreateInfo,
15790 pMemoryTypeIndex);
15791
15792 allocator->GetVulkanFunctions().vkDestroyBuffer(
15793 hDev, hBuffer, allocator->GetAllocationCallbacks());
15794 }
15795 return res;
15796}
15797
15798VkResult vmaFindMemoryTypeIndexForImageInfo(
15799 VmaAllocator allocator,
15800 const VkImageCreateInfo* pImageCreateInfo,
15801 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15802 uint32_t* pMemoryTypeIndex)
15803{
15804 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15805 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15806 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15807 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15808
15809 const VkDevice hDev = allocator->m_hDevice;
15810 VkImage hImage = VK_NULL_HANDLE;
15811 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15812 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15813 if(res == VK_SUCCESS)
15814 {
15815 VkMemoryRequirements memReq = {};
15816 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15817 hDev, hImage, &memReq);
15818
15819 res = vmaFindMemoryTypeIndex(
15820 allocator,
15821 memReq.memoryTypeBits,
15822 pAllocationCreateInfo,
15823 pMemoryTypeIndex);
15824
15825 allocator->GetVulkanFunctions().vkDestroyImage(
15826 hDev, hImage, allocator->GetAllocationCallbacks());
15827 }
15828 return res;
15829}
15830
15831VkResult vmaCreatePool(
15832 VmaAllocator allocator,
15833 const VmaPoolCreateInfo* pCreateInfo,
15834 VmaPool* pPool)
15835{
15836 VMA_ASSERT(allocator && pCreateInfo && pPool);
15837
15838 VMA_DEBUG_LOG("vmaCreatePool");
15839
15840 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15841
15842 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15843
15844#if VMA_RECORDING_ENABLED
15845 if(allocator->GetRecorder() != VMA_NULL)
15846 {
15847 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15848 }
15849#endif
15850
15851 return res;
15852}
15853
15854void vmaDestroyPool(
15855 VmaAllocator allocator,
15856 VmaPool pool)
15857{
15858 VMA_ASSERT(allocator);
15859
15860 if(pool == VK_NULL_HANDLE)
15861 {
15862 return;
15863 }
15864
15865 VMA_DEBUG_LOG("vmaDestroyPool");
15866
15867 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15868
15869#if VMA_RECORDING_ENABLED
15870 if(allocator->GetRecorder() != VMA_NULL)
15871 {
15872 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15873 }
15874#endif
15875
15876 allocator->DestroyPool(pool);
15877}
15878
15879void vmaGetPoolStats(
15880 VmaAllocator allocator,
15881 VmaPool pool,
15882 VmaPoolStats* pPoolStats)
15883{
15884 VMA_ASSERT(allocator && pool && pPoolStats);
15885
15886 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15887
15888 allocator->GetPoolStats(pool, pPoolStats);
15889}
15890
15891void vmaMakePoolAllocationsLost(
15892 VmaAllocator allocator,
15893 VmaPool pool,
15894 size_t* pLostAllocationCount)
15895{
15896 VMA_ASSERT(allocator && pool);
15897
15898 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15899
15900#if VMA_RECORDING_ENABLED
15901 if(allocator->GetRecorder() != VMA_NULL)
15902 {
15903 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15904 }
15905#endif
15906
15907 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15908}
15909
15910VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15911{
15912 VMA_ASSERT(allocator && pool);
15913
15914 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15915
15916 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15917
15918 return allocator->CheckPoolCorruption(pool);
15919}
15920
15921VkResult vmaAllocateMemory(
15922 VmaAllocator allocator,
15923 const VkMemoryRequirements* pVkMemoryRequirements,
15924 const VmaAllocationCreateInfo* pCreateInfo,
15925 VmaAllocation* pAllocation,
15926 VmaAllocationInfo* pAllocationInfo)
15927{
15928 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15929
15930 VMA_DEBUG_LOG("vmaAllocateMemory");
15931
15932 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15933
15934 VkResult result = allocator->AllocateMemory(
15935 *pVkMemoryRequirements,
15936 false, // requiresDedicatedAllocation
15937 false, // prefersDedicatedAllocation
15938 VK_NULL_HANDLE, // dedicatedBuffer
15939 VK_NULL_HANDLE, // dedicatedImage
15940 *pCreateInfo,
15941 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15942 1, // allocationCount
15943 pAllocation);
15944
15945#if VMA_RECORDING_ENABLED
15946 if(allocator->GetRecorder() != VMA_NULL)
15947 {
15948 allocator->GetRecorder()->RecordAllocateMemory(
15949 allocator->GetCurrentFrameIndex(),
15950 *pVkMemoryRequirements,
15951 *pCreateInfo,
15952 *pAllocation);
15953 }
15954#endif
15955
15956 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15957 {
15958 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15959 }
15960
15961 return result;
15962}
15963
15964VkResult vmaAllocateMemoryPages(
15965 VmaAllocator allocator,
15966 const VkMemoryRequirements* pVkMemoryRequirements,
15967 const VmaAllocationCreateInfo* pCreateInfo,
15968 size_t allocationCount,
15969 VmaAllocation* pAllocations,
15970 VmaAllocationInfo* pAllocationInfo)
15971{
15972 if(allocationCount == 0)
15973 {
15974 return VK_SUCCESS;
15975 }
15976
15977 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15978
15979 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15980
15981 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15982
15983 VkResult result = allocator->AllocateMemory(
15984 *pVkMemoryRequirements,
15985 false, // requiresDedicatedAllocation
15986 false, // prefersDedicatedAllocation
15987 VK_NULL_HANDLE, // dedicatedBuffer
15988 VK_NULL_HANDLE, // dedicatedImage
15989 *pCreateInfo,
15990 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15991 allocationCount,
15992 pAllocations);
15993
15994#if VMA_RECORDING_ENABLED
15995 if(allocator->GetRecorder() != VMA_NULL)
15996 {
15997 allocator->GetRecorder()->RecordAllocateMemoryPages(
15998 allocator->GetCurrentFrameIndex(),
15999 *pVkMemoryRequirements,
16000 *pCreateInfo,
16001 (uint64_t)allocationCount,
16002 pAllocations);
16003 }
16004#endif
16005
16006 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16007 {
16008 for(size_t i = 0; i < allocationCount; ++i)
16009 {
16010 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16011 }
16012 }
16013
16014 return result;
16015}
16016
16017VkResult vmaAllocateMemoryForBuffer(
16018 VmaAllocator allocator,
16019 VkBuffer buffer,
16020 const VmaAllocationCreateInfo* pCreateInfo,
16021 VmaAllocation* pAllocation,
16022 VmaAllocationInfo* pAllocationInfo)
16023{
16024 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16025
16026 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16027
16028 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16029
16030 VkMemoryRequirements vkMemReq = {};
16031 bool requiresDedicatedAllocation = false;
16032 bool prefersDedicatedAllocation = false;
16033 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16034 requiresDedicatedAllocation,
16035 prefersDedicatedAllocation);
16036
16037 VkResult result = allocator->AllocateMemory(
16038 vkMemReq,
16039 requiresDedicatedAllocation,
16040 prefersDedicatedAllocation,
16041 buffer, // dedicatedBuffer
16042 VK_NULL_HANDLE, // dedicatedImage
16043 *pCreateInfo,
16044 VMA_SUBALLOCATION_TYPE_BUFFER,
16045 1, // allocationCount
16046 pAllocation);
16047
16048#if VMA_RECORDING_ENABLED
16049 if(allocator->GetRecorder() != VMA_NULL)
16050 {
16051 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16052 allocator->GetCurrentFrameIndex(),
16053 vkMemReq,
16054 requiresDedicatedAllocation,
16055 prefersDedicatedAllocation,
16056 *pCreateInfo,
16057 *pAllocation);
16058 }
16059#endif
16060
16061 if(pAllocationInfo && result == VK_SUCCESS)
16062 {
16063 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16064 }
16065
16066 return result;
16067}
16068
16069VkResult vmaAllocateMemoryForImage(
16070 VmaAllocator allocator,
16071 VkImage image,
16072 const VmaAllocationCreateInfo* pCreateInfo,
16073 VmaAllocation* pAllocation,
16074 VmaAllocationInfo* pAllocationInfo)
16075{
16076 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16077
16078 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16079
16080 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16081
16082 VkMemoryRequirements vkMemReq = {};
16083 bool requiresDedicatedAllocation = false;
16084 bool prefersDedicatedAllocation = false;
16085 allocator->GetImageMemoryRequirements(image, vkMemReq,
16086 requiresDedicatedAllocation, prefersDedicatedAllocation);
16087
16088 VkResult result = allocator->AllocateMemory(
16089 vkMemReq,
16090 requiresDedicatedAllocation,
16091 prefersDedicatedAllocation,
16092 VK_NULL_HANDLE, // dedicatedBuffer
16093 image, // dedicatedImage
16094 *pCreateInfo,
16095 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16096 1, // allocationCount
16097 pAllocation);
16098
16099#if VMA_RECORDING_ENABLED
16100 if(allocator->GetRecorder() != VMA_NULL)
16101 {
16102 allocator->GetRecorder()->RecordAllocateMemoryForImage(
16103 allocator->GetCurrentFrameIndex(),
16104 vkMemReq,
16105 requiresDedicatedAllocation,
16106 prefersDedicatedAllocation,
16107 *pCreateInfo,
16108 *pAllocation);
16109 }
16110#endif
16111
16112 if(pAllocationInfo && result == VK_SUCCESS)
16113 {
16114 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16115 }
16116
16117 return result;
16118}
16119
16120void vmaFreeMemory(
16121 VmaAllocator allocator,
16122 VmaAllocation allocation)
16123{
16124 VMA_ASSERT(allocator);
16125
16126 if(allocation == VK_NULL_HANDLE)
16127 {
16128 return;
16129 }
16130
16131 VMA_DEBUG_LOG("vmaFreeMemory");
16132
16133 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16134
16135#if VMA_RECORDING_ENABLED
16136 if(allocator->GetRecorder() != VMA_NULL)
16137 {
16138 allocator->GetRecorder()->RecordFreeMemory(
16139 allocator->GetCurrentFrameIndex(),
16140 allocation);
16141 }
16142#endif
16143
16144 allocator->FreeMemory(
16145 1, // allocationCount
16146 &allocation);
16147}
16148
16149void vmaFreeMemoryPages(
16150 VmaAllocator allocator,
16151 size_t allocationCount,
16152 VmaAllocation* pAllocations)
16153{
16154 if(allocationCount == 0)
16155 {
16156 return;
16157 }
16158
16159 VMA_ASSERT(allocator);
16160
16161 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16162
16163 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16164
16165#if VMA_RECORDING_ENABLED
16166 if(allocator->GetRecorder() != VMA_NULL)
16167 {
16168 allocator->GetRecorder()->RecordFreeMemoryPages(
16169 allocator->GetCurrentFrameIndex(),
16170 (uint64_t)allocationCount,
16171 pAllocations);
16172 }
16173#endif
16174
16175 allocator->FreeMemory(allocationCount, pAllocations);
16176}
16177
16178VkResult vmaResizeAllocation(
16179 VmaAllocator allocator,
16180 VmaAllocation allocation,
16181 VkDeviceSize newSize)
16182{
16183 VMA_ASSERT(allocator && allocation);
16184
16185 VMA_DEBUG_LOG("vmaResizeAllocation");
16186
16187 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16188
16189#if VMA_RECORDING_ENABLED
16190 if(allocator->GetRecorder() != VMA_NULL)
16191 {
16192 allocator->GetRecorder()->RecordResizeAllocation(
16193 allocator->GetCurrentFrameIndex(),
16194 allocation,
16195 newSize);
16196 }
16197#endif
16198
16199 return allocator->ResizeAllocation(allocation, newSize);
16200}
16201
16202void vmaGetAllocationInfo(
16203 VmaAllocator allocator,
16204 VmaAllocation allocation,
16205 VmaAllocationInfo* pAllocationInfo)
16206{
16207 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16208
16209 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16210
16211#if VMA_RECORDING_ENABLED
16212 if(allocator->GetRecorder() != VMA_NULL)
16213 {
16214 allocator->GetRecorder()->RecordGetAllocationInfo(
16215 allocator->GetCurrentFrameIndex(),
16216 allocation);
16217 }
16218#endif
16219
16220 allocator->GetAllocationInfo(allocation, pAllocationInfo);
16221}
16222
16223VkBool32 vmaTouchAllocation(
16224 VmaAllocator allocator,
16225 VmaAllocation allocation)
16226{
16227 VMA_ASSERT(allocator && allocation);
16228
16229 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16230
16231#if VMA_RECORDING_ENABLED
16232 if(allocator->GetRecorder() != VMA_NULL)
16233 {
16234 allocator->GetRecorder()->RecordTouchAllocation(
16235 allocator->GetCurrentFrameIndex(),
16236 allocation);
16237 }
16238#endif
16239
16240 return allocator->TouchAllocation(allocation);
16241}
16242
16243void vmaSetAllocationUserData(
16244 VmaAllocator allocator,
16245 VmaAllocation allocation,
16246 void* pUserData)
16247{
16248 VMA_ASSERT(allocator && allocation);
16249
16250 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16251
16252 allocation->SetUserData(allocator, pUserData);
16253
16254#if VMA_RECORDING_ENABLED
16255 if(allocator->GetRecorder() != VMA_NULL)
16256 {
16257 allocator->GetRecorder()->RecordSetAllocationUserData(
16258 allocator->GetCurrentFrameIndex(),
16259 allocation,
16260 pUserData);
16261 }
16262#endif
16263}
16264
16265void vmaCreateLostAllocation(
16266 VmaAllocator allocator,
16267 VmaAllocation* pAllocation)
16268{
16269 VMA_ASSERT(allocator && pAllocation);
16270
16271 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16272
16273 allocator->CreateLostAllocation(pAllocation);
16274
16275#if VMA_RECORDING_ENABLED
16276 if(allocator->GetRecorder() != VMA_NULL)
16277 {
16278 allocator->GetRecorder()->RecordCreateLostAllocation(
16279 allocator->GetCurrentFrameIndex(),
16280 *pAllocation);
16281 }
16282#endif
16283}
16284
16285VkResult vmaMapMemory(
16286 VmaAllocator allocator,
16287 VmaAllocation allocation,
16288 void** ppData)
16289{
16290 VMA_ASSERT(allocator && allocation && ppData);
16291
16292 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16293
16294 VkResult res = allocator->Map(allocation, ppData);
16295
16296#if VMA_RECORDING_ENABLED
16297 if(allocator->GetRecorder() != VMA_NULL)
16298 {
16299 allocator->GetRecorder()->RecordMapMemory(
16300 allocator->GetCurrentFrameIndex(),
16301 allocation);
16302 }
16303#endif
16304
16305 return res;
16306}
16307
16308void vmaUnmapMemory(
16309 VmaAllocator allocator,
16310 VmaAllocation allocation)
16311{
16312 VMA_ASSERT(allocator && allocation);
16313
16314 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16315
16316#if VMA_RECORDING_ENABLED
16317 if(allocator->GetRecorder() != VMA_NULL)
16318 {
16319 allocator->GetRecorder()->RecordUnmapMemory(
16320 allocator->GetCurrentFrameIndex(),
16321 allocation);
16322 }
16323#endif
16324
16325 allocator->Unmap(allocation);
16326}
16327
16328void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16329{
16330 VMA_ASSERT(allocator && allocation);
16331
16332 VMA_DEBUG_LOG("vmaFlushAllocation");
16333
16334 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16335
16336 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16337
16338#if VMA_RECORDING_ENABLED
16339 if(allocator->GetRecorder() != VMA_NULL)
16340 {
16341 allocator->GetRecorder()->RecordFlushAllocation(
16342 allocator->GetCurrentFrameIndex(),
16343 allocation, offset, size);
16344 }
16345#endif
16346}
16347
16348void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16349{
16350 VMA_ASSERT(allocator && allocation);
16351
16352 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16353
16354 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16355
16356 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16357
16358#if VMA_RECORDING_ENABLED
16359 if(allocator->GetRecorder() != VMA_NULL)
16360 {
16361 allocator->GetRecorder()->RecordInvalidateAllocation(
16362 allocator->GetCurrentFrameIndex(),
16363 allocation, offset, size);
16364 }
16365#endif
16366}
16367
16368VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16369{
16370 VMA_ASSERT(allocator);
16371
16372 VMA_DEBUG_LOG("vmaCheckCorruption");
16373
16374 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16375
16376 return allocator->CheckCorruption(memoryTypeBits);
16377}
16378
16379VkResult vmaDefragment(
16380 VmaAllocator allocator,
16381 VmaAllocation* pAllocations,
16382 size_t allocationCount,
16383 VkBool32* pAllocationsChanged,
16384 const VmaDefragmentationInfo *pDefragmentationInfo,
16385 VmaDefragmentationStats* pDefragmentationStats)
16386{
16387 // Deprecated interface, reimplemented using new one.
16388
16389 VmaDefragmentationInfo2 info2 = {};
16390 info2.allocationCount = (uint32_t)allocationCount;
16391 info2.pAllocations = pAllocations;
16392 info2.pAllocationsChanged = pAllocationsChanged;
16393 if(pDefragmentationInfo != VMA_NULL)
16394 {
16395 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16396 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16397 }
16398 else
16399 {
16400 info2.maxCpuAllocationsToMove = UINT32_MAX;
16401 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16402 }
16403 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16404
16405 VmaDefragmentationContext ctx;
16406 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16407 if(res == VK_NOT_READY)
16408 {
16409 res = vmaDefragmentationEnd( allocator, ctx);
16410 }
16411 return res;
16412}
16413
16414VkResult vmaDefragmentationBegin(
16415 VmaAllocator allocator,
16416 const VmaDefragmentationInfo2* pInfo,
16417 VmaDefragmentationStats* pStats,
16418 VmaDefragmentationContext *pContext)
16419{
16420 VMA_ASSERT(allocator && pInfo && pContext);
16421
16422 // Degenerate case: Nothing to defragment.
16423 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16424 {
16425 return VK_SUCCESS;
16426 }
16427
16428 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16429 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16430 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16431 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16432
16433 VMA_DEBUG_LOG("vmaDefragmentationBegin");
16434
16435 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16436
16437 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16438
16439#if VMA_RECORDING_ENABLED
16440 if(allocator->GetRecorder() != VMA_NULL)
16441 {
16442 allocator->GetRecorder()->RecordDefragmentationBegin(
16443 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16444 }
16445#endif
16446
16447 return res;
16448}
16449
16450VkResult vmaDefragmentationEnd(
16451 VmaAllocator allocator,
16452 VmaDefragmentationContext context)
16453{
16454 VMA_ASSERT(allocator);
16455
16456 VMA_DEBUG_LOG("vmaDefragmentationEnd");
16457
16458 if(context != VK_NULL_HANDLE)
16459 {
16460 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16461
16462#if VMA_RECORDING_ENABLED
16463 if(allocator->GetRecorder() != VMA_NULL)
16464 {
16465 allocator->GetRecorder()->RecordDefragmentationEnd(
16466 allocator->GetCurrentFrameIndex(), context);
16467 }
16468#endif
16469
16470 return allocator->DefragmentationEnd(context);
16471 }
16472 else
16473 {
16474 return VK_SUCCESS;
16475 }
16476}
16477
16478VkResult vmaBindBufferMemory(
16479 VmaAllocator allocator,
16480 VmaAllocation allocation,
16481 VkBuffer buffer)
16482{
16483 VMA_ASSERT(allocator && allocation && buffer);
16484
16485 VMA_DEBUG_LOG("vmaBindBufferMemory");
16486
16487 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16488
16489 return allocator->BindBufferMemory(allocation, buffer);
16490}
16491
16492VkResult vmaBindImageMemory(
16493 VmaAllocator allocator,
16494 VmaAllocation allocation,
16495 VkImage image)
16496{
16497 VMA_ASSERT(allocator && allocation && image);
16498
16499 VMA_DEBUG_LOG("vmaBindImageMemory");
16500
16501 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16502
16503 return allocator->BindImageMemory(allocation, image);
16504}
16505
16506VkResult vmaCreateBuffer(
16507 VmaAllocator allocator,
16508 const VkBufferCreateInfo* pBufferCreateInfo,
16509 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16510 VkBuffer* pBuffer,
16511 VmaAllocation* pAllocation,
16512 VmaAllocationInfo* pAllocationInfo)
16513{
16514 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16515
16516 if(pBufferCreateInfo->size == 0)
16517 {
16518 return VK_ERROR_VALIDATION_FAILED_EXT;
16519 }
16520
16521 VMA_DEBUG_LOG("vmaCreateBuffer");
16522
16523 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16524
16525 *pBuffer = VK_NULL_HANDLE;
16526 *pAllocation = VK_NULL_HANDLE;
16527
16528 // 1. Create VkBuffer.
16529 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16530 allocator->m_hDevice,
16531 pBufferCreateInfo,
16532 allocator->GetAllocationCallbacks(),
16533 pBuffer);
16534 if(res >= 0)
16535 {
16536 // 2. vkGetBufferMemoryRequirements.
16537 VkMemoryRequirements vkMemReq = {};
16538 bool requiresDedicatedAllocation = false;
16539 bool prefersDedicatedAllocation = false;
16540 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16541 requiresDedicatedAllocation, prefersDedicatedAllocation);
16542
16543 // Make sure alignment requirements for specific buffer usages reported
16544 // in Physical Device Properties are included in alignment reported by memory requirements.
16545 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16546 {
16547 VMA_ASSERT(vkMemReq.alignment %
16548 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16549 }
16550 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16551 {
16552 VMA_ASSERT(vkMemReq.alignment %
16553 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16554 }
16555 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16556 {
16557 VMA_ASSERT(vkMemReq.alignment %
16558 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16559 }
16560
16561 // 3. Allocate memory using allocator.
16562 res = allocator->AllocateMemory(
16563 vkMemReq,
16564 requiresDedicatedAllocation,
16565 prefersDedicatedAllocation,
16566 *pBuffer, // dedicatedBuffer
16567 VK_NULL_HANDLE, // dedicatedImage
16568 *pAllocationCreateInfo,
16569 VMA_SUBALLOCATION_TYPE_BUFFER,
16570 1, // allocationCount
16571 pAllocation);
16572
16573#if VMA_RECORDING_ENABLED
16574 if(allocator->GetRecorder() != VMA_NULL)
16575 {
16576 allocator->GetRecorder()->RecordCreateBuffer(
16577 allocator->GetCurrentFrameIndex(),
16578 *pBufferCreateInfo,
16579 *pAllocationCreateInfo,
16580 *pAllocation);
16581 }
16582#endif
16583
16584 if(res >= 0)
16585 {
16586 // 3. Bind buffer with memory.
16587 res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16588 if(res >= 0)
16589 {
16590 // All steps succeeded.
16591 #if VMA_STATS_STRING_ENABLED
16592 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16593 #endif
16594 if(pAllocationInfo != VMA_NULL)
16595 {
16596 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16597 }
16598
16599 return VK_SUCCESS;
16600 }
16601 allocator->FreeMemory(
16602 1, // allocationCount
16603 pAllocation);
16604 *pAllocation = VK_NULL_HANDLE;
16605 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16606 *pBuffer = VK_NULL_HANDLE;
16607 return res;
16608 }
16609 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16610 *pBuffer = VK_NULL_HANDLE;
16611 return res;
16612 }
16613 return res;
16614}
16615
16616void vmaDestroyBuffer(
16617 VmaAllocator allocator,
16618 VkBuffer buffer,
16619 VmaAllocation allocation)
16620{
16621 VMA_ASSERT(allocator);
16622
16623 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16624 {
16625 return;
16626 }
16627
16628 VMA_DEBUG_LOG("vmaDestroyBuffer");
16629
16630 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16631
16632#if VMA_RECORDING_ENABLED
16633 if(allocator->GetRecorder() != VMA_NULL)
16634 {
16635 allocator->GetRecorder()->RecordDestroyBuffer(
16636 allocator->GetCurrentFrameIndex(),
16637 allocation);
16638 }
16639#endif
16640
16641 if(buffer != VK_NULL_HANDLE)
16642 {
16643 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16644 }
16645
16646 if(allocation != VK_NULL_HANDLE)
16647 {
16648 allocator->FreeMemory(
16649 1, // allocationCount
16650 &allocation);
16651 }
16652}
16653
16654VkResult vmaCreateImage(
16655 VmaAllocator allocator,
16656 const VkImageCreateInfo* pImageCreateInfo,
16657 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16658 VkImage* pImage,
16659 VmaAllocation* pAllocation,
16660 VmaAllocationInfo* pAllocationInfo)
16661{
16662 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16663
16664 if(pImageCreateInfo->extent.width == 0 ||
16665 pImageCreateInfo->extent.height == 0 ||
16666 pImageCreateInfo->extent.depth == 0 ||
16667 pImageCreateInfo->mipLevels == 0 ||
16668 pImageCreateInfo->arrayLayers == 0)
16669 {
16670 return VK_ERROR_VALIDATION_FAILED_EXT;
16671 }
16672
16673 VMA_DEBUG_LOG("vmaCreateImage");
16674
16675 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16676
16677 *pImage = VK_NULL_HANDLE;
16678 *pAllocation = VK_NULL_HANDLE;
16679
16680 // 1. Create VkImage.
16681 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16682 allocator->m_hDevice,
16683 pImageCreateInfo,
16684 allocator->GetAllocationCallbacks(),
16685 pImage);
16686 if(res >= 0)
16687 {
16688 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16689 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16690 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16691
16692 // 2. Allocate memory using allocator.
16693 VkMemoryRequirements vkMemReq = {};
16694 bool requiresDedicatedAllocation = false;
16695 bool prefersDedicatedAllocation = false;
16696 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16697 requiresDedicatedAllocation, prefersDedicatedAllocation);
16698
16699 res = allocator->AllocateMemory(
16700 vkMemReq,
16701 requiresDedicatedAllocation,
16702 prefersDedicatedAllocation,
16703 VK_NULL_HANDLE, // dedicatedBuffer
16704 *pImage, // dedicatedImage
16705 *pAllocationCreateInfo,
16706 suballocType,
16707 1, // allocationCount
16708 pAllocation);
16709
16710#if VMA_RECORDING_ENABLED
16711 if(allocator->GetRecorder() != VMA_NULL)
16712 {
16713 allocator->GetRecorder()->RecordCreateImage(
16714 allocator->GetCurrentFrameIndex(),
16715 *pImageCreateInfo,
16716 *pAllocationCreateInfo,
16717 *pAllocation);
16718 }
16719#endif
16720
16721 if(res >= 0)
16722 {
16723 // 3. Bind image with memory.
16724 res = allocator->BindImageMemory(*pAllocation, *pImage);
16725 if(res >= 0)
16726 {
16727 // All steps succeeded.
16728 #if VMA_STATS_STRING_ENABLED
16729 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16730 #endif
16731 if(pAllocationInfo != VMA_NULL)
16732 {
16733 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16734 }
16735
16736 return VK_SUCCESS;
16737 }
16738 allocator->FreeMemory(
16739 1, // allocationCount
16740 pAllocation);
16741 *pAllocation = VK_NULL_HANDLE;
16742 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16743 *pImage = VK_NULL_HANDLE;
16744 return res;
16745 }
16746 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16747 *pImage = VK_NULL_HANDLE;
16748 return res;
16749 }
16750 return res;
16751}
16752
16753void vmaDestroyImage(
16754 VmaAllocator allocator,
16755 VkImage image,
16756 VmaAllocation allocation)
16757{
16758 VMA_ASSERT(allocator);
16759
16760 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16761 {
16762 return;
16763 }
16764
16765 VMA_DEBUG_LOG("vmaDestroyImage");
16766
16767 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16768
16769#if VMA_RECORDING_ENABLED
16770 if(allocator->GetRecorder() != VMA_NULL)
16771 {
16772 allocator->GetRecorder()->RecordDestroyImage(
16773 allocator->GetCurrentFrameIndex(),
16774 allocation);
16775 }
16776#endif
16777
16778 if(image != VK_NULL_HANDLE)
16779 {
16780 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16781 }
16782 if(allocation != VK_NULL_HANDLE)
16783 {
16784 allocator->FreeMemory(
16785 1, // allocationCount
16786 &allocation);
16787 }
16788}
Tony-LunarG390319b2019-03-18 15:54:16 -060016789#if defined(__GNUC__)
16790#pragma GCC diagnostic pop
16791#endif
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060016792#endif // #ifdef VMA_IMPLEMENTATION
Tony-LunarG0e564722019-03-19 16:09:14 -060016793// clang-format on