blob: 629c2fb76173e4023248e945eb848e0e9613c8c7 [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
Jeremy Hayes33838bf2019-06-05 14:01:20 -060031// 6/05/19 - Make changes to suppress warnings from clang 3.8.0
Tony-LunarG705b4cb2019-05-22 15:32:30 -060032// 6/05/19 - Make changes to suppress more warnings from GCC
Tony-LunarG390319b2019-03-18 15:54:16 -060033//
34
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060035#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
36#define AMD_VULKAN_MEMORY_ALLOCATOR_H
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42/** \mainpage Vulkan Memory Allocator
43
44<b>Version 2.2.0</b> (2018-12-13)
45
46Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
47License: MIT
48
49Documentation of all members: vk_mem_alloc.h
50
51\section main_table_of_contents Table of contents
52
53- <b>User guide</b>
54 - \subpage quick_start
55 - [Project setup](@ref quick_start_project_setup)
56 - [Initialization](@ref quick_start_initialization)
57 - [Resource allocation](@ref quick_start_resource_allocation)
58 - \subpage choosing_memory_type
59 - [Usage](@ref choosing_memory_type_usage)
60 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
61 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
62 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
63 - \subpage memory_mapping
64 - [Mapping functions](@ref memory_mapping_mapping_functions)
65 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
66 - [Cache control](@ref memory_mapping_cache_control)
67 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
68 - \subpage custom_memory_pools
69 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
70 - [Linear allocation algorithm](@ref linear_algorithm)
71 - [Free-at-once](@ref linear_algorithm_free_at_once)
72 - [Stack](@ref linear_algorithm_stack)
73 - [Double stack](@ref linear_algorithm_double_stack)
74 - [Ring buffer](@ref linear_algorithm_ring_buffer)
75 - [Buddy allocation algorithm](@ref buddy_algorithm)
76 - \subpage defragmentation
77 - [Defragmenting CPU memory](@ref defragmentation_cpu)
78 - [Defragmenting GPU memory](@ref defragmentation_gpu)
79 - [Additional notes](@ref defragmentation_additional_notes)
80 - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
81 - \subpage lost_allocations
82 - \subpage statistics
83 - [Numeric statistics](@ref statistics_numeric_statistics)
84 - [JSON dump](@ref statistics_json_dump)
85 - \subpage allocation_annotation
86 - [Allocation user data](@ref allocation_user_data)
87 - [Allocation names](@ref allocation_names)
88 - \subpage debugging_memory_usage
89 - [Memory initialization](@ref debugging_memory_usage_initialization)
90 - [Margins](@ref debugging_memory_usage_margins)
91 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
92 - \subpage record_and_replay
93- \subpage usage_patterns
94 - [Simple patterns](@ref usage_patterns_simple)
95 - [Advanced patterns](@ref usage_patterns_advanced)
96- \subpage configuration
97 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
98 - [Custom host memory allocator](@ref custom_memory_allocator)
99 - [Device memory allocation callbacks](@ref allocation_callbacks)
100 - [Device heap memory limit](@ref heap_memory_limit)
101 - \subpage vk_khr_dedicated_allocation
102- \subpage general_considerations
103 - [Thread safety](@ref general_considerations_thread_safety)
104 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
105 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
106 - [Features not supported](@ref general_considerations_features_not_supported)
107
108\section main_see_also See also
109
110- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
111- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
112
113
114
115
116\page quick_start Quick start
117
118\section quick_start_project_setup Project setup
119
120Vulkan Memory Allocator comes in form of a single header file.
121You don't need to build it as a separate library project.
122You can add this file directly to your project and submit it to code repository next to your other source files.
123
124"Single header" doesn't mean that everything is contained in C/C++ declarations,
125like it tends to be in case of inline functions or C++ templates.
126It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
127If you don't do it properly, you will get linker errors.
128
129To do it properly:
130
131-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
132 This includes declarations of all members of the library.
133-# In exacly one CPP file define following macro before this include.
134 It enables also internal definitions.
135
136\code
137#define VMA_IMPLEMENTATION
138#include "vk_mem_alloc.h"
139\endcode
140
141It may be a good idea to create dedicated CPP file just for this purpose.
142
143Note on language: This library is written in C++, but has C-compatible interface.
144Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
145implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
146
147Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
148includes `<windows.h>` on Windows. If you need some specific macros defined
149before including these headers (like `WIN32_LEAN_AND_MEAN` or
150`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
151them before every `#include` of this library.
152
153
154\section quick_start_initialization Initialization
155
156At program startup:
157
158-# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
159-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
160 calling vmaCreateAllocator().
161
162\code
163VmaAllocatorCreateInfo allocatorInfo = {};
164allocatorInfo.physicalDevice = physicalDevice;
165allocatorInfo.device = device;
166
167VmaAllocator allocator;
168vmaCreateAllocator(&allocatorInfo, &allocator);
169\endcode
170
171\section quick_start_resource_allocation Resource allocation
172
173When you want to create a buffer or image:
174
175-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
176-# Fill VmaAllocationCreateInfo structure.
177-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
178 already allocated and bound to it.
179
180\code
181VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
182bufferInfo.size = 65536;
183bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
184
185VmaAllocationCreateInfo allocInfo = {};
186allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
187
188VkBuffer buffer;
189VmaAllocation allocation;
190vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
191\endcode
192
193Don't forget to destroy your objects when no longer needed:
194
195\code
196vmaDestroyBuffer(allocator, buffer, allocation);
197vmaDestroyAllocator(allocator);
198\endcode
199
200
201\page choosing_memory_type Choosing memory type
202
203Physical devices in Vulkan support various combinations of memory heaps and
204types. Help with choosing correct and optimal memory type for your specific
205resource is one of the key features of this library. You can use it by filling
206appropriate members of VmaAllocationCreateInfo structure, as described below.
207You can also combine multiple methods.
208
209-# If you just want to find memory type index that meets your requirements, you
210 can use function vmaFindMemoryTypeIndex().
211-# If you want to allocate a region of device memory without association with any
212 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
213 this function is not recommended and usually not needed.
214-# If you already have a buffer or an image created, you want to allocate memory
215 for it and then you will bind it yourself, you can use function
216 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
217 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
218-# If you want to create a buffer or an image, allocate memory for it and bind
219 them together, all in one call, you can use function vmaCreateBuffer(),
220 vmaCreateImage(). This is the recommended way to use this library.
221
222When using 3. or 4., the library internally queries Vulkan for memory types
223supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
224and uses only one of these types.
225
226If no memory type can be found that meets all the requirements, these functions
227return `VK_ERROR_FEATURE_NOT_PRESENT`.
228
229You can leave VmaAllocationCreateInfo structure completely filled with zeros.
230It means no requirements are specified for memory type.
231It is valid, although not very useful.
232
233\section choosing_memory_type_usage Usage
234
235The easiest way to specify memory requirements is to fill member
236VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
237It defines high level, common usage types.
238For more details, see description of this enum.
239
240For example, if you want to create a uniform buffer that will be filled using
241transfer only once or infrequently and used for rendering every frame, you can
242do it using following code:
243
244\code
245VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
246bufferInfo.size = 65536;
247bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
248
249VmaAllocationCreateInfo allocInfo = {};
250allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
251
252VkBuffer buffer;
253VmaAllocation allocation;
254vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
255\endcode
256
257\section choosing_memory_type_required_preferred_flags Required and preferred flags
258
259You can specify more detailed requirements by filling members
260VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
261with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
262if you want to create a buffer that will be persistently mapped on host (so it
263must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
264use following code:
265
266\code
267VmaAllocationCreateInfo allocInfo = {};
268allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
269allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
270allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
271
272VkBuffer buffer;
273VmaAllocation allocation;
274vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
275\endcode
276
277A memory type is chosen that has all the required flags and as many preferred
278flags set as possible.
279
280If you use VmaAllocationCreateInfo::usage, it is just internally converted to
281a set of required and preferred flags.
282
283\section choosing_memory_type_explicit_memory_types Explicit memory types
284
285If you inspected memory types available on the physical device and you have
286a preference for memory types that you want to use, you can fill member
287VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
288means that a memory type with that index is allowed to be used for the
289allocation. Special value 0, just like `UINT32_MAX`, means there are no
290restrictions to memory type index.
291
292Please note that this member is NOT just a memory type index.
293Still you can use it to choose just one, specific memory type.
294For example, if you already determined that your buffer should be created in
295memory type 2, use following code:
296
297\code
298uint32_t memoryTypeIndex = 2;
299
300VmaAllocationCreateInfo allocInfo = {};
301allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
302
303VkBuffer buffer;
304VmaAllocation allocation;
305vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
306\endcode
307
308\section choosing_memory_type_custom_memory_pools Custom memory pools
309
310If you allocate from custom memory pool, all the ways of specifying memory
311requirements described above are not applicable and the aforementioned members
312of VmaAllocationCreateInfo structure are ignored. Memory type is selected
313explicitly when creating the pool and then used to make all the allocations from
314that pool. For further details, see \ref custom_memory_pools.
315
316
317\page memory_mapping Memory mapping
318
319To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
320to be able to read from it or write to it in CPU code.
321Mapping is possible only of memory allocated from a memory type that has
322`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
323Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
324You can use them directly with memory allocated by this library,
325but it is not recommended because of following issue:
326Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
327This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
328Because of this, Vulkan Memory Allocator provides following facilities:
329
330\section memory_mapping_mapping_functions Mapping functions
331
332The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
333They are safer and more convenient to use than standard Vulkan functions.
334You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
335You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
336The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
337For further details, see description of vmaMapMemory() function.
338Example:
339
340\code
341// Having these objects initialized:
342
343struct ConstantBuffer
344{
345 ...
346};
347ConstantBuffer constantBufferData;
348
349VmaAllocator allocator;
350VkBuffer constantBuffer;
351VmaAllocation constantBufferAllocation;
352
353// You can map and fill your buffer using following code:
354
355void* mappedData;
356vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
357memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
358vmaUnmapMemory(allocator, constantBufferAllocation);
359\endcode
360
361When mapping, you may see a warning from Vulkan validation layer similar to this one:
362
363<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>
364
365It happens because the library maps entire `VkDeviceMemory` block, where different
366types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
367You can safely ignore it if you are sure you access only memory of the intended
368object that you wanted to map.
369
370
371\section memory_mapping_persistently_mapped_memory Persistently mapped memory
372
373Kepping your memory persistently mapped is generally OK in Vulkan.
374You don't need to unmap it before using its data on the GPU.
375The library provides a special feature designed for that:
376Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
377VmaAllocationCreateInfo::flags stay mapped all the time,
378so you can just access CPU pointer to it any time
379without a need to call any "map" or "unmap" function.
380Example:
381
382\code
383VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
384bufCreateInfo.size = sizeof(ConstantBuffer);
385bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
386
387VmaAllocationCreateInfo allocCreateInfo = {};
388allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
389allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
390
391VkBuffer buf;
392VmaAllocation alloc;
393VmaAllocationInfo allocInfo;
394vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
395
396// Buffer is already mapped. You can access its memory.
397memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
398\endcode
399
400There are some exceptions though, when you should consider mapping memory only for a short period of time:
401
402- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
403 device is discrete AMD GPU,
404 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
405 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
406 then whenever a memory block allocated from this memory type stays mapped
407 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
408 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
409 matter if that particular memory block is actually used by the command buffer
410 being submitted.
411- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
412 which requires unmapping before GPU can see updated texture.
413- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
414
415\section memory_mapping_cache_control Cache control
416
417Memory in Vulkan doesn't need to be unmapped before using it on GPU,
418but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
419you need to manually invalidate cache before reading of mapped pointer
420and flush cache after writing to mapped pointer.
421Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
422`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
423functions that refer to given allocation object: vmaFlushAllocation(),
424vmaInvalidateAllocation().
425
426Regions of memory specified for flush/invalidate must be aligned to
427`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
428In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
429within blocks are aligned to this value, so their offsets are always multiply of
430`nonCoherentAtomSize` and two different allocations never share same "line" of this size.
431
432Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
433
434Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
435currently provide `HOST_COHERENT` flag on all memory types that are
436`HOST_VISIBLE`, so on this platform you may not need to bother.
437
438\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
439
440It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
441despite it wasn't explicitly requested.
442For example, application may work on integrated graphics with unified memory (like Intel) or
443allocation from video memory might have failed, so the library chose system memory as fallback.
444
445You can detect this case and map such allocation to access its memory on CPU directly,
446instead of launching a transfer operation.
447In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
448and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
449
450\code
451VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
452bufCreateInfo.size = sizeof(ConstantBuffer);
453bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
454
455VmaAllocationCreateInfo allocCreateInfo = {};
456allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
457allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
458
459VkBuffer buf;
460VmaAllocation alloc;
461VmaAllocationInfo allocInfo;
462vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
463
464VkMemoryPropertyFlags memFlags;
465vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
466if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
467{
468 // Allocation ended up in mappable memory. You can map it and access it directly.
469 void* mappedData;
470 vmaMapMemory(allocator, alloc, &mappedData);
471 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
472 vmaUnmapMemory(allocator, alloc);
473}
474else
475{
476 // Allocation ended up in non-mappable memory.
477 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
478}
479\endcode
480
481You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
482that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
483If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
484If not, the flag is just ignored.
485Example:
486
487\code
488VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
489bufCreateInfo.size = sizeof(ConstantBuffer);
490bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
491
492VmaAllocationCreateInfo allocCreateInfo = {};
493allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
494allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
495
496VkBuffer buf;
497VmaAllocation alloc;
498VmaAllocationInfo allocInfo;
499vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
500
501if(allocInfo.pUserData != nullptr)
502{
503 // Allocation ended up in mappable memory.
504 // It's persistently mapped. You can access it directly.
505 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
506}
507else
508{
509 // Allocation ended up in non-mappable memory.
510 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
511}
512\endcode
513
514
515\page custom_memory_pools Custom memory pools
516
517A memory pool contains a number of `VkDeviceMemory` blocks.
518The library automatically creates and manages default pool for each memory type available on the device.
519Default memory pool automatically grows in size.
520Size of allocated blocks is also variable and managed automatically.
521
522You can create custom pool and allocate memory out of it.
523It can be useful if you want to:
524
525- Keep certain kind of allocations separate from others.
526- Enforce particular, fixed size of Vulkan memory blocks.
527- Limit maximum amount of Vulkan memory allocated for that pool.
528- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
529
530To use custom memory pools:
531
532-# Fill VmaPoolCreateInfo structure.
533-# Call vmaCreatePool() to obtain #VmaPool handle.
534-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
535 You don't need to specify any other parameters of this structure, like `usage`.
536
537Example:
538
539\code
540// Create a pool that can have at most 2 blocks, 128 MiB each.
541VmaPoolCreateInfo poolCreateInfo = {};
542poolCreateInfo.memoryTypeIndex = ...
543poolCreateInfo.blockSize = 128ull * 1024 * 1024;
544poolCreateInfo.maxBlockCount = 2;
545
546VmaPool pool;
547vmaCreatePool(allocator, &poolCreateInfo, &pool);
548
549// Allocate a buffer out of it.
550VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
551bufCreateInfo.size = 1024;
552bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
553
554VmaAllocationCreateInfo allocCreateInfo = {};
555allocCreateInfo.pool = pool;
556
557VkBuffer buf;
558VmaAllocation alloc;
559VmaAllocationInfo allocInfo;
560vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
561\endcode
562
563You have to free all allocations made from this pool before destroying it.
564
565\code
566vmaDestroyBuffer(allocator, buf, alloc);
567vmaDestroyPool(allocator, pool);
568\endcode
569
570\section custom_memory_pools_MemTypeIndex Choosing memory type index
571
572When creating a pool, you must explicitly specify memory type index.
573To find the one suitable for your buffers or images, you can use helper functions
574vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
575You need to provide structures with example parameters of buffers or images
576that you are going to create in that pool.
577
578\code
579VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
580exampleBufCreateInfo.size = 1024; // Whatever.
581exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
582
583VmaAllocationCreateInfo allocCreateInfo = {};
584allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
585
586uint32_t memTypeIndex;
587vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
588
589VmaPoolCreateInfo poolCreateInfo = {};
590poolCreateInfo.memoryTypeIndex = memTypeIndex;
591// ...
592\endcode
593
594When creating buffers/images allocated in that pool, provide following parameters:
595
596- `VkBufferCreateInfo`: Prefer to pass same parameters as above.
597 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
598 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
599 or the other way around.
600- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
601 Other members are ignored anyway.
602
603\section linear_algorithm Linear allocation algorithm
604
605Each Vulkan memory block managed by this library has accompanying metadata that
606keeps track of used and unused regions. By default, the metadata structure and
607algorithm tries to find best place for new allocations among free regions to
608optimize memory usage. This way you can allocate and free objects in any order.
609
610![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
611
612Sometimes there is a need to use simpler, linear allocation algorithm. You can
613create custom pool that uses such algorithm by adding flag
614#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
615#VmaPool object. Then an alternative metadata management is used. It always
616creates new allocations after last one and doesn't reuse free regions after
617allocations freed in the middle. It results in better allocation performance and
618less memory consumed by metadata.
619
620![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
621
622With this one flag, you can create a custom pool that can be used in many ways:
623free-at-once, stack, double stack, and ring buffer. See below for details.
624
625\subsection linear_algorithm_free_at_once Free-at-once
626
627In a pool that uses linear algorithm, you still need to free all the allocations
628individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
629them in any order. New allocations are always made after last one - free space
630in the middle is not reused. However, when you release all the allocation and
631the pool becomes empty, allocation starts from the beginning again. This way you
632can use linear algorithm to speed up creation of allocations that you are going
633to release all at once.
634
635![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
636
637This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
638value that allows multiple memory blocks.
639
640\subsection linear_algorithm_stack Stack
641
642When you free an allocation that was created last, its space can be reused.
643Thanks to this, if you always release allocations in the order opposite to their
644creation (LIFO - Last In First Out), you can achieve behavior of a stack.
645
646![Stack](../gfx/Linear_allocator_4_stack.png)
647
648This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
649value that allows multiple memory blocks.
650
651\subsection linear_algorithm_double_stack Double stack
652
653The space reserved by a custom pool with linear algorithm may be used by two
654stacks:
655
656- First, default one, growing up from offset 0.
657- Second, "upper" one, growing down from the end towards lower offsets.
658
659To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
660to VmaAllocationCreateInfo::flags.
661
662![Double stack](../gfx/Linear_allocator_7_double_stack.png)
663
664Double stack is available only in pools with one memory block -
665VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
666
667When the two stacks' ends meet so there is not enough space between them for a
668new allocation, such allocation fails with usual
669`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
670
671\subsection linear_algorithm_ring_buffer Ring buffer
672
673When you free some allocations from the beginning and there is not enough free space
674for a new one at the end of a pool, allocator's "cursor" wraps around to the
675beginning and starts allocation there. Thanks to this, if you always release
676allocations in the same order as you created them (FIFO - First In First Out),
677you can achieve behavior of a ring buffer / queue.
678
679![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
680
681Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
682If there is not enough free space for a new allocation, but existing allocations
683from the front of the queue can become lost, they become lost and the allocation
684succeeds.
685
686![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
687
688Ring buffer is available only in pools with one memory block -
689VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
690
691\section buddy_algorithm Buddy allocation algorithm
692
693There is another allocation algorithm that can be used with custom pools, called
694"buddy". Its internal data structure is based on a tree of blocks, each having
695size that is a power of two and a half of its parent's size. When you want to
696allocate memory of certain size, a free node in the tree is located. If it's too
697large, it is recursively split into two halves (called "buddies"). However, if
698requested allocation size is not a power of two, the size of a tree node is
699aligned up to the nearest power of two and the remaining space is wasted. When
700two buddy nodes become free, they are merged back into one larger node.
701
702![Buddy allocator](../gfx/Buddy_allocator.png)
703
704The advantage of buddy allocation algorithm over default algorithm is faster
705allocation and deallocation, as well as smaller external fragmentation. The
706disadvantage is more wasted space (internal fragmentation).
707
708For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
709or other sources that describe this concept in general.
710
711To use buddy allocation algorithm with a custom pool, add flag
712#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
713#VmaPool object.
714
715Several limitations apply to pools that use buddy algorithm:
716
717- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
718 Otherwise, only largest power of two smaller than the size is used for
719 allocations. The remaining space always stays unused.
720- [Margins](@ref debugging_memory_usage_margins) and
721 [corruption detection](@ref debugging_memory_usage_corruption_detection)
722 don't work in such pools.
723- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
724 use them, but they never become lost. Support may be added in the future.
725- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
726 such pool.
727
728\page defragmentation Defragmentation
729
730Interleaved allocations and deallocations of many objects of varying size can
731cause fragmentation over time, which can lead to a situation where the library is unable
732to find a continuous range of free memory for a new allocation despite there is
733enough free space, just scattered across many small free ranges between existing
734allocations.
735
736To mitigate this problem, you can use defragmentation feature:
737structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
738Given set of allocations,
739this function can move them to compact used memory, ensure more continuous free
740space and possibly also free some `VkDeviceMemory` blocks.
741
742What the defragmentation does is:
743
744- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
745 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
746 VmaAllocationInfo::offset changes. You must query them again using
747 vmaGetAllocationInfo() if you need them.
748- Moves actual data in memory.
749
750What it doesn't do, so you need to do it yourself:
751
752- Recreate buffers and images that were bound to allocations that were defragmented and
753 bind them with their new places in memory.
754 You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
755 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
756 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
757 destroy or create allocation objects!
758- Recreate views and update descriptors that point to these buffers and images.
759
760\section defragmentation_cpu Defragmenting CPU memory
761
762Following example demonstrates how you can run defragmentation on CPU.
763Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
764Others are ignored.
765
766The way it works is:
767
768- It temporarily maps entire memory blocks when necessary.
769- It moves data using `memmove()` function.
770
771\code
772// Given following variables already initialized:
773VkDevice device;
774VmaAllocator allocator;
775std::vector<VkBuffer> buffers;
776std::vector<VmaAllocation> allocations;
777
778
779const uint32_t allocCount = (uint32_t)allocations.size();
780std::vector<VkBool32> allocationsChanged(allocCount);
781
782VmaDefragmentationInfo2 defragInfo = {};
783defragInfo.allocationCount = allocCount;
784defragInfo.pAllocations = allocations.data();
785defragInfo.pAllocationsChanged = allocationsChanged.data();
786defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
787defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
788
789VmaDefragmentationContext defragCtx;
790vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
791vmaDefragmentationEnd(allocator, defragCtx);
792
793for(uint32_t i = 0; i < allocCount; ++i)
794{
795 if(allocationsChanged[i])
796 {
797 // Destroy buffer that is immutably bound to memory region which is no longer valid.
798 vkDestroyBuffer(device, buffers[i], nullptr);
799
800 // Create new buffer with same parameters.
801 VkBufferCreateInfo bufferInfo = ...;
802 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
803
804 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
805
806 // Bind new buffer to new memory region. Data contained in it is already moved.
807 VmaAllocationInfo allocInfo;
808 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
809 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
810 }
811}
812\endcode
813
814Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
815This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
816has been modified during defragmentation.
817You can pass null, but you then need to query every allocation passed to defragmentation
818for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
819
820If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
821you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
822instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
823to defragment all allocations in given pools.
824You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
825You can also combine both methods.
826
827\section defragmentation_gpu Defragmenting GPU memory
828
829It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
830To do that, you need to pass a command buffer that meets requirements as described in
831VmaDefragmentationInfo2::commandBuffer. The way it works is:
832
833- It creates temporary buffers and binds them to entire memory blocks when necessary.
834- It issues `vkCmdCopyBuffer()` to passed command buffer.
835
836Example:
837
838\code
839// Given following variables already initialized:
840VkDevice device;
841VmaAllocator allocator;
842VkCommandBuffer commandBuffer;
843std::vector<VkBuffer> buffers;
844std::vector<VmaAllocation> allocations;
845
846
847const uint32_t allocCount = (uint32_t)allocations.size();
848std::vector<VkBool32> allocationsChanged(allocCount);
849
850VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
851vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
852
853VmaDefragmentationInfo2 defragInfo = {};
854defragInfo.allocationCount = allocCount;
855defragInfo.pAllocations = allocations.data();
856defragInfo.pAllocationsChanged = allocationsChanged.data();
857defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
858defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
859defragInfo.commandBuffer = commandBuffer;
860
861VmaDefragmentationContext defragCtx;
862vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
863
864vkEndCommandBuffer(commandBuffer);
865
866// Submit commandBuffer.
867// Wait for a fence that ensures commandBuffer execution finished.
868
869vmaDefragmentationEnd(allocator, defragCtx);
870
871for(uint32_t i = 0; i < allocCount; ++i)
872{
873 if(allocationsChanged[i])
874 {
875 // Destroy buffer that is immutably bound to memory region which is no longer valid.
876 vkDestroyBuffer(device, buffers[i], nullptr);
877
878 // Create new buffer with same parameters.
879 VkBufferCreateInfo bufferInfo = ...;
880 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
881
882 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
883
884 // Bind new buffer to new memory region. Data contained in it is already moved.
885 VmaAllocationInfo allocInfo;
886 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
887 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
888 }
889}
890\endcode
891
892You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
893The library automatically chooses best method to defragment each memory pool.
894
895You may try not to block your entire program to wait until defragmentation finishes,
896but do it in the background, as long as you carefully fullfill requirements described
897in function vmaDefragmentationBegin().
898
899\section defragmentation_additional_notes Additional notes
900
901While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
902See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
903
904If you defragment allocations bound to images, these images should be created with
905`VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
906parameters and pointing to data copied to another memory region will interpret
907its contents consistently. Otherwise you may experience corrupted data on some
908implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
909
910If you defragment allocations bound to images, new images to be bound to new
911memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
912and then transitioned to their original layout from before defragmentation using
913an image memory barrier.
914
915Please don't expect memory to be fully compacted after defragmentation.
916Algorithms inside are based on some heuristics that try to maximize number of Vulkan
917memory blocks to make totally empty to release them, as well as to maximimze continuous
918empty space inside remaining blocks, while minimizing the number and size of allocations that
919need to be moved. Some fragmentation may still remain - this is normal.
920
921\section defragmentation_custom_algorithm Writing custom defragmentation algorithm
922
923If you want to implement your own, custom defragmentation algorithm,
924there is infrastructure prepared for that,
925but it is not exposed through the library API - you need to hack its source code.
926Here are steps needed to do this:
927
928-# Main thing you need to do is to define your own class derived from base abstract
929 class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
930 See definition and comments of this class for details.
931-# Your code needs to interact with device memory block metadata.
932 If you need more access to its data than it's provided by its public interface,
933 declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
934-# If you want to create a flag that would enable your algorithm or pass some additional
935 flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
936 VmaDefragmentationInfo2::flags.
937-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
938 of your new class whenever needed.
939
940
941\page lost_allocations Lost allocations
942
943If your game oversubscribes video memory, if may work OK in previous-generation
944graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
945paged to system RAM. In Vulkan you can't do it because when you run out of
946memory, an allocation just fails. If you have more data (e.g. textures) that can
947fit into VRAM and you don't need it all at once, you may want to upload them to
948GPU on demand and "push out" ones that are not used for a long time to make room
949for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
950cache. Vulkan Memory Allocator can help you with that by supporting a concept of
951"lost allocations".
952
953To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
954flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
955such allocation in every new frame, you need to query it if it's not lost.
956To check it, call vmaTouchAllocation().
957If the allocation is lost, you should not use it or buffer/image bound to it.
958You mustn't forget to destroy this allocation and this buffer/image.
959vmaGetAllocationInfo() can also be used for checking status of the allocation.
960Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
961
962To create an allocation that can make some other allocations lost to make room
963for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
964usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
965#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
966
967Warning! Current implementation uses quite naive, brute force algorithm,
968which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
969flag quite slow. A new, more optimal algorithm and data structure to speed this
970up is planned for the future.
971
972<b>Q: When interleaving creation of new allocations with usage of existing ones,
973how do you make sure that an allocation won't become lost while it's used in the
974current frame?</b>
975
976It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
977status/parameters and checks whether it's not lost, but when it's not, it also
978atomically marks it as used in the current frame, which makes it impossible to
979become lost in that frame. It uses lockless algorithm, so it works fast and
980doesn't involve locking any internal mutex.
981
982<b>Q: What if my allocation may still be in use by the GPU when it's rendering a
983previous frame while I already submit new frame on the CPU?</b>
984
985You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
986become lost for a number of additional frames back from the current one by
987specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
988memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
989
990<b>Q: How do you inform the library when new frame starts?</b>
991
992You need to call function vmaSetCurrentFrameIndex().
993
994Example code:
995
996\code
997struct MyBuffer
998{
999 VkBuffer m_Buf = nullptr;
1000 VmaAllocation m_Alloc = nullptr;
1001
1002 // Called when the buffer is really needed in the current frame.
1003 void EnsureBuffer();
1004};
1005
1006void MyBuffer::EnsureBuffer()
1007{
1008 // Buffer has been created.
1009 if(m_Buf != VK_NULL_HANDLE)
1010 {
1011 // Check if its allocation is not lost + mark it as used in current frame.
1012 if(vmaTouchAllocation(allocator, m_Alloc))
1013 {
1014 // It's all OK - safe to use m_Buf.
1015 return;
1016 }
1017 }
1018
1019 // Buffer not yet exists or lost - destroy and recreate it.
1020
1021 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
1022
1023 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1024 bufCreateInfo.size = 1024;
1025 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
1026
1027 VmaAllocationCreateInfo allocCreateInfo = {};
1028 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1029 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
1030 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
1031
1032 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
1033}
1034\endcode
1035
1036When using lost allocations, you may see some Vulkan validation layer warnings
1037about overlapping regions of memory bound to different kinds of buffers and
1038images. This is still valid as long as you implement proper handling of lost
1039allocations (like in the example above) and don't use them.
1040
1041You can create an allocation that is already in lost state from the beginning using function
1042vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
1043
1044You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
1045in a specified custom pool to lost state.
1046Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
1047cannot become lost.
1048
1049<b>Q: Can I touch allocation that cannot become lost?</b>
1050
1051Yes, although it has no visible effect.
1052Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
1053also for allocations that cannot become lost, but the only way to observe it is to dump
1054internal allocator state using vmaBuildStatsString().
1055You can use this feature for debugging purposes to explicitly mark allocations that you use
1056in current frame and then analyze JSON dump to see for how long each allocation stays unused.
1057
1058
1059\page statistics Statistics
1060
1061This library contains functions that return information about its internal state,
1062especially the amount of memory allocated from Vulkan.
1063Please keep in mind that these functions need to traverse all internal data structures
1064to gather these information, so they may be quite time-consuming.
1065Don't call them too often.
1066
1067\section statistics_numeric_statistics Numeric statistics
1068
1069You can query for overall statistics of the allocator using function vmaCalculateStats().
1070Information are returned using structure #VmaStats.
1071It contains #VmaStatInfo - number of allocated blocks, number of allocations
1072(occupied ranges in these blocks), number of unused (free) ranges in these blocks,
1073number of bytes used and unused (but still allocated from Vulkan) and other information.
1074They are summed across memory heaps, memory types and total for whole allocator.
1075
1076You can query for statistics of a custom pool using function vmaGetPoolStats().
1077Information are returned using structure #VmaPoolStats.
1078
1079You can query for information about specific allocation using function vmaGetAllocationInfo().
1080It fill structure #VmaAllocationInfo.
1081
1082\section statistics_json_dump JSON dump
1083
1084You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
1085The result is guaranteed to be correct JSON.
1086It uses ANSI encoding.
1087Any strings provided by user (see [Allocation names](@ref allocation_names))
1088are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
1089this JSON string can be treated as using this encoding.
1090It must be freed using function vmaFreeStatsString().
1091
1092The format of this JSON string is not part of official documentation of the library,
1093but it will not change in backward-incompatible way without increasing library major version number
1094and appropriate mention in changelog.
1095
1096The JSON string contains all the data that can be obtained using vmaCalculateStats().
1097It can also contain detailed map of allocated memory blocks and their regions -
1098free and occupied by allocations.
1099This allows e.g. to visualize the memory or assess fragmentation.
1100
1101
1102\page allocation_annotation Allocation names and user data
1103
1104\section allocation_user_data Allocation user data
1105
1106You can annotate allocations with your own information, e.g. for debugging purposes.
1107To do that, fill VmaAllocationCreateInfo::pUserData field when creating
1108an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
1109some handle, index, key, ordinal number or any other value that would associate
1110the allocation with your custom metadata.
1111
1112\code
1113VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
1114// Fill bufferInfo...
1115
1116MyBufferMetadata* pMetadata = CreateBufferMetadata();
1117
1118VmaAllocationCreateInfo allocCreateInfo = {};
1119allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1120allocCreateInfo.pUserData = pMetadata;
1121
1122VkBuffer buffer;
1123VmaAllocation allocation;
1124vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
1125\endcode
1126
1127The pointer may be later retrieved as VmaAllocationInfo::pUserData:
1128
1129\code
1130VmaAllocationInfo allocInfo;
1131vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1132MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
1133\endcode
1134
1135It can also be changed using function vmaSetAllocationUserData().
1136
1137Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
1138vmaBuildStatsString(), in hexadecimal form.
1139
1140\section allocation_names Allocation names
1141
1142There is alternative mode available where `pUserData` pointer is used to point to
1143a null-terminated string, giving a name to the allocation. To use this mode,
1144set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
1145Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
1146vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
1147The library creates internal copy of the string, so the pointer you pass doesn't need
1148to be valid for whole lifetime of the allocation. You can free it after the call.
1149
1150\code
1151VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
1152// Fill imageInfo...
1153
1154std::string imageName = "Texture: ";
1155imageName += fileName;
1156
1157VmaAllocationCreateInfo allocCreateInfo = {};
1158allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
1159allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
1160allocCreateInfo.pUserData = imageName.c_str();
1161
1162VkImage image;
1163VmaAllocation allocation;
1164vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
1165\endcode
1166
1167The value of `pUserData` pointer of the allocation will be different than the one
1168you passed when setting allocation's name - pointing to a buffer managed
1169internally that holds copy of the string.
1170
1171\code
1172VmaAllocationInfo allocInfo;
1173vmaGetAllocationInfo(allocator, allocation, &allocInfo);
1174const char* imageName = (const char*)allocInfo.pUserData;
1175printf("Image name: %s\n", imageName);
1176\endcode
1177
1178That string is also printed in JSON report created by vmaBuildStatsString().
1179
1180
1181\page debugging_memory_usage Debugging incorrect memory usage
1182
1183If you suspect a bug with memory usage, like usage of uninitialized memory or
1184memory being overwritten out of bounds of an allocation,
1185you can use debug features of this library to verify this.
1186
1187\section debugging_memory_usage_initialization Memory initialization
1188
1189If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
1190you can enable automatic memory initialization to verify this.
1191To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
1192
1193\code
1194#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
1195#include "vk_mem_alloc.h"
1196\endcode
1197
1198It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
1199Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
1200Memory is automatically mapped and unmapped if necessary.
1201
1202If you find these values while debugging your program, good chances are that you incorrectly
1203read Vulkan memory that is allocated but not initialized, or already freed, respectively.
1204
1205Memory initialization works only with memory types that are `HOST_VISIBLE`.
1206It works also with dedicated allocations.
1207It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1208as they cannot be mapped.
1209
1210\section debugging_memory_usage_margins Margins
1211
1212By default, allocations are laid out in memory blocks next to each other if possible
1213(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
1214
1215![Allocations without margin](../gfx/Margins_1.png)
1216
1217Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
1218number of bytes as a margin before and after every allocation.
1219
1220\code
1221#define VMA_DEBUG_MARGIN 16
1222#include "vk_mem_alloc.h"
1223\endcode
1224
1225![Allocations with margin](../gfx/Margins_2.png)
1226
1227If your bug goes away after enabling margins, it means it may be caused by memory
1228being overwritten outside of allocation boundaries. It is not 100% certain though.
1229Change in application behavior may also be caused by different order and distribution
1230of allocations across memory blocks after margins are applied.
1231
1232The margin is applied also before first and after last allocation in a block.
1233It may occur only once between two adjacent allocations.
1234
1235Margins work with all types of memory.
1236
1237Margin is applied only to allocations made out of memory blocks and not to dedicated
1238allocations, which have their own memory block of specific size.
1239It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
1240or those automatically decided to put into dedicated allocations, e.g. due to its
1241large size or recommended by VK_KHR_dedicated_allocation extension.
1242Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
1243
1244Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
1245
1246Note that enabling margins increases memory usage and fragmentation.
1247
1248\section debugging_memory_usage_corruption_detection Corruption detection
1249
1250You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
1251of contents of the margins.
1252
1253\code
1254#define VMA_DEBUG_MARGIN 16
1255#define VMA_DEBUG_DETECT_CORRUPTION 1
1256#include "vk_mem_alloc.h"
1257\endcode
1258
1259When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
1260(it must be multiply of 4) before and after every allocation is filled with a magic number.
1261This idea is also know as "canary".
1262Memory is automatically mapped and unmapped if necessary.
1263
1264This number is validated automatically when the allocation is destroyed.
1265If it's not equal to the expected value, `VMA_ASSERT()` is executed.
1266It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
1267which indicates a serious bug.
1268
1269You can also explicitly request checking margins of all allocations in all memory blocks
1270that belong to specified memory types by using function vmaCheckCorruption(),
1271or in memory blocks that belong to specified custom pool, by using function
1272vmaCheckPoolCorruption().
1273
1274Margin validation (corruption detection) works only for memory types that are
1275`HOST_VISIBLE` and `HOST_COHERENT`.
1276
1277
1278\page record_and_replay Record and replay
1279
1280\section record_and_replay_introduction Introduction
1281
1282While using the library, sequence of calls to its functions together with their
1283parameters can be recorded to a file and later replayed using standalone player
1284application. It can be useful to:
1285
1286- Test correctness - check if same sequence of calls will not cause crash or
1287 failures on a target platform.
1288- Gather statistics - see number of allocations, peak memory usage, number of
1289 calls etc.
1290- Benchmark performance - see how much time it takes to replay the whole
1291 sequence.
1292
1293\section record_and_replay_usage Usage
1294
1295<b>To record sequence of calls to a file:</b> Fill in
1296VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
1297object. File is opened and written during whole lifetime of the allocator.
1298
1299<b>To replay file:</b> Use VmaReplay - standalone command-line program.
1300Precompiled binary can be found in "bin" directory.
1301Its source can be found in "src/VmaReplay" directory.
1302Its project is generated by Premake.
1303Command line syntax is printed when the program is launched without parameters.
1304Basic usage:
1305
1306 VmaReplay.exe MyRecording.csv
1307
1308<b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
1309It's a human-readable, text file in CSV format (Comma Separated Values).
1310
1311\section record_and_replay_additional_considerations Additional considerations
1312
1313- Replaying file that was recorded on a different GPU (with different parameters
1314 like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
1315 set of memory heaps and types) may give different performance and memory usage
1316 results, as well as issue some warnings and errors.
1317- Current implementation of recording in VMA, as well as VmaReplay application, is
1318 coded and tested only on Windows. Inclusion of recording code is driven by
1319 `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
1320 add. Contributions are welcomed.
1321- Currently calls to vmaDefragment() function are not recorded.
1322
1323
1324\page usage_patterns Recommended usage patterns
1325
1326See also slides from talk:
1327[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)
1328
1329
1330\section usage_patterns_simple Simple patterns
1331
1332\subsection usage_patterns_simple_render_targets Render targets
1333
1334<b>When:</b>
1335Any resources that you frequently write and read on GPU,
1336e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
1337images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
1338
1339<b>What to do:</b>
1340Create them in video memory that is fastest to access from GPU using
1341#VMA_MEMORY_USAGE_GPU_ONLY.
1342
1343Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
1344and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
1345especially if they are large or if you plan to destroy and recreate them e.g. when
1346display resolution changes.
1347Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
1348
1349\subsection usage_patterns_simple_immutable_resources Immutable resources
1350
1351<b>When:</b>
1352Any resources that you fill on CPU only once (aka "immutable") or infrequently
1353and then read frequently on GPU,
1354e.g. textures, vertex and index buffers, constant buffers that don't change often.
1355
1356<b>What to do:</b>
1357Create them in video memory that is fastest to access from GPU using
1358#VMA_MEMORY_USAGE_GPU_ONLY.
1359
1360To initialize content of such resource, create a CPU-side (aka "staging") copy of it
1361in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
1362and submit a transfer from it to the GPU resource.
1363You can keep the staging copy if you need it for another upload transfer in the future.
1364If you don't, you can destroy it or reuse this buffer for uploading different resource
1365after the transfer finishes.
1366
1367Prefer to create just buffers in system memory rather than images, even for uploading textures.
1368Use `vkCmdCopyBufferToImage()`.
1369Dont use images with `VK_IMAGE_TILING_LINEAR`.
1370
1371\subsection usage_patterns_dynamic_resources Dynamic resources
1372
1373<b>When:</b>
1374Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
1375written on CPU, read on GPU.
1376
1377<b>What to do:</b>
1378Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
1379You can map it and write to it directly on CPU, as well as read from it on GPU.
1380
1381This is a more complex situation. Different solutions are possible,
1382and the best one depends on specific GPU type, but you can use this simple approach for the start.
1383Prefer to write to such resource sequentially (e.g. using `memcpy`).
1384Don't perform random access or any reads from it on CPU, as it may be very slow.
1385
1386\subsection usage_patterns_readback Readback
1387
1388<b>When:</b>
1389Resources that contain data written by GPU that you want to read back on CPU,
1390e.g. results of some computations.
1391
1392<b>What to do:</b>
1393Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
1394You can write to them directly on GPU, as well as map and read them on CPU.
1395
1396\section usage_patterns_advanced Advanced patterns
1397
1398\subsection usage_patterns_integrated_graphics Detecting integrated graphics
1399
1400You can support integrated graphics (like Intel HD Graphics, AMD APU) better
1401by detecting it in Vulkan.
1402To do it, call `vkGetPhysicalDeviceProperties()`, inspect
1403`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
1404When you find it, you can assume that memory is unified and all memory types are comparably fast
1405to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
1406
1407You can then sum up sizes of all available memory heaps and treat them as useful for
1408your GPU resources, instead of only `DEVICE_LOCAL` ones.
1409You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
1410directly instead of submitting explicit transfer (see below).
1411
1412\subsection usage_patterns_direct_vs_transfer Direct access versus transfer
1413
1414For resources that you frequently write on CPU and read on GPU, many solutions are possible:
1415
1416-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1417 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
1418-# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
1419 read it directly on GPU.
1420-# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
1421 read it directly on GPU.
1422
1423Which solution is the most efficient depends on your resource and especially on the GPU.
1424It is best to measure it and then make the decision.
1425Some general recommendations:
1426
1427- On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
1428 related to using a second copy and making transfer.
1429- For small resources (e.g. constant buffers) use (2).
1430 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
1431 Even if the resource ends up in system memory, its data may be cached on GPU after first
1432 fetch over PCIe bus.
1433- For larger resources (e.g. textures), decide between (1) and (2).
1434 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
1435 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
1436
1437Similarly, for resources that you frequently write on GPU and read on CPU, multiple
1438solutions are possible:
1439
1440-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
1441 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
1442-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
1443 map it and read it on CPU.
1444
1445You should take some measurements to decide which option is faster in case of your specific
1446resource.
1447
1448If you don't want to specialize your code for specific types of GPUs, you can still make
1449an simple optimization for cases when your resource ends up in mappable memory to use it
1450directly in this case instead of creating CPU-side staging copy.
1451For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
1452
1453
1454\page configuration Configuration
1455
1456Please check "CONFIGURATION SECTION" in the code to find macros that you can define
1457before each include of this file or change directly in this file to provide
1458your own implementation of basic facilities like assert, `min()` and `max()` functions,
1459mutex, atomic etc.
1460The library uses its own implementation of containers by default, but you can switch to using
1461STL containers instead.
1462
1463\section config_Vulkan_functions Pointers to Vulkan functions
1464
1465The library uses Vulkan functions straight from the `vulkan.h` header by default.
1466If you want to provide your own pointers to these functions, e.g. fetched using
1467`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
1468
1469-# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
1470-# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
1471
1472\section custom_memory_allocator Custom host memory allocator
1473
1474If you use custom allocator for CPU memory rather than default operator `new`
1475and `delete` from C++, you can make this library using your allocator as well
1476by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
1477functions will be passed to Vulkan, as well as used by the library itself to
1478make any CPU-side allocations.
1479
1480\section allocation_callbacks Device memory allocation callbacks
1481
1482The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
1483You can setup callbacks to be informed about these calls, e.g. for the purpose
1484of gathering some statistics. To do it, fill optional member
1485VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1486
1487\section heap_memory_limit Device heap memory limit
1488
1489If you want to test how your program behaves with limited amount of Vulkan device
1490memory available without switching your graphics card to one that really has
1491smaller VRAM, you can use a feature of this library intended for this purpose.
1492To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
1493
1494
1495
1496\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
1497
1498VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
1499performance on some GPUs. It augments Vulkan API with possibility to query
1500driver whether it prefers particular buffer or image to have its own, dedicated
1501allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
1502to do some internal optimizations.
1503
1504The extension is supported by this library. It will be used automatically when
1505enabled. To enable it:
1506
15071 . When creating Vulkan device, check if following 2 device extensions are
1508supported (call `vkEnumerateDeviceExtensionProperties()`).
1509If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
1510
1511- VK_KHR_get_memory_requirements2
1512- VK_KHR_dedicated_allocation
1513
1514If you enabled these extensions:
1515
15162 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1517your #VmaAllocator`to inform the library that you enabled required extensions
1518and you want the library to use them.
1519
1520\code
1521allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1522
1523vmaCreateAllocator(&allocatorInfo, &allocator);
1524\endcode
1525
1526That's all. The extension will be automatically used whenever you create a
1527buffer using vmaCreateBuffer() or image using vmaCreateImage().
1528
1529When using the extension together with Vulkan Validation Layer, you will receive
1530warnings like this:
1531
1532 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1533
1534It is OK, you should just ignore it. It happens because you use function
1535`vkGetBufferMemoryRequirements2KHR()` instead of standard
1536`vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1537unaware of it.
1538
1539To learn more about this extension, see:
1540
1541- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1542- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1543
1544
1545
1546\page general_considerations General considerations
1547
1548\section general_considerations_thread_safety Thread safety
1549
1550- The library has no global state, so separate #VmaAllocator objects can be used
1551 independently.
1552 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1553- By default, all calls to functions that take #VmaAllocator as first parameter
1554 are safe to call from multiple threads simultaneously because they are
1555 synchronized internally when needed.
1556- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1557 flag, calls to functions that take such #VmaAllocator object must be
1558 synchronized externally.
1559- Access to a #VmaAllocation object must be externally synchronized. For example,
1560 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1561 threads at the same time if you pass the same #VmaAllocation object to these
1562 functions.
1563
1564\section general_considerations_validation_layer_warnings Validation layer warnings
1565
1566When using this library, you can meet following types of warnings issued by
1567Vulkan validation layer. They don't necessarily indicate a bug, so you may need
1568to just ignore them.
1569
1570- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
1571 - It happens when VK_KHR_dedicated_allocation extension is enabled.
1572 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
1573- *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.*
1574 - It happens when you map a buffer or image, because the library maps entire
1575 `VkDeviceMemory` block, where different types of images and buffers may end
1576 up together, especially on GPUs with unified memory like Intel.
1577- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
1578 - It happens when you use lost allocations, and a new image or buffer is
1579 created in place of an existing object that bacame lost.
1580 - It may happen also when you use [defragmentation](@ref defragmentation).
1581
1582\section general_considerations_allocation_algorithm Allocation algorithm
1583
1584The library uses following algorithm for allocation, in order:
1585
1586-# Try to find free range of memory in existing blocks.
1587-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1588-# If failed, try to create such block with size/2, size/4, size/8.
1589-# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1590 specified, try to find space in existing blocks, possilby making some other
1591 allocations lost.
1592-# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1593 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1594-# If failed, choose other memory type that meets the requirements specified in
1595 VmaAllocationCreateInfo and go to point 1.
1596-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1597
1598\section general_considerations_features_not_supported Features not supported
1599
1600Features deliberately excluded from the scope of this library:
1601
1602- Data transfer. Uploading (straming) and downloading data of buffers and images
1603 between CPU and GPU memory and related synchronization is responsibility of the user.
1604- Allocations for imported/exported external memory. They tend to require
1605 explicit memory type index and dedicated allocation anyway, so they don't
1606 interact with main features of this library. Such special purpose allocations
1607 should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
1608- Recreation of buffers and images. Although the library has functions for
1609 buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
1610 recreate these objects yourself after defragmentation. That's because the big
1611 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
1612 #VmaAllocation object.
1613- Handling CPU memory allocation failures. When dynamically creating small C++
1614 objects in CPU memory (not Vulkan memory), allocation failures are not checked
1615 and handled gracefully, because that would complicate code significantly and
1616 is usually not needed in desktop PC applications anyway.
1617- Code free of any compiler warnings. Maintaining the library to compile and
1618 work correctly on so many different platforms is hard enough. Being free of
1619 any warnings, on any version of any compiler, is simply not feasible.
1620- This is a C++ library with C interface.
1621 Bindings or ports to any other programming languages are welcomed as external projects and
1622 are not going to be included into this repository.
1623
1624*/
1625
1626/*
1627Define this macro to 0/1 to disable/enable support for recording functionality,
1628available through VmaAllocatorCreateInfo::pRecordSettings.
1629*/
1630#ifndef VMA_RECORDING_ENABLED
1631 #ifdef _WIN32
1632 #define VMA_RECORDING_ENABLED 1
1633 #else
1634 #define VMA_RECORDING_ENABLED 0
1635 #endif
1636#endif
1637
1638#ifndef NOMINMAX
1639 #define NOMINMAX // For windows.h
1640#endif
1641
1642#ifndef VULKAN_H_
1643 #include <vulkan/vulkan.h>
1644#endif
1645
1646#if VMA_RECORDING_ENABLED
1647 #include <windows.h>
1648#endif
1649
1650#if !defined(VMA_DEDICATED_ALLOCATION)
1651 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
1652 #define VMA_DEDICATED_ALLOCATION 1
1653 #else
1654 #define VMA_DEDICATED_ALLOCATION 0
1655 #endif
1656#endif
1657
1658/** \struct VmaAllocator
1659\brief Represents main object of this library initialized.
1660
1661Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1662Call function vmaDestroyAllocator() to destroy it.
1663
1664It is recommended to create just one object of this type per `VkDevice` object,
1665right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1666*/
1667VK_DEFINE_HANDLE(VmaAllocator)
1668
1669/// Callback function called after successful vkAllocateMemory.
1670typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1671 VmaAllocator allocator,
1672 uint32_t memoryType,
1673 VkDeviceMemory memory,
1674 VkDeviceSize size);
1675/// Callback function called before vkFreeMemory.
1676typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1677 VmaAllocator allocator,
1678 uint32_t memoryType,
1679 VkDeviceMemory memory,
1680 VkDeviceSize size);
1681
1682/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1683
1684Provided for informative purpose, e.g. to gather statistics about number of
1685allocations or total amount of memory allocated in Vulkan.
1686
1687Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1688*/
1689typedef struct VmaDeviceMemoryCallbacks {
1690 /// Optional, can be null.
1691 PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1692 /// Optional, can be null.
1693 PFN_vmaFreeDeviceMemoryFunction pfnFree;
1694} VmaDeviceMemoryCallbacks;
1695
1696/// Flags for created #VmaAllocator.
1697typedef enum VmaAllocatorCreateFlagBits {
1698 /** \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.
1699
1700 Using this flag may increase performance because internal mutexes are not used.
1701 */
1702 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1703 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1704
1705 Using this extenion will automatically allocate dedicated blocks of memory for
1706 some buffers and images instead of suballocating place for them out of bigger
1707 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1708 flag) when it is recommended by the driver. It may improve performance on some
1709 GPUs.
1710
1711 You may set this flag only if you found out that following device extensions are
1712 supported, you enabled them while creating Vulkan device passed as
1713 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1714 library:
1715
1716 - VK_KHR_get_memory_requirements2
1717 - VK_KHR_dedicated_allocation
1718
1719When this flag is set, you can experience following warnings reported by Vulkan
1720validation layer. You can ignore them.
1721
1722> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1723 */
1724 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1725
1726 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1727} VmaAllocatorCreateFlagBits;
1728typedef VkFlags VmaAllocatorCreateFlags;
1729
1730/** \brief Pointers to some Vulkan functions - a subset used by the library.
1731
1732Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1733*/
1734typedef struct VmaVulkanFunctions {
1735 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1736 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1737 PFN_vkAllocateMemory vkAllocateMemory;
1738 PFN_vkFreeMemory vkFreeMemory;
1739 PFN_vkMapMemory vkMapMemory;
1740 PFN_vkUnmapMemory vkUnmapMemory;
1741 PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
1742 PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
1743 PFN_vkBindBufferMemory vkBindBufferMemory;
1744 PFN_vkBindImageMemory vkBindImageMemory;
1745 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1746 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1747 PFN_vkCreateBuffer vkCreateBuffer;
1748 PFN_vkDestroyBuffer vkDestroyBuffer;
1749 PFN_vkCreateImage vkCreateImage;
1750 PFN_vkDestroyImage vkDestroyImage;
1751 PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
1752#if VMA_DEDICATED_ALLOCATION
1753 PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1754 PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1755#endif
1756} VmaVulkanFunctions;
1757
1758/// Flags to be used in VmaRecordSettings::flags.
1759typedef enum VmaRecordFlagBits {
1760 /** \brief Enables flush after recording every function call.
1761
1762 Enable it if you expect your application to crash, which may leave recording file truncated.
1763 It may degrade performance though.
1764 */
1765 VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
1766
1767 VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1768} VmaRecordFlagBits;
1769typedef VkFlags VmaRecordFlags;
1770
1771/// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
1772typedef struct VmaRecordSettings
1773{
1774 /// Flags for recording. Use #VmaRecordFlagBits enum.
1775 VmaRecordFlags flags;
1776 /** \brief Path to the file that should be written by the recording.
1777
1778 Suggested extension: "csv".
1779 If the file already exists, it will be overwritten.
1780 It will be opened for the whole time #VmaAllocator object is alive.
1781 If opening this file fails, creation of the whole allocator object fails.
1782 */
1783 const char* pFilePath;
1784} VmaRecordSettings;
1785
1786/// Description of a Allocator to be created.
1787typedef struct VmaAllocatorCreateInfo
1788{
1789 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1790 VmaAllocatorCreateFlags flags;
1791 /// Vulkan physical device.
1792 /** It must be valid throughout whole lifetime of created allocator. */
1793 VkPhysicalDevice physicalDevice;
1794 /// Vulkan device.
1795 /** It must be valid throughout whole lifetime of created allocator. */
1796 VkDevice device;
1797 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1798 /** Set to 0 to use default, which is currently 256 MiB. */
1799 VkDeviceSize preferredLargeHeapBlockSize;
1800 /// Custom CPU memory allocation callbacks. Optional.
1801 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1802 const VkAllocationCallbacks* pAllocationCallbacks;
1803 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1804 /** Optional, can be null. */
1805 const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1806 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1807
1808 This value is used only when you make allocations with
1809 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1810 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1811
1812 For example, if you double-buffer your command buffers, so resources used for
1813 rendering in previous frame may still be in use by the GPU at the moment you
1814 allocate resources needed for the current frame, set this value to 1.
1815
1816 If you want to allow any allocations other than used in the current frame to
1817 become lost, set this value to 0.
1818 */
1819 uint32_t frameInUseCount;
1820 /** \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.
1821
1822 If not NULL, it must be a pointer to an array of
1823 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1824 maximum number of bytes that can be allocated out of particular Vulkan memory
1825 heap.
1826
1827 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1828 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1829
1830 If there is a limit defined for a heap:
1831
1832 - If user tries to allocate more memory from that heap using this allocator,
1833 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1834 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1835 value of this limit will be reported instead when using vmaGetMemoryProperties().
1836
1837 Warning! Using this feature may not be equivalent to installing a GPU with
1838 smaller amount of memory, because graphics driver doesn't necessary fail new
1839 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1840 exceeded. It may return success and just silently migrate some device memory
1841 blocks to system RAM. This driver behavior can also be controlled using
1842 VK_AMD_memory_overallocation_behavior extension.
1843 */
1844 const VkDeviceSize* pHeapSizeLimit;
1845 /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1846
1847 If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1848 you can pass null as this member, because the library will fetch pointers to
1849 Vulkan functions internally in a static way, like:
1850
1851 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1852
1853 Fill this member if you want to provide your own pointers to Vulkan functions,
1854 e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1855 */
1856 const VmaVulkanFunctions* pVulkanFunctions;
1857 /** \brief Parameters for recording of VMA calls. Can be null.
1858
1859 If not null, it enables recording of calls to VMA functions to a file.
1860 If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
1861 creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
1862 */
1863 const VmaRecordSettings* pRecordSettings;
1864} VmaAllocatorCreateInfo;
1865
1866/// Creates Allocator object.
1867VkResult vmaCreateAllocator(
1868 const VmaAllocatorCreateInfo* pCreateInfo,
1869 VmaAllocator* pAllocator);
1870
1871/// Destroys allocator object.
1872void vmaDestroyAllocator(
1873 VmaAllocator allocator);
1874
1875/**
1876PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1877You can access it here, without fetching it again on your own.
1878*/
1879void vmaGetPhysicalDeviceProperties(
1880 VmaAllocator allocator,
1881 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1882
1883/**
1884PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1885You can access it here, without fetching it again on your own.
1886*/
1887void vmaGetMemoryProperties(
1888 VmaAllocator allocator,
1889 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1890
1891/**
1892\brief Given Memory Type Index, returns Property Flags of this memory type.
1893
1894This is just a convenience function. Same information can be obtained using
1895vmaGetMemoryProperties().
1896*/
1897void vmaGetMemoryTypeProperties(
1898 VmaAllocator allocator,
1899 uint32_t memoryTypeIndex,
1900 VkMemoryPropertyFlags* pFlags);
1901
1902/** \brief Sets index of the current frame.
1903
1904This function must be used if you make allocations with
1905#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1906#VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1907when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1908become lost in the current frame.
1909*/
1910void vmaSetCurrentFrameIndex(
1911 VmaAllocator allocator,
1912 uint32_t frameIndex);
1913
1914/** \brief Calculated statistics of memory usage in entire allocator.
1915*/
1916typedef struct VmaStatInfo
1917{
1918 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1919 uint32_t blockCount;
1920 /// Number of #VmaAllocation allocation objects allocated.
1921 uint32_t allocationCount;
1922 /// Number of free ranges of memory between allocations.
1923 uint32_t unusedRangeCount;
1924 /// Total number of bytes occupied by all allocations.
1925 VkDeviceSize usedBytes;
1926 /// Total number of bytes occupied by unused ranges.
1927 VkDeviceSize unusedBytes;
1928 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1929 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1930} VmaStatInfo;
1931
1932/// General statistics from current state of Allocator.
1933typedef struct VmaStats
1934{
1935 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1936 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1937 VmaStatInfo total;
1938} VmaStats;
1939
1940/// Retrieves statistics from current state of the Allocator.
1941void vmaCalculateStats(
1942 VmaAllocator allocator,
1943 VmaStats* pStats);
1944
1945#define VMA_STATS_STRING_ENABLED 1
1946
1947#if VMA_STATS_STRING_ENABLED
1948
1949/// Builds and returns statistics as string in JSON format.
1950/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1951*/
1952void vmaBuildStatsString(
1953 VmaAllocator allocator,
1954 char** ppStatsString,
1955 VkBool32 detailedMap);
1956
1957void vmaFreeStatsString(
1958 VmaAllocator allocator,
1959 char* pStatsString);
1960
1961#endif // #if VMA_STATS_STRING_ENABLED
1962
1963/** \struct VmaPool
1964\brief Represents custom memory pool
1965
1966Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1967Call function vmaDestroyPool() to destroy it.
1968
1969For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1970*/
1971VK_DEFINE_HANDLE(VmaPool)
1972
1973typedef enum VmaMemoryUsage
1974{
1975 /** No intended memory usage specified.
1976 Use other members of VmaAllocationCreateInfo to specify your requirements.
1977 */
1978 VMA_MEMORY_USAGE_UNKNOWN = 0,
1979 /** Memory will be used on device only, so fast access from the device is preferred.
1980 It usually means device-local GPU (video) memory.
1981 No need to be mappable on host.
1982 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1983
1984 Usage:
1985
1986 - Resources written and read by device, e.g. images used as attachments.
1987 - Resources transferred from host once (immutable) or infrequently and read by
1988 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1989 (constant) buffers, and majority of other types of resources used on GPU.
1990
1991 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1992 In such case, you are free to map it.
1993 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1994 */
1995 VMA_MEMORY_USAGE_GPU_ONLY = 1,
1996 /** Memory will be mappable on host.
1997 It usually means CPU (system) memory.
1998 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1999 CPU access is typically uncached. Writes may be write-combined.
2000 Resources created in this pool may still be accessible to the device, but access to them can be slow.
2001 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
2002
2003 Usage: Staging copy of resources used as transfer source.
2004 */
2005 VMA_MEMORY_USAGE_CPU_ONLY = 2,
2006 /**
2007 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
2008 CPU access is typically uncached. Writes may be write-combined.
2009
2010 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
2011 */
2012 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
2013 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
2014 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
2015
2016 Usage:
2017
2018 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
2019 - 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.
2020 */
2021 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
2022 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
2023} VmaMemoryUsage;
2024
2025/// Flags to be passed as VmaAllocationCreateInfo::flags.
2026typedef enum VmaAllocationCreateFlagBits {
2027 /** \brief Set this flag if the allocation should have its own memory block.
2028
2029 Use it for special, big resources, like fullscreen images used as attachments.
2030
2031 This flag must also be used for host visible resources that you want to map
2032 simultaneously because otherwise they might end up as regions of the same
2033 `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
2034 simultaneously is illegal.
2035
2036 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
2037 */
2038 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
2039
2040 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
2041
2042 If new allocation cannot be placed in any of the existing blocks, allocation
2043 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
2044
2045 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
2046 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
2047
2048 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
2049 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
2050 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
2051
2052 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
2053
2054 Is it valid to use this flag for allocation made from memory type that is not
2055 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
2056 useful if you need an allocation that is efficient to use on GPU
2057 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
2058 support it (e.g. Intel GPU).
2059
2060 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
2061 */
2062 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
2063 /** Allocation created with this flag can become lost as a result of another
2064 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
2065 must check it before use.
2066
2067 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
2068 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
2069
2070 For details about supporting lost allocations, see Lost Allocations
2071 chapter of User Guide on Main Page.
2072
2073 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
2074 */
2075 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
2076 /** While creating allocation using this flag, other allocations that were
2077 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
2078
2079 For details about supporting lost allocations, see Lost Allocations
2080 chapter of User Guide on Main Page.
2081 */
2082 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
2083 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
2084 null-terminated string. Instead of copying pointer value, a local copy of the
2085 string is made and stored in allocation's `pUserData`. The string is automatically
2086 freed together with the allocation. It is also used in vmaBuildStatsString().
2087 */
2088 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
2089 /** Allocation will be created from upper stack in a double stack pool.
2090
2091 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
2092 */
2093 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
2094
2095 /** Allocation strategy that chooses smallest possible free range for the
2096 allocation.
2097 */
2098 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
2099 /** Allocation strategy that chooses biggest possible free range for the
2100 allocation.
2101 */
2102 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
2103 /** Allocation strategy that chooses first suitable free range for the
2104 allocation.
2105
2106 "First" doesn't necessarily means the one with smallest offset in memory,
2107 but rather the one that is easiest and fastest to find.
2108 */
2109 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
2110
2111 /** Allocation strategy that tries to minimize memory usage.
2112 */
2113 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
2114 /** Allocation strategy that tries to minimize allocation time.
2115 */
2116 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2117 /** Allocation strategy that tries to minimize memory fragmentation.
2118 */
2119 VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
2120
2121 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
2122 */
2123 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
2124 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
2125 VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
2126 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
2127
2128 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2129} VmaAllocationCreateFlagBits;
2130typedef VkFlags VmaAllocationCreateFlags;
2131
2132typedef struct VmaAllocationCreateInfo
2133{
2134 /// Use #VmaAllocationCreateFlagBits enum.
2135 VmaAllocationCreateFlags flags;
2136 /** \brief Intended usage of memory.
2137
2138 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
2139 If `pool` is not null, this member is ignored.
2140 */
2141 VmaMemoryUsage usage;
2142 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
2143
2144 Leave 0 if you specify memory requirements in other way. \n
2145 If `pool` is not null, this member is ignored.*/
2146 VkMemoryPropertyFlags requiredFlags;
2147 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
2148
2149 Set to 0 if no additional flags are prefered. \n
2150 If `pool` is not null, this member is ignored. */
2151 VkMemoryPropertyFlags preferredFlags;
2152 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
2153
2154 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
2155 it meets other requirements specified by this structure, with no further
2156 restrictions on memory type index. \n
2157 If `pool` is not null, this member is ignored.
2158 */
2159 uint32_t memoryTypeBits;
2160 /** \brief Pool that this allocation should be created in.
2161
2162 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
2163 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
2164 */
2165 VmaPool pool;
2166 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
2167
2168 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
2169 null or pointer to a null-terminated string. The string will be then copied to
2170 internal buffer, so it doesn't need to be valid after allocation call.
2171 */
2172 void* pUserData;
2173} VmaAllocationCreateInfo;
2174
2175/**
2176\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
2177
2178This algorithm tries to find a memory type that:
2179
2180- Is allowed by memoryTypeBits.
2181- Contains all the flags from pAllocationCreateInfo->requiredFlags.
2182- Matches intended usage.
2183- Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
2184
2185\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
2186from this function or any other allocating function probably means that your
2187device doesn't support any memory type with requested features for the specific
2188type of resource you want to use it for. Please check parameters of your
2189resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
2190*/
2191VkResult vmaFindMemoryTypeIndex(
2192 VmaAllocator allocator,
2193 uint32_t memoryTypeBits,
2194 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2195 uint32_t* pMemoryTypeIndex);
2196
2197/**
2198\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
2199
2200It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2201It internally creates a temporary, dummy buffer that never has memory bound.
2202It is just a convenience function, equivalent to calling:
2203
2204- `vkCreateBuffer`
2205- `vkGetBufferMemoryRequirements`
2206- `vmaFindMemoryTypeIndex`
2207- `vkDestroyBuffer`
2208*/
2209VkResult vmaFindMemoryTypeIndexForBufferInfo(
2210 VmaAllocator allocator,
2211 const VkBufferCreateInfo* pBufferCreateInfo,
2212 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2213 uint32_t* pMemoryTypeIndex);
2214
2215/**
2216\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
2217
2218It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
2219It internally creates a temporary, dummy image that never has memory bound.
2220It is just a convenience function, equivalent to calling:
2221
2222- `vkCreateImage`
2223- `vkGetImageMemoryRequirements`
2224- `vmaFindMemoryTypeIndex`
2225- `vkDestroyImage`
2226*/
2227VkResult vmaFindMemoryTypeIndexForImageInfo(
2228 VmaAllocator allocator,
2229 const VkImageCreateInfo* pImageCreateInfo,
2230 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2231 uint32_t* pMemoryTypeIndex);
2232
2233/// Flags to be passed as VmaPoolCreateInfo::flags.
2234typedef enum VmaPoolCreateFlagBits {
2235 /** \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.
2236
2237 This is an optional optimization flag.
2238
2239 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
2240 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
2241 knows exact type of your allocations so it can handle Buffer-Image Granularity
2242 in the optimal way.
2243
2244 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
2245 exact type of such allocations is not known, so allocator must be conservative
2246 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
2247 (wasted memory). In that case, if you can make sure you always allocate only
2248 buffers and linear images or only optimal images out of this pool, use this flag
2249 to make allocator disregard Buffer-Image Granularity and so make allocations
2250 faster and more optimal.
2251 */
2252 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
2253
2254 /** \brief Enables alternative, linear allocation algorithm in this pool.
2255
2256 Specify this flag to enable linear allocation algorithm, which always creates
2257 new allocations after last one and doesn't reuse space from allocations freed in
2258 between. It trades memory consumption for simplified algorithm and data
2259 structure, which has better performance and uses less memory for metadata.
2260
2261 By using this flag, you can achieve behavior of free-at-once, stack,
2262 ring buffer, and double stack. For details, see documentation chapter
2263 \ref linear_algorithm.
2264
2265 When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
2266
2267 For more details, see [Linear allocation algorithm](@ref linear_algorithm).
2268 */
2269 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
2270
2271 /** \brief Enables alternative, buddy allocation algorithm in this pool.
2272
2273 It operates on a tree of blocks, each having size that is a power of two and
2274 a half of its parent's size. Comparing to default algorithm, this one provides
2275 faster allocation and deallocation and decreased external fragmentation,
2276 at the expense of more memory wasted (internal fragmentation).
2277
2278 For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
2279 */
2280 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
2281
2282 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
2283 */
2284 VMA_POOL_CREATE_ALGORITHM_MASK =
2285 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
2286 VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
2287
2288 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2289} VmaPoolCreateFlagBits;
2290typedef VkFlags VmaPoolCreateFlags;
2291
2292/** \brief Describes parameter of created #VmaPool.
2293*/
2294typedef struct VmaPoolCreateInfo {
2295 /** \brief Vulkan memory type index to allocate this pool from.
2296 */
2297 uint32_t memoryTypeIndex;
2298 /** \brief Use combination of #VmaPoolCreateFlagBits.
2299 */
2300 VmaPoolCreateFlags flags;
2301 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
2302
2303 Specify nonzero to set explicit, constant size of memory blocks used by this
2304 pool.
2305
2306 Leave 0 to use default and let the library manage block sizes automatically.
2307 Sizes of particular blocks may vary.
2308 */
2309 VkDeviceSize blockSize;
2310 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
2311
2312 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
2313 */
2314 size_t minBlockCount;
2315 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
2316
2317 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
2318
2319 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
2320 throughout whole lifetime of this pool.
2321 */
2322 size_t maxBlockCount;
2323 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
2324
2325 This value is used only when you make allocations with
2326 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
2327 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
2328
2329 For example, if you double-buffer your command buffers, so resources used for
2330 rendering in previous frame may still be in use by the GPU at the moment you
2331 allocate resources needed for the current frame, set this value to 1.
2332
2333 If you want to allow any allocations other than used in the current frame to
2334 become lost, set this value to 0.
2335 */
2336 uint32_t frameInUseCount;
2337} VmaPoolCreateInfo;
2338
2339/** \brief Describes parameter of existing #VmaPool.
2340*/
2341typedef struct VmaPoolStats {
2342 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
2343 */
2344 VkDeviceSize size;
2345 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
2346 */
2347 VkDeviceSize unusedSize;
2348 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
2349 */
2350 size_t allocationCount;
2351 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
2352 */
2353 size_t unusedRangeCount;
2354 /** \brief Size of the largest continuous free memory region available for new allocation.
2355
2356 Making a new allocation of that size is not guaranteed to succeed because of
2357 possible additional margin required to respect alignment and buffer/image
2358 granularity.
2359 */
2360 VkDeviceSize unusedRangeSizeMax;
2361 /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
2362 */
2363 size_t blockCount;
2364} VmaPoolStats;
2365
2366/** \brief Allocates Vulkan device memory and creates #VmaPool object.
2367
2368@param allocator Allocator object.
2369@param pCreateInfo Parameters of pool to create.
2370@param[out] pPool Handle to created pool.
2371*/
2372VkResult vmaCreatePool(
2373 VmaAllocator allocator,
2374 const VmaPoolCreateInfo* pCreateInfo,
2375 VmaPool* pPool);
2376
2377/** \brief Destroys #VmaPool object and frees Vulkan device memory.
2378*/
2379void vmaDestroyPool(
2380 VmaAllocator allocator,
2381 VmaPool pool);
2382
2383/** \brief Retrieves statistics of existing #VmaPool object.
2384
2385@param allocator Allocator object.
2386@param pool Pool object.
2387@param[out] pPoolStats Statistics of specified pool.
2388*/
2389void vmaGetPoolStats(
2390 VmaAllocator allocator,
2391 VmaPool pool,
2392 VmaPoolStats* pPoolStats);
2393
2394/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
2395
2396@param allocator Allocator object.
2397@param pool Pool.
2398@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
2399*/
2400void vmaMakePoolAllocationsLost(
2401 VmaAllocator allocator,
2402 VmaPool pool,
2403 size_t* pLostAllocationCount);
2404
2405/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
2406
2407Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2408`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
2409`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2410
2411Possible return values:
2412
2413- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
2414- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2415- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2416 `VMA_ASSERT` is also fired in that case.
2417- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2418*/
2419VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
2420
2421/** \struct VmaAllocation
2422\brief Represents single memory allocation.
2423
2424It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
2425plus unique offset.
2426
2427There are multiple ways to create such object.
2428You need to fill structure VmaAllocationCreateInfo.
2429For more information see [Choosing memory type](@ref choosing_memory_type).
2430
2431Although the library provides convenience functions that create Vulkan buffer or image,
2432allocate memory for it and bind them together,
2433binding of the allocation to a buffer or an image is out of scope of the allocation itself.
2434Allocation object can exist without buffer/image bound,
2435binding can be done manually by the user, and destruction of it can be done
2436independently of destruction of the allocation.
2437
2438The object also remembers its size and some other information.
2439To retrieve this information, use function vmaGetAllocationInfo() and inspect
2440returned structure VmaAllocationInfo.
2441
2442Some kinds allocations can be in lost state.
2443For more information, see [Lost allocations](@ref lost_allocations).
2444*/
2445VK_DEFINE_HANDLE(VmaAllocation)
2446
2447/** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
2448*/
2449typedef struct VmaAllocationInfo {
2450 /** \brief Memory type index that this allocation was allocated from.
2451
2452 It never changes.
2453 */
2454 uint32_t memoryType;
2455 /** \brief Handle to Vulkan memory object.
2456
2457 Same memory object can be shared by multiple allocations.
2458
2459 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2460
2461 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
2462 */
2463 VkDeviceMemory deviceMemory;
2464 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
2465
2466 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
2467 */
2468 VkDeviceSize offset;
2469 /** \brief Size of this allocation, in bytes.
2470
2471 It never changes, unless allocation is lost.
2472 */
2473 VkDeviceSize size;
2474 /** \brief Pointer to the beginning of this allocation as mapped data.
2475
2476 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
2477 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
2478
2479 It can change after call to vmaMapMemory(), vmaUnmapMemory().
2480 It can also change after call to vmaDefragment() if this allocation is passed to the function.
2481 */
2482 void* pMappedData;
2483 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
2484
2485 It can change after call to vmaSetAllocationUserData() for this allocation.
2486 */
2487 void* pUserData;
2488} VmaAllocationInfo;
2489
2490/** \brief General purpose memory allocation.
2491
2492@param[out] pAllocation Handle to allocated memory.
2493@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2494
2495You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2496
2497It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
2498vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
2499*/
2500VkResult vmaAllocateMemory(
2501 VmaAllocator allocator,
2502 const VkMemoryRequirements* pVkMemoryRequirements,
2503 const VmaAllocationCreateInfo* pCreateInfo,
2504 VmaAllocation* pAllocation,
2505 VmaAllocationInfo* pAllocationInfo);
2506
2507/** \brief General purpose memory allocation for multiple allocation objects at once.
2508
2509@param allocator Allocator object.
2510@param pVkMemoryRequirements Memory requirements for each allocation.
2511@param pCreateInfo Creation parameters for each alloction.
2512@param allocationCount Number of allocations to make.
2513@param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
2514@param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
2515
2516You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
2517
2518Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
2519It is just a general purpose allocation function able to make multiple allocations at once.
2520It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
2521
2522All allocations are made using same parameters. All of them are created out of the same memory pool and type.
2523If any allocation fails, all allocations already made within this function call are also freed, so that when
2524returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
2525*/
2526VkResult vmaAllocateMemoryPages(
2527 VmaAllocator allocator,
2528 const VkMemoryRequirements* pVkMemoryRequirements,
2529 const VmaAllocationCreateInfo* pCreateInfo,
2530 size_t allocationCount,
2531 VmaAllocation* pAllocations,
2532 VmaAllocationInfo* pAllocationInfo);
2533
2534/**
2535@param[out] pAllocation Handle to allocated memory.
2536@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2537
2538You should free the memory using vmaFreeMemory().
2539*/
2540VkResult vmaAllocateMemoryForBuffer(
2541 VmaAllocator allocator,
2542 VkBuffer buffer,
2543 const VmaAllocationCreateInfo* pCreateInfo,
2544 VmaAllocation* pAllocation,
2545 VmaAllocationInfo* pAllocationInfo);
2546
2547/// Function similar to vmaAllocateMemoryForBuffer().
2548VkResult vmaAllocateMemoryForImage(
2549 VmaAllocator allocator,
2550 VkImage image,
2551 const VmaAllocationCreateInfo* pCreateInfo,
2552 VmaAllocation* pAllocation,
2553 VmaAllocationInfo* pAllocationInfo);
2554
2555/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
2556
2557Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
2558*/
2559void vmaFreeMemory(
2560 VmaAllocator allocator,
2561 VmaAllocation allocation);
2562
2563/** \brief Frees memory and destroys multiple allocations.
2564
2565Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
2566It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
2567vmaAllocateMemoryPages() and other functions.
2568It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
2569
2570Allocations in `pAllocations` array can come from any memory pools and types.
2571Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
2572*/
2573void vmaFreeMemoryPages(
2574 VmaAllocator allocator,
2575 size_t allocationCount,
2576 VmaAllocation* pAllocations);
2577
2578/** \brief Tries to resize an allocation in place, if there is enough free memory after it.
2579
2580Tries to change allocation's size without moving or reallocating it.
2581You can both shrink and grow allocation size.
2582When growing, it succeeds only when the allocation belongs to a memory block with enough
2583free space after it.
2584
2585Returns `VK_SUCCESS` if allocation's size has been successfully changed.
2586Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
2587
2588After successful call to this function, VmaAllocationInfo::size of this allocation changes.
2589All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
2590
2591- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
2592- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
2593- Resizing dedicated allocations, as well as allocations created in pools that use linear
2594 or buddy algorithm, is not supported.
2595 The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
2596 Support may be added in the future.
2597*/
2598VkResult vmaResizeAllocation(
2599 VmaAllocator allocator,
2600 VmaAllocation allocation,
2601 VkDeviceSize newSize);
2602
2603/** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
2604
2605Current paramters of given allocation are returned in `pAllocationInfo`.
2606
2607This function also atomically "touches" allocation - marks it as used in current frame,
2608just like vmaTouchAllocation().
2609If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
2610
2611Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
2612you can avoid calling it too often.
2613
2614- You can retrieve same VmaAllocationInfo structure while creating your resource, from function
2615 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
2616 (e.g. due to defragmentation or allocation becoming lost).
2617- If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
2618*/
2619void vmaGetAllocationInfo(
2620 VmaAllocator allocator,
2621 VmaAllocation allocation,
2622 VmaAllocationInfo* pAllocationInfo);
2623
2624/** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
2625
2626If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2627this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
2628It then also atomically "touches" the allocation - marks it as used in current frame,
2629so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
2630
2631If the allocation is in lost state, the function returns `VK_FALSE`.
2632Memory of such allocation, as well as buffer or image bound to it, should not be used.
2633Lost allocation and the buffer/image still need to be destroyed.
2634
2635If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
2636this function always returns `VK_TRUE`.
2637*/
2638VkBool32 vmaTouchAllocation(
2639 VmaAllocator allocator,
2640 VmaAllocation allocation);
2641
2642/** \brief Sets pUserData in given allocation to new value.
2643
2644If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
2645pUserData must be either null, or pointer to a null-terminated string. The function
2646makes local copy of the string and sets it as allocation's `pUserData`. String
2647passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
2648you can free it after this call. String previously pointed by allocation's
2649pUserData is freed from memory.
2650
2651If the flag was not used, the value of pointer `pUserData` is just copied to
2652allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
2653as a pointer, ordinal number or some handle to you own data.
2654*/
2655void vmaSetAllocationUserData(
2656 VmaAllocator allocator,
2657 VmaAllocation allocation,
2658 void* pUserData);
2659
2660/** \brief Creates new allocation that is in lost state from the beginning.
2661
2662It can be useful if you need a dummy, non-null allocation.
2663
2664You still need to destroy created object using vmaFreeMemory().
2665
2666Returned allocation is not tied to any specific memory pool or memory type and
2667not bound to any image or buffer. It has size = 0. It cannot be turned into
2668a real, non-empty allocation.
2669*/
2670void vmaCreateLostAllocation(
2671 VmaAllocator allocator,
2672 VmaAllocation* pAllocation);
2673
2674/** \brief Maps memory represented by given allocation and returns pointer to it.
2675
2676Maps memory represented by given allocation to make it accessible to CPU code.
2677When succeeded, `*ppData` contains pointer to first byte of this memory.
2678If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
2679correctly offseted to the beginning of region assigned to this particular
2680allocation.
2681
2682Mapping is internally reference-counted and synchronized, so despite raw Vulkan
2683function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
2684multiple times simultaneously, it is safe to call this function on allocations
2685assigned to the same memory block. Actual Vulkan memory will be mapped on first
2686mapping and unmapped on last unmapping.
2687
2688If the function succeeded, you must call vmaUnmapMemory() to unmap the
2689allocation when mapping is no longer needed or before freeing the allocation, at
2690the latest.
2691
2692It also safe to call this function multiple times on the same allocation. You
2693must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
2694
2695It is also safe to call this function on allocation created with
2696#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
2697You must still call vmaUnmapMemory() same number of times as you called
2698vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
2699"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
2700
2701This function fails when used on allocation made in memory type that is not
2702`HOST_VISIBLE`.
2703
2704This function always fails when called for allocation that was created with
2705#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
2706mapped.
2707*/
2708VkResult vmaMapMemory(
2709 VmaAllocator allocator,
2710 VmaAllocation allocation,
2711 void** ppData);
2712
2713/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2714
2715For details, see description of vmaMapMemory().
2716*/
2717void vmaUnmapMemory(
2718 VmaAllocator allocator,
2719 VmaAllocation allocation);
2720
2721/** \brief Flushes memory of given allocation.
2722
2723Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2724
2725- `offset` must be relative to the beginning of allocation.
2726- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2727- `offset` and `size` don't have to be aligned.
2728 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2729- If `size` is 0, this call is ignored.
2730- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2731 this call is ignored.
2732*/
2733void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2734
2735/** \brief Invalidates memory of given allocation.
2736
2737Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2738
2739- `offset` must be relative to the beginning of allocation.
2740- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2741- `offset` and `size` don't have to be aligned.
2742 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2743- If `size` is 0, this call is ignored.
2744- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2745 this call is ignored.
2746*/
2747void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
2748
2749/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2750
2751@param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2752
2753Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2754`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2755`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2756
2757Possible return values:
2758
2759- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2760- `VK_SUCCESS` - corruption detection has been performed and succeeded.
2761- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
2762 `VMA_ASSERT` is also fired in that case.
2763- Other value: Error returned by Vulkan, e.g. memory mapping failure.
2764*/
2765VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
2766
2767/** \struct VmaDefragmentationContext
2768\brief Represents Opaque object that represents started defragmentation process.
2769
2770Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
2771Call function vmaDefragmentationEnd() to destroy it.
2772*/
2773VK_DEFINE_HANDLE(VmaDefragmentationContext)
2774
2775/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
2776typedef enum VmaDefragmentationFlagBits {
2777 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
2778} VmaDefragmentationFlagBits;
2779typedef VkFlags VmaDefragmentationFlags;
2780
2781/** \brief Parameters for defragmentation.
2782
2783To be used with function vmaDefragmentationBegin().
2784*/
2785typedef struct VmaDefragmentationInfo2 {
2786 /** \brief Reserved for future use. Should be 0.
2787 */
2788 VmaDefragmentationFlags flags;
2789 /** \brief Number of allocations in `pAllocations` array.
2790 */
2791 uint32_t allocationCount;
2792 /** \brief Pointer to array of allocations that can be defragmented.
2793
2794 The array should have `allocationCount` elements.
2795 The array should not contain nulls.
2796 Elements in the array should be unique - same allocation cannot occur twice.
2797 It is safe to pass allocations that are in the lost state - they are ignored.
2798 All allocations not present in this array are considered non-moveable during this defragmentation.
2799 */
2800 VmaAllocation* pAllocations;
2801 /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
2802
2803 The array should have `allocationCount` elements.
2804 You can pass null if you are not interested in this information.
2805 */
2806 VkBool32* pAllocationsChanged;
2807 /** \brief Numer of pools in `pPools` array.
2808 */
2809 uint32_t poolCount;
2810 /** \brief Either null or pointer to array of pools to be defragmented.
2811
2812 All the allocations in the specified pools can be moved during defragmentation
2813 and there is no way to check if they were really moved as in `pAllocationsChanged`,
2814 so you must query all the allocations in all these pools for new `VkDeviceMemory`
2815 and offset using vmaGetAllocationInfo() if you might need to recreate buffers
2816 and images bound to them.
2817
2818 The array should have `poolCount` elements.
2819 The array should not contain nulls.
2820 Elements in the array should be unique - same pool cannot occur twice.
2821
2822 Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
2823 It might be more efficient.
2824 */
2825 VmaPool* pPools;
2826 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
2827
2828 `VK_WHOLE_SIZE` means no limit.
2829 */
2830 VkDeviceSize maxCpuBytesToMove;
2831 /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
2832
2833 `UINT32_MAX` means no limit.
2834 */
2835 uint32_t maxCpuAllocationsToMove;
2836 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
2837
2838 `VK_WHOLE_SIZE` means no limit.
2839 */
2840 VkDeviceSize maxGpuBytesToMove;
2841 /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
2842
2843 `UINT32_MAX` means no limit.
2844 */
2845 uint32_t maxGpuAllocationsToMove;
2846 /** \brief Optional. Command buffer where GPU copy commands will be posted.
2847
2848 If not null, it must be a valid command buffer handle that supports Transfer queue type.
2849 It must be in the recording state and outside of a render pass instance.
2850 You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
2851
2852 Passing null means that only CPU defragmentation will be performed.
2853 */
2854 VkCommandBuffer commandBuffer;
2855} VmaDefragmentationInfo2;
2856
2857/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
2858
2859\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2860*/
2861typedef struct VmaDefragmentationInfo {
2862 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
2863
2864 Default is `VK_WHOLE_SIZE`, which means no limit.
2865 */
2866 VkDeviceSize maxBytesToMove;
2867 /** \brief Maximum number of allocations that can be moved to different place.
2868
2869 Default is `UINT32_MAX`, which means no limit.
2870 */
2871 uint32_t maxAllocationsToMove;
2872} VmaDefragmentationInfo;
2873
2874/** \brief Statistics returned by function vmaDefragment(). */
2875typedef struct VmaDefragmentationStats {
2876 /// Total number of bytes that have been copied while moving allocations to different places.
2877 VkDeviceSize bytesMoved;
2878 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
2879 VkDeviceSize bytesFreed;
2880 /// Number of allocations that have been moved to different places.
2881 uint32_t allocationsMoved;
2882 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
2883 uint32_t deviceMemoryBlocksFreed;
2884} VmaDefragmentationStats;
2885
2886/** \brief Begins defragmentation process.
2887
2888@param allocator Allocator object.
2889@param pInfo Structure filled with parameters of defragmentation.
2890@param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
2891@param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
2892@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.
2893
2894Use this function instead of old, deprecated vmaDefragment().
2895
2896Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
2897
2898- You should not use any of allocations passed as `pInfo->pAllocations` or
2899 any allocations that belong to pools passed as `pInfo->pPools`,
2900 including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
2901 their data.
2902- Some mutexes protecting internal data structures may be locked, so trying to
2903 make or free any allocations, bind buffers or images, map memory, or launch
2904 another simultaneous defragmentation in between may cause stall (when done on
2905 another thread) or deadlock (when done on the same thread), unless you are
2906 100% sure that defragmented allocations are in different pools.
2907- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
2908 They become valid after call to vmaDefragmentationEnd().
2909- If `pInfo->commandBuffer` is not null, you must submit that command buffer
2910 and make sure it finished execution before calling vmaDefragmentationEnd().
2911*/
2912VkResult vmaDefragmentationBegin(
2913 VmaAllocator allocator,
2914 const VmaDefragmentationInfo2* pInfo,
2915 VmaDefragmentationStats* pStats,
2916 VmaDefragmentationContext *pContext);
2917
2918/** \brief Ends defragmentation process.
2919
2920Use this function to finish defragmentation started by vmaDefragmentationBegin().
2921It is safe to pass `context == null`. The function then does nothing.
2922*/
2923VkResult vmaDefragmentationEnd(
2924 VmaAllocator allocator,
2925 VmaDefragmentationContext context);
2926
2927/** \brief Deprecated. Compacts memory by moving allocations.
2928
2929@param pAllocations Array of allocations that can be moved during this compation.
2930@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
2931@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.
2932@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
2933@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
2934@return `VK_SUCCESS` if completed, negative error code in case of error.
2935
2936\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
2937
2938This function works by moving allocations to different places (different
2939`VkDeviceMemory` objects and/or different offsets) in order to optimize memory
2940usage. Only allocations that are in `pAllocations` array can be moved. All other
2941allocations are considered nonmovable in this call. Basic rules:
2942
2943- Only allocations made in memory types that have
2944 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
2945 flags can be compacted. You may pass other allocations but it makes no sense -
2946 these will never be moved.
2947- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
2948 #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
2949 passed to this function that come from such pools are ignored.
2950- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
2951 created as dedicated allocations for any other reason are also ignored.
2952- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
2953 flag can be compacted. If not persistently mapped, memory will be mapped
2954 temporarily inside this function if needed.
2955- You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
2956
2957The function also frees empty `VkDeviceMemory` blocks.
2958
2959Warning: This function may be time-consuming, so you shouldn't call it too often
2960(like after every resource creation/destruction).
2961You can call it on special occasions (like when reloading a game level or
2962when you just destroyed a lot of objects). Calling it every frame may be OK, but
2963you should measure that on your platform.
2964
2965For more information, see [Defragmentation](@ref defragmentation) chapter.
2966*/
2967VkResult vmaDefragment(
2968 VmaAllocator allocator,
2969 VmaAllocation* pAllocations,
2970 size_t allocationCount,
2971 VkBool32* pAllocationsChanged,
2972 const VmaDefragmentationInfo *pDefragmentationInfo,
2973 VmaDefragmentationStats* pDefragmentationStats);
2974
2975/** \brief Binds buffer to allocation.
2976
2977Binds specified buffer to region of memory represented by specified allocation.
2978Gets `VkDeviceMemory` handle and offset from the allocation.
2979If you want to create a buffer, allocate memory for it and bind them together separately,
2980you should use this function for binding instead of standard `vkBindBufferMemory()`,
2981because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2982allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2983(which is illegal in Vulkan).
2984
2985It is recommended to use function vmaCreateBuffer() instead of this one.
2986*/
2987VkResult vmaBindBufferMemory(
2988 VmaAllocator allocator,
2989 VmaAllocation allocation,
2990 VkBuffer buffer);
2991
2992/** \brief Binds image to allocation.
2993
2994Binds specified image to region of memory represented by specified allocation.
2995Gets `VkDeviceMemory` handle and offset from the allocation.
2996If you want to create an image, allocate memory for it and bind them together separately,
2997you should use this function for binding instead of standard `vkBindImageMemory()`,
2998because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2999allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
3000(which is illegal in Vulkan).
3001
3002It is recommended to use function vmaCreateImage() instead of this one.
3003*/
3004VkResult vmaBindImageMemory(
3005 VmaAllocator allocator,
3006 VmaAllocation allocation,
3007 VkImage image);
3008
3009/**
3010@param[out] pBuffer Buffer that was created.
3011@param[out] pAllocation Allocation that was created.
3012@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
3013
3014This function automatically:
3015
3016-# Creates buffer.
3017-# Allocates appropriate memory for it.
3018-# Binds the buffer with the memory.
3019
3020If any of these operations fail, buffer and allocation are not created,
3021returned value is negative error code, *pBuffer and *pAllocation are null.
3022
3023If the function succeeded, you must destroy both buffer and allocation when you
3024no longer need them using either convenience function vmaDestroyBuffer() or
3025separately, using `vkDestroyBuffer()` and vmaFreeMemory().
3026
3027If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
3028VK_KHR_dedicated_allocation extension is used internally to query driver whether
3029it requires or prefers the new buffer to have dedicated allocation. If yes,
3030and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
3031and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
3032allocation for this buffer, just like when using
3033VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
3034*/
3035VkResult vmaCreateBuffer(
3036 VmaAllocator allocator,
3037 const VkBufferCreateInfo* pBufferCreateInfo,
3038 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3039 VkBuffer* pBuffer,
3040 VmaAllocation* pAllocation,
3041 VmaAllocationInfo* pAllocationInfo);
3042
3043/** \brief Destroys Vulkan buffer and frees allocated memory.
3044
3045This is just a convenience function equivalent to:
3046
3047\code
3048vkDestroyBuffer(device, buffer, allocationCallbacks);
3049vmaFreeMemory(allocator, allocation);
3050\endcode
3051
3052It it safe to pass null as buffer and/or allocation.
3053*/
3054void vmaDestroyBuffer(
3055 VmaAllocator allocator,
3056 VkBuffer buffer,
3057 VmaAllocation allocation);
3058
3059/// Function similar to vmaCreateBuffer().
3060VkResult vmaCreateImage(
3061 VmaAllocator allocator,
3062 const VkImageCreateInfo* pImageCreateInfo,
3063 const VmaAllocationCreateInfo* pAllocationCreateInfo,
3064 VkImage* pImage,
3065 VmaAllocation* pAllocation,
3066 VmaAllocationInfo* pAllocationInfo);
3067
3068/** \brief Destroys Vulkan image and frees allocated memory.
3069
3070This is just a convenience function equivalent to:
3071
3072\code
3073vkDestroyImage(device, image, allocationCallbacks);
3074vmaFreeMemory(allocator, allocation);
3075\endcode
3076
3077It it safe to pass null as image and/or allocation.
3078*/
3079void vmaDestroyImage(
3080 VmaAllocator allocator,
3081 VkImage image,
3082 VmaAllocation allocation);
3083
3084#ifdef __cplusplus
3085}
3086#endif
3087
3088#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
3089
3090// For Visual Studio IntelliSense.
3091#if defined(__cplusplus) && defined(__INTELLISENSE__)
3092#define VMA_IMPLEMENTATION
3093#endif
3094
3095#ifdef VMA_IMPLEMENTATION
3096#undef VMA_IMPLEMENTATION
3097
3098#include <cstdint>
3099#include <cstdlib>
3100#include <cstring>
3101
3102/*******************************************************************************
3103CONFIGURATION SECTION
3104
3105Define some of these macros before each #include of this header or change them
3106here if you need other then default behavior depending on your environment.
3107*/
3108
3109/*
3110Define this macro to 1 to make the library fetch pointers to Vulkan functions
3111internally, like:
3112
3113 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
3114
3115Define to 0 if you are going to provide you own pointers to Vulkan functions via
3116VmaAllocatorCreateInfo::pVulkanFunctions.
3117*/
3118#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
3119#define VMA_STATIC_VULKAN_FUNCTIONS 1
3120#endif
3121
3122// Define this macro to 1 to make the library use STL containers instead of its own implementation.
3123//#define VMA_USE_STL_CONTAINERS 1
3124
3125/* Set this macro to 1 to make the library including and using STL containers:
3126std::pair, std::vector, std::list, std::unordered_map.
3127
3128Set it to 0 or undefined to make the library using its own implementation of
3129the containers.
3130*/
3131#if VMA_USE_STL_CONTAINERS
3132 #define VMA_USE_STL_VECTOR 1
3133 #define VMA_USE_STL_UNORDERED_MAP 1
3134 #define VMA_USE_STL_LIST 1
3135#endif
3136
3137#ifndef VMA_USE_STL_SHARED_MUTEX
3138 // Minimum Visual Studio 2015 Update 2
3139 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
3140 #define VMA_USE_STL_SHARED_MUTEX 1
3141 #endif
3142#endif
3143
3144#if VMA_USE_STL_VECTOR
3145 #include <vector>
3146#endif
3147
3148#if VMA_USE_STL_UNORDERED_MAP
3149 #include <unordered_map>
3150#endif
3151
3152#if VMA_USE_STL_LIST
3153 #include <list>
3154#endif
3155
3156/*
3157Following headers are used in this CONFIGURATION section only, so feel free to
3158remove them if not needed.
3159*/
3160#include <cassert> // for assert
3161#include <algorithm> // for min, max
3162#include <mutex>
3163#include <atomic> // for std::atomic
3164
3165#ifndef VMA_NULL
3166 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
3167 #define VMA_NULL nullptr
3168#endif
3169
3170#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
3171#include <cstdlib>
3172void *aligned_alloc(size_t alignment, size_t size)
3173{
3174 // alignment must be >= sizeof(void*)
3175 if(alignment < sizeof(void*))
3176 {
3177 alignment = sizeof(void*);
3178 }
3179
3180 return memalign(alignment, size);
3181}
3182#elif defined(__APPLE__) || defined(__ANDROID__)
François Bertel3aaaa812019-04-19 11:11:50 -04003183# define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3184#elif defined(__GNU_LIBRARY__)
3185# if !defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 16)
3186// aligned_alloc() is defined in glibc only for version >= 2.16
3187# define ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
3188# endif
3189#endif
3190
3191#ifdef ALIGNED_ALLOC_WITH_POSIX_MEMALIGN
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06003192#include <cstdlib>
3193void *aligned_alloc(size_t alignment, size_t size)
3194{
3195 // alignment must be >= sizeof(void*)
3196 if(alignment < sizeof(void*))
3197 {
3198 alignment = sizeof(void*);
3199 }
3200
3201 void *pointer;
3202 if(posix_memalign(&pointer, alignment, size) == 0)
3203 return pointer;
3204 return VMA_NULL;
3205}
3206#endif
3207
3208// If your compiler is not compatible with C++11 and definition of
3209// aligned_alloc() function is missing, uncommeting following line may help:
3210
3211//#include <malloc.h>
3212
3213// Normal assert to check for programmer's errors, especially in Debug configuration.
3214#ifndef VMA_ASSERT
3215 #ifdef _DEBUG
3216 #define VMA_ASSERT(expr) assert(expr)
3217 #else
3218 #define VMA_ASSERT(expr)
3219 #endif
3220#endif
3221
3222// Assert that will be called very often, like inside data structures e.g. operator[].
3223// Making it non-empty can make program slow.
3224#ifndef VMA_HEAVY_ASSERT
3225 #ifdef _DEBUG
3226 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
3227 #else
3228 #define VMA_HEAVY_ASSERT(expr)
3229 #endif
3230#endif
3231
3232#ifndef VMA_ALIGN_OF
3233 #define VMA_ALIGN_OF(type) (__alignof(type))
3234#endif
3235
3236#ifndef VMA_SYSTEM_ALIGNED_MALLOC
3237 #if defined(_WIN32)
3238 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
3239 #else
3240 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
3241 #endif
3242#endif
3243
3244#ifndef VMA_SYSTEM_FREE
3245 #if defined(_WIN32)
3246 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
3247 #else
3248 #define VMA_SYSTEM_FREE(ptr) free(ptr)
3249 #endif
3250#endif
3251
3252#ifndef VMA_MIN
3253 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
3254#endif
3255
3256#ifndef VMA_MAX
3257 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
3258#endif
3259
3260#ifndef VMA_SWAP
3261 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
3262#endif
3263
3264#ifndef VMA_SORT
3265 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
3266#endif
3267
3268#ifndef VMA_DEBUG_LOG
3269 #define VMA_DEBUG_LOG(format, ...)
3270 /*
3271 #define VMA_DEBUG_LOG(format, ...) do { \
3272 printf(format, __VA_ARGS__); \
3273 printf("\n"); \
3274 } while(false)
3275 */
3276#endif
3277
3278// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
3279#if VMA_STATS_STRING_ENABLED
3280 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
3281 {
3282 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
3283 }
3284 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
3285 {
3286 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
3287 }
3288 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
3289 {
3290 snprintf(outStr, strLen, "%p", ptr);
3291 }
3292#endif
3293
3294#ifndef VMA_MUTEX
3295 class VmaMutex
3296 {
3297 public:
3298 void Lock() { m_Mutex.lock(); }
3299 void Unlock() { m_Mutex.unlock(); }
3300 private:
3301 std::mutex m_Mutex;
3302 };
3303 #define VMA_MUTEX VmaMutex
3304#endif
3305
3306// Read-write mutex, where "read" is shared access, "write" is exclusive access.
3307#ifndef VMA_RW_MUTEX
3308 #if VMA_USE_STL_SHARED_MUTEX
3309 // Use std::shared_mutex from C++17.
3310 #include <shared_mutex>
3311 class VmaRWMutex
3312 {
3313 public:
3314 void LockRead() { m_Mutex.lock_shared(); }
3315 void UnlockRead() { m_Mutex.unlock_shared(); }
3316 void LockWrite() { m_Mutex.lock(); }
3317 void UnlockWrite() { m_Mutex.unlock(); }
3318 private:
3319 std::shared_mutex m_Mutex;
3320 };
3321 #define VMA_RW_MUTEX VmaRWMutex
3322 #elif defined(_WIN32)
3323 // Use SRWLOCK from WinAPI.
3324 class VmaRWMutex
3325 {
3326 public:
3327 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
3328 void LockRead() { AcquireSRWLockShared(&m_Lock); }
3329 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
3330 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
3331 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
3332 private:
3333 SRWLOCK m_Lock;
3334 };
3335 #define VMA_RW_MUTEX VmaRWMutex
3336 #else
3337 // Less efficient fallback: Use normal mutex.
3338 class VmaRWMutex
3339 {
3340 public:
3341 void LockRead() { m_Mutex.Lock(); }
3342 void UnlockRead() { m_Mutex.Unlock(); }
3343 void LockWrite() { m_Mutex.Lock(); }
3344 void UnlockWrite() { m_Mutex.Unlock(); }
3345 private:
3346 VMA_MUTEX m_Mutex;
3347 };
3348 #define VMA_RW_MUTEX VmaRWMutex
3349 #endif // #if VMA_USE_STL_SHARED_MUTEX
3350#endif // #ifndef VMA_RW_MUTEX
3351
3352/*
3353If providing your own implementation, you need to implement a subset of std::atomic:
3354
3355- Constructor(uint32_t desired)
3356- uint32_t load() const
3357- void store(uint32_t desired)
3358- bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
3359*/
3360#ifndef VMA_ATOMIC_UINT32
3361 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
3362#endif
3363
3364#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
3365 /**
3366 Every allocation will have its own memory block.
3367 Define to 1 for debugging purposes only.
3368 */
3369 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
3370#endif
3371
3372#ifndef VMA_DEBUG_ALIGNMENT
3373 /**
3374 Minimum alignment of all allocations, in bytes.
3375 Set to more than 1 for debugging purposes only. Must be power of two.
3376 */
3377 #define VMA_DEBUG_ALIGNMENT (1)
3378#endif
3379
3380#ifndef VMA_DEBUG_MARGIN
3381 /**
3382 Minimum margin before and after every allocation, in bytes.
3383 Set nonzero for debugging purposes only.
3384 */
3385 #define VMA_DEBUG_MARGIN (0)
3386#endif
3387
3388#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
3389 /**
3390 Define this macro to 1 to automatically fill new allocations and destroyed
3391 allocations with some bit pattern.
3392 */
3393 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
3394#endif
3395
3396#ifndef VMA_DEBUG_DETECT_CORRUPTION
3397 /**
3398 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
3399 enable writing magic value to the margin before and after every allocation and
3400 validating it, so that memory corruptions (out-of-bounds writes) are detected.
3401 */
3402 #define VMA_DEBUG_DETECT_CORRUPTION (0)
3403#endif
3404
3405#ifndef VMA_DEBUG_GLOBAL_MUTEX
3406 /**
3407 Set this to 1 for debugging purposes only, to enable single mutex protecting all
3408 entry calls to the library. Can be useful for debugging multithreading issues.
3409 */
3410 #define VMA_DEBUG_GLOBAL_MUTEX (0)
3411#endif
3412
3413#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
3414 /**
3415 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
3416 Set to more than 1 for debugging purposes only. Must be power of two.
3417 */
3418 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
3419#endif
3420
3421#ifndef VMA_SMALL_HEAP_MAX_SIZE
3422 /// Maximum size of a memory heap in Vulkan to consider it "small".
3423 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
3424#endif
3425
3426#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
3427 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
3428 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
3429#endif
3430
3431#ifndef VMA_CLASS_NO_COPY
3432 #define VMA_CLASS_NO_COPY(className) \
3433 private: \
3434 className(const className&) = delete; \
3435 className& operator=(const className&) = delete;
3436#endif
3437
3438static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
3439
3440// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3441static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3442
3443static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3444static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3445
3446/*******************************************************************************
3447END OF CONFIGURATION
3448*/
3449
Tony-LunarG390319b2019-03-18 15:54:16 -06003450#if defined(__GNUC__)
Tony-LunarG705b4cb2019-05-22 15:32:30 -06003451#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
Tony-LunarG390319b2019-03-18 15:54:16 -06003452#pragma GCC diagnostic push
3453#pragma GCC diagnostic ignored "-Wtype-limits"
3454#pragma GCC diagnostic ignored "-Wunused-variable"
Jeremy Hayes33838bf2019-06-05 14:01:20 -06003455#if defined(__clang__)
3456#pragma clang diagnostic push
3457#pragma clang diagnostic ignored "-Wtautological-compare"
3458#endif
Tony-LunarG705b4cb2019-05-22 15:32:30 -06003459#if GCC_VERSION >= 80000
3460#pragma GCC diagnostic ignored "-Wclass-memaccess"
3461#endif
Tony-LunarG390319b2019-03-18 15:54:16 -06003462#if defined(ANDROID)
3463#pragma GCC diagnostic ignored "-Wunused-private-field"
3464#endif
3465#endif
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06003466static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3467
3468static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
3469 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3470
3471// Returns number of bits set to 1 in (v).
3472static inline uint32_t VmaCountBitsSet(uint32_t v)
3473{
3474 uint32_t c = v - ((v >> 1) & 0x55555555);
3475 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3476 c = ((c >> 4) + c) & 0x0F0F0F0F;
3477 c = ((c >> 8) + c) & 0x00FF00FF;
3478 c = ((c >> 16) + c) & 0x0000FFFF;
3479 return c;
3480}
3481
3482// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3483// Use types like uint32_t, uint64_t as T.
3484template <typename T>
3485static inline T VmaAlignUp(T val, T align)
3486{
3487 return (val + align - 1) / align * align;
3488}
3489// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3490// Use types like uint32_t, uint64_t as T.
3491template <typename T>
3492static inline T VmaAlignDown(T val, T align)
3493{
3494 return val / align * align;
3495}
3496
3497// Division with mathematical rounding to nearest number.
3498template <typename T>
3499static inline T VmaRoundDiv(T x, T y)
3500{
3501 return (x + (y / (T)2)) / y;
3502}
3503
3504/*
3505Returns true if given number is a power of two.
3506T must be unsigned integer number or signed integer but always nonnegative.
3507For 0 returns true.
3508*/
3509template <typename T>
3510inline bool VmaIsPow2(T x)
3511{
3512 return (x & (x-1)) == 0;
3513}
3514
3515// Returns smallest power of 2 greater or equal to v.
3516static inline uint32_t VmaNextPow2(uint32_t v)
3517{
3518 v--;
3519 v |= v >> 1;
3520 v |= v >> 2;
3521 v |= v >> 4;
3522 v |= v >> 8;
3523 v |= v >> 16;
3524 v++;
3525 return v;
3526}
3527static inline uint64_t VmaNextPow2(uint64_t v)
3528{
3529 v--;
3530 v |= v >> 1;
3531 v |= v >> 2;
3532 v |= v >> 4;
3533 v |= v >> 8;
3534 v |= v >> 16;
3535 v |= v >> 32;
3536 v++;
3537 return v;
3538}
3539
3540// Returns largest power of 2 less or equal to v.
3541static inline uint32_t VmaPrevPow2(uint32_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 ^ (v >> 1);
3549 return v;
3550}
3551static inline uint64_t VmaPrevPow2(uint64_t v)
3552{
3553 v |= v >> 1;
3554 v |= v >> 2;
3555 v |= v >> 4;
3556 v |= v >> 8;
3557 v |= v >> 16;
3558 v |= v >> 32;
3559 v = v ^ (v >> 1);
3560 return v;
3561}
3562
3563static inline bool VmaStrIsEmpty(const char* pStr)
3564{
3565 return pStr == VMA_NULL || *pStr == '\0';
3566}
3567
3568static const char* VmaAlgorithmToStr(uint32_t algorithm)
3569{
3570 switch(algorithm)
3571 {
3572 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
3573 return "Linear";
3574 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
3575 return "Buddy";
3576 case 0:
3577 return "Default";
3578 default:
3579 VMA_ASSERT(0);
3580 return "";
3581 }
3582}
3583
3584#ifndef VMA_SORT
3585
3586template<typename Iterator, typename Compare>
3587Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
3588{
3589 Iterator centerValue = end; --centerValue;
3590 Iterator insertIndex = beg;
3591 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
3592 {
3593 if(cmp(*memTypeIndex, *centerValue))
3594 {
3595 if(insertIndex != memTypeIndex)
3596 {
3597 VMA_SWAP(*memTypeIndex, *insertIndex);
3598 }
3599 ++insertIndex;
3600 }
3601 }
3602 if(insertIndex != centerValue)
3603 {
3604 VMA_SWAP(*insertIndex, *centerValue);
3605 }
3606 return insertIndex;
3607}
3608
3609template<typename Iterator, typename Compare>
3610void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
3611{
3612 if(beg < end)
3613 {
3614 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
3615 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
3616 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
3617 }
3618}
3619
3620#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
3621
3622#endif // #ifndef VMA_SORT
3623
3624/*
3625Returns true if two memory blocks occupy overlapping pages.
3626ResourceA must be in less memory offset than ResourceB.
3627
3628Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3629chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3630*/
3631static inline bool VmaBlocksOnSamePage(
3632 VkDeviceSize resourceAOffset,
3633 VkDeviceSize resourceASize,
3634 VkDeviceSize resourceBOffset,
3635 VkDeviceSize pageSize)
3636{
3637 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3638 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3639 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3640 VkDeviceSize resourceBStart = resourceBOffset;
3641 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3642 return resourceAEndPage == resourceBStartPage;
3643}
3644
3645enum VmaSuballocationType
3646{
3647 VMA_SUBALLOCATION_TYPE_FREE = 0,
3648 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3649 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3650 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3651 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3652 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3653 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3654};
3655
3656/*
3657Returns true if given suballocation types could conflict and must respect
3658VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3659or linear image and another one is optimal image. If type is unknown, behave
3660conservatively.
3661*/
3662static inline bool VmaIsBufferImageGranularityConflict(
3663 VmaSuballocationType suballocType1,
3664 VmaSuballocationType suballocType2)
3665{
3666 if(suballocType1 > suballocType2)
3667 {
3668 VMA_SWAP(suballocType1, suballocType2);
3669 }
3670
3671 switch(suballocType1)
3672 {
3673 case VMA_SUBALLOCATION_TYPE_FREE:
3674 return false;
3675 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3676 return true;
3677 case VMA_SUBALLOCATION_TYPE_BUFFER:
3678 return
3679 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3680 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3681 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3682 return
3683 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3684 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3685 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3686 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3687 return
3688 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3689 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3690 return false;
3691 default:
3692 VMA_ASSERT(0);
3693 return true;
3694 }
3695}
3696
3697static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3698{
3699 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3700 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3701 for(size_t i = 0; i < numberCount; ++i, ++pDst)
3702 {
3703 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3704 }
3705}
3706
3707static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3708{
3709 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3710 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3711 for(size_t i = 0; i < numberCount; ++i, ++pSrc)
3712 {
3713 if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3714 {
3715 return false;
3716 }
3717 }
3718 return true;
3719}
3720
3721// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3722struct VmaMutexLock
3723{
3724 VMA_CLASS_NO_COPY(VmaMutexLock)
3725public:
3726 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
3727 m_pMutex(useMutex ? &mutex : VMA_NULL)
3728 { if(m_pMutex) { m_pMutex->Lock(); } }
3729 ~VmaMutexLock()
3730 { if(m_pMutex) { m_pMutex->Unlock(); } }
3731private:
3732 VMA_MUTEX* m_pMutex;
3733};
3734
3735// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3736struct VmaMutexLockRead
3737{
3738 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3739public:
3740 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3741 m_pMutex(useMutex ? &mutex : VMA_NULL)
3742 { if(m_pMutex) { m_pMutex->LockRead(); } }
3743 ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
3744private:
3745 VMA_RW_MUTEX* m_pMutex;
3746};
3747
3748// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3749struct VmaMutexLockWrite
3750{
3751 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3752public:
3753 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
3754 m_pMutex(useMutex ? &mutex : VMA_NULL)
3755 { if(m_pMutex) { m_pMutex->LockWrite(); } }
3756 ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
3757private:
3758 VMA_RW_MUTEX* m_pMutex;
3759};
3760
3761#if VMA_DEBUG_GLOBAL_MUTEX
3762 static VMA_MUTEX gDebugGlobalMutex;
3763 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3764#else
3765 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3766#endif
3767
3768// Minimum size of a free suballocation to register it in the free suballocation collection.
3769static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
3770
3771/*
3772Performs binary search and returns iterator to first element that is greater or
3773equal to (key), according to comparison (cmp).
3774
3775Cmp should return true if first argument is less than second argument.
3776
3777Returned value is the found element, if present in the collection or place where
3778new element with value (key) should be inserted.
3779*/
3780template <typename CmpLess, typename IterT, typename KeyT>
3781static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
3782{
3783 size_t down = 0, up = (end - beg);
3784 while(down < up)
3785 {
3786 const size_t mid = (down + up) / 2;
3787 if(cmp(*(beg+mid), key))
3788 {
3789 down = mid + 1;
3790 }
3791 else
3792 {
3793 up = mid;
3794 }
3795 }
3796 return beg + down;
3797}
3798
3799/*
3800Returns true if all pointers in the array are not-null and unique.
3801Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3802T must be pointer type, e.g. VmaAllocation, VmaPool.
3803*/
3804template<typename T>
3805static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3806{
3807 for(uint32_t i = 0; i < count; ++i)
3808 {
3809 const T iPtr = arr[i];
3810 if(iPtr == VMA_NULL)
3811 {
3812 return false;
3813 }
3814 for(uint32_t j = i + 1; j < count; ++j)
3815 {
3816 if(iPtr == arr[j])
3817 {
3818 return false;
3819 }
3820 }
3821 }
3822 return true;
3823}
3824
3825////////////////////////////////////////////////////////////////////////////////
3826// Memory allocation
3827
3828static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3829{
3830 if((pAllocationCallbacks != VMA_NULL) &&
3831 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3832 {
3833 return (*pAllocationCallbacks->pfnAllocation)(
3834 pAllocationCallbacks->pUserData,
3835 size,
3836 alignment,
3837 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3838 }
3839 else
3840 {
3841 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3842 }
3843}
3844
3845static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3846{
3847 if((pAllocationCallbacks != VMA_NULL) &&
3848 (pAllocationCallbacks->pfnFree != VMA_NULL))
3849 {
3850 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3851 }
3852 else
3853 {
3854 VMA_SYSTEM_FREE(ptr);
3855 }
3856}
3857
3858template<typename T>
3859static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3860{
3861 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3862}
3863
3864template<typename T>
3865static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3866{
3867 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3868}
3869
3870#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3871
3872#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3873
3874template<typename T>
3875static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3876{
3877 ptr->~T();
3878 VmaFree(pAllocationCallbacks, ptr);
3879}
3880
3881template<typename T>
3882static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3883{
3884 if(ptr != VMA_NULL)
3885 {
3886 for(size_t i = count; i--; )
3887 {
3888 ptr[i].~T();
3889 }
3890 VmaFree(pAllocationCallbacks, ptr);
3891 }
3892}
3893
3894// STL-compatible allocator.
3895template<typename T>
3896class VmaStlAllocator
3897{
3898public:
3899 const VkAllocationCallbacks* const m_pCallbacks;
3900 typedef T value_type;
3901
3902 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
3903 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
3904
3905 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
3906 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
3907
3908 template<typename U>
3909 bool operator==(const VmaStlAllocator<U>& rhs) const
3910 {
3911 return m_pCallbacks == rhs.m_pCallbacks;
3912 }
3913 template<typename U>
3914 bool operator!=(const VmaStlAllocator<U>& rhs) const
3915 {
3916 return m_pCallbacks != rhs.m_pCallbacks;
3917 }
3918
3919 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
3920};
3921
3922#if VMA_USE_STL_VECTOR
3923
3924#define VmaVector std::vector
3925
3926template<typename T, typename allocatorT>
3927static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
3928{
3929 vec.insert(vec.begin() + index, item);
3930}
3931
3932template<typename T, typename allocatorT>
3933static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
3934{
3935 vec.erase(vec.begin() + index);
3936}
3937
3938#else // #if VMA_USE_STL_VECTOR
3939
3940/* Class with interface compatible with subset of std::vector.
3941T must be POD because constructors and destructors are not called and memcpy is
3942used for these objects. */
3943template<typename T, typename AllocatorT>
3944class VmaVector
3945{
3946public:
3947 typedef T value_type;
3948
3949 VmaVector(const AllocatorT& allocator) :
3950 m_Allocator(allocator),
3951 m_pArray(VMA_NULL),
3952 m_Count(0),
3953 m_Capacity(0)
3954 {
3955 }
3956
3957 VmaVector(size_t count, const AllocatorT& allocator) :
3958 m_Allocator(allocator),
3959 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
3960 m_Count(count),
3961 m_Capacity(count)
3962 {
3963 }
3964
3965 VmaVector(const VmaVector<T, AllocatorT>& src) :
3966 m_Allocator(src.m_Allocator),
3967 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
3968 m_Count(src.m_Count),
3969 m_Capacity(src.m_Count)
3970 {
3971 if(m_Count != 0)
3972 {
3973 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
3974 }
3975 }
3976
3977 ~VmaVector()
3978 {
3979 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
3980 }
3981
3982 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
3983 {
3984 if(&rhs != this)
3985 {
3986 resize(rhs.m_Count);
3987 if(m_Count != 0)
3988 {
3989 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
3990 }
3991 }
3992 return *this;
3993 }
3994
3995 bool empty() const { return m_Count == 0; }
3996 size_t size() const { return m_Count; }
3997 T* data() { return m_pArray; }
3998 const T* data() const { return m_pArray; }
3999
4000 T& operator[](size_t index)
4001 {
4002 VMA_HEAVY_ASSERT(index < m_Count);
4003 return m_pArray[index];
4004 }
4005 const T& operator[](size_t index) const
4006 {
4007 VMA_HEAVY_ASSERT(index < m_Count);
4008 return m_pArray[index];
4009 }
4010
4011 T& front()
4012 {
4013 VMA_HEAVY_ASSERT(m_Count > 0);
4014 return m_pArray[0];
4015 }
4016 const T& front() const
4017 {
4018 VMA_HEAVY_ASSERT(m_Count > 0);
4019 return m_pArray[0];
4020 }
4021 T& back()
4022 {
4023 VMA_HEAVY_ASSERT(m_Count > 0);
4024 return m_pArray[m_Count - 1];
4025 }
4026 const T& back() const
4027 {
4028 VMA_HEAVY_ASSERT(m_Count > 0);
4029 return m_pArray[m_Count - 1];
4030 }
4031
4032 void reserve(size_t newCapacity, bool freeMemory = false)
4033 {
4034 newCapacity = VMA_MAX(newCapacity, m_Count);
4035
4036 if((newCapacity < m_Capacity) && !freeMemory)
4037 {
4038 newCapacity = m_Capacity;
4039 }
4040
4041 if(newCapacity != m_Capacity)
4042 {
4043 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4044 if(m_Count != 0)
4045 {
4046 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4047 }
4048 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4049 m_Capacity = newCapacity;
4050 m_pArray = newArray;
4051 }
4052 }
4053
4054 void resize(size_t newCount, bool freeMemory = false)
4055 {
4056 size_t newCapacity = m_Capacity;
4057 if(newCount > m_Capacity)
4058 {
4059 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4060 }
4061 else if(freeMemory)
4062 {
4063 newCapacity = newCount;
4064 }
4065
4066 if(newCapacity != m_Capacity)
4067 {
4068 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4069 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4070 if(elementsToCopy != 0)
4071 {
4072 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4073 }
4074 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4075 m_Capacity = newCapacity;
4076 m_pArray = newArray;
4077 }
4078
4079 m_Count = newCount;
4080 }
4081
4082 void clear(bool freeMemory = false)
4083 {
4084 resize(0, freeMemory);
4085 }
4086
4087 void insert(size_t index, const T& src)
4088 {
4089 VMA_HEAVY_ASSERT(index <= m_Count);
4090 const size_t oldCount = size();
4091 resize(oldCount + 1);
4092 if(index < oldCount)
4093 {
4094 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4095 }
4096 m_pArray[index] = src;
4097 }
4098
4099 void remove(size_t index)
4100 {
4101 VMA_HEAVY_ASSERT(index < m_Count);
4102 const size_t oldCount = size();
4103 if(index < oldCount - 1)
4104 {
4105 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4106 }
4107 resize(oldCount - 1);
4108 }
4109
4110 void push_back(const T& src)
4111 {
4112 const size_t newIndex = size();
4113 resize(newIndex + 1);
4114 m_pArray[newIndex] = src;
4115 }
4116
4117 void pop_back()
4118 {
4119 VMA_HEAVY_ASSERT(m_Count > 0);
4120 resize(size() - 1);
4121 }
4122
4123 void push_front(const T& src)
4124 {
4125 insert(0, src);
4126 }
4127
4128 void pop_front()
4129 {
4130 VMA_HEAVY_ASSERT(m_Count > 0);
4131 remove(0);
4132 }
4133
4134 typedef T* iterator;
4135
4136 iterator begin() { return m_pArray; }
4137 iterator end() { return m_pArray + m_Count; }
4138
4139private:
4140 AllocatorT m_Allocator;
4141 T* m_pArray;
4142 size_t m_Count;
4143 size_t m_Capacity;
4144};
4145
4146template<typename T, typename allocatorT>
4147static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4148{
4149 vec.insert(index, item);
4150}
4151
4152template<typename T, typename allocatorT>
4153static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4154{
4155 vec.remove(index);
4156}
4157
4158#endif // #if VMA_USE_STL_VECTOR
4159
4160template<typename CmpLess, typename VectorT>
4161size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
4162{
4163 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4164 vector.data(),
4165 vector.data() + vector.size(),
4166 value,
4167 CmpLess()) - vector.data();
4168 VmaVectorInsert(vector, indexToInsert, value);
4169 return indexToInsert;
4170}
4171
4172template<typename CmpLess, typename VectorT>
4173bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
4174{
4175 CmpLess comparator;
4176 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
4177 vector.begin(),
4178 vector.end(),
4179 value,
4180 comparator);
4181 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
4182 {
4183 size_t indexToRemove = it - vector.begin();
4184 VmaVectorRemove(vector, indexToRemove);
4185 return true;
4186 }
4187 return false;
4188}
4189
4190template<typename CmpLess, typename IterT, typename KeyT>
4191IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
4192{
4193 CmpLess comparator;
4194 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
4195 beg, end, value, comparator);
4196 if(it == end ||
4197 (!comparator(*it, value) && !comparator(value, *it)))
4198 {
4199 return it;
4200 }
4201 return end;
4202}
4203
4204////////////////////////////////////////////////////////////////////////////////
4205// class VmaPoolAllocator
4206
4207/*
4208Allocator for objects of type T using a list of arrays (pools) to speed up
4209allocation. Number of elements that can be allocated is not bounded because
4210allocator can create multiple blocks.
4211*/
4212template<typename T>
4213class VmaPoolAllocator
4214{
4215 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4216public:
4217 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
4218 ~VmaPoolAllocator();
4219 void Clear();
4220 T* Alloc();
4221 void Free(T* ptr);
4222
4223private:
4224 union Item
4225 {
4226 uint32_t NextFreeIndex;
4227 T Value;
4228 };
4229
4230 struct ItemBlock
4231 {
4232 Item* pItems;
4233 uint32_t FirstFreeIndex;
4234 };
4235
4236 const VkAllocationCallbacks* m_pAllocationCallbacks;
4237 size_t m_ItemsPerBlock;
4238 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
4239
4240 ItemBlock& CreateNewBlock();
4241};
4242
4243template<typename T>
4244VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
4245 m_pAllocationCallbacks(pAllocationCallbacks),
4246 m_ItemsPerBlock(itemsPerBlock),
4247 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4248{
4249 VMA_ASSERT(itemsPerBlock > 0);
4250}
4251
4252template<typename T>
4253VmaPoolAllocator<T>::~VmaPoolAllocator()
4254{
4255 Clear();
4256}
4257
4258template<typename T>
4259void VmaPoolAllocator<T>::Clear()
4260{
4261 for(size_t i = m_ItemBlocks.size(); i--; )
4262 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
4263 m_ItemBlocks.clear();
4264}
4265
4266template<typename T>
4267T* VmaPoolAllocator<T>::Alloc()
4268{
4269 for(size_t i = m_ItemBlocks.size(); i--; )
4270 {
4271 ItemBlock& block = m_ItemBlocks[i];
4272 // This block has some free items: Use first one.
4273 if(block.FirstFreeIndex != UINT32_MAX)
4274 {
4275 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4276 block.FirstFreeIndex = pItem->NextFreeIndex;
4277 return &pItem->Value;
4278 }
4279 }
4280
4281 // No block has free item: Create new one and use it.
4282 ItemBlock& newBlock = CreateNewBlock();
4283 Item* const pItem = &newBlock.pItems[0];
4284 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4285 return &pItem->Value;
4286}
4287
4288template<typename T>
4289void VmaPoolAllocator<T>::Free(T* ptr)
4290{
4291 // Search all memory blocks to find ptr.
4292 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
4293 {
4294 ItemBlock& block = m_ItemBlocks[i];
4295
4296 // Casting to union.
4297 Item* pItemPtr;
4298 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4299
4300 // Check if pItemPtr is in address range of this block.
4301 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
4302 {
4303 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4304 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4305 block.FirstFreeIndex = index;
4306 return;
4307 }
4308 }
4309 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4310}
4311
4312template<typename T>
4313typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4314{
4315 ItemBlock newBlock = {
4316 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
4317
4318 m_ItemBlocks.push_back(newBlock);
4319
4320 // Setup singly-linked list of all free items in this block.
4321 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
4322 newBlock.pItems[i].NextFreeIndex = i + 1;
4323 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
4324 return m_ItemBlocks.back();
4325}
4326
4327////////////////////////////////////////////////////////////////////////////////
4328// class VmaRawList, VmaList
4329
4330#if VMA_USE_STL_LIST
4331
4332#define VmaList std::list
4333
4334#else // #if VMA_USE_STL_LIST
4335
4336template<typename T>
4337struct VmaListItem
4338{
4339 VmaListItem* pPrev;
4340 VmaListItem* pNext;
4341 T Value;
4342};
4343
4344// Doubly linked list.
4345template<typename T>
4346class VmaRawList
4347{
4348 VMA_CLASS_NO_COPY(VmaRawList)
4349public:
4350 typedef VmaListItem<T> ItemType;
4351
4352 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4353 ~VmaRawList();
4354 void Clear();
4355
4356 size_t GetCount() const { return m_Count; }
4357 bool IsEmpty() const { return m_Count == 0; }
4358
4359 ItemType* Front() { return m_pFront; }
4360 const ItemType* Front() const { return m_pFront; }
4361 ItemType* Back() { return m_pBack; }
4362 const ItemType* Back() const { return m_pBack; }
4363
4364 ItemType* PushBack();
4365 ItemType* PushFront();
4366 ItemType* PushBack(const T& value);
4367 ItemType* PushFront(const T& value);
4368 void PopBack();
4369 void PopFront();
4370
4371 // Item can be null - it means PushBack.
4372 ItemType* InsertBefore(ItemType* pItem);
4373 // Item can be null - it means PushFront.
4374 ItemType* InsertAfter(ItemType* pItem);
4375
4376 ItemType* InsertBefore(ItemType* pItem, const T& value);
4377 ItemType* InsertAfter(ItemType* pItem, const T& value);
4378
4379 void Remove(ItemType* pItem);
4380
4381private:
4382 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4383 VmaPoolAllocator<ItemType> m_ItemAllocator;
4384 ItemType* m_pFront;
4385 ItemType* m_pBack;
4386 size_t m_Count;
4387};
4388
4389template<typename T>
4390VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
4391 m_pAllocationCallbacks(pAllocationCallbacks),
4392 m_ItemAllocator(pAllocationCallbacks, 128),
4393 m_pFront(VMA_NULL),
4394 m_pBack(VMA_NULL),
4395 m_Count(0)
4396{
4397}
4398
4399template<typename T>
4400VmaRawList<T>::~VmaRawList()
4401{
4402 // Intentionally not calling Clear, because that would be unnecessary
4403 // computations to return all items to m_ItemAllocator as free.
4404}
4405
4406template<typename T>
4407void VmaRawList<T>::Clear()
4408{
4409 if(IsEmpty() == false)
4410 {
4411 ItemType* pItem = m_pBack;
4412 while(pItem != VMA_NULL)
4413 {
4414 ItemType* const pPrevItem = pItem->pPrev;
4415 m_ItemAllocator.Free(pItem);
4416 pItem = pPrevItem;
4417 }
4418 m_pFront = VMA_NULL;
4419 m_pBack = VMA_NULL;
4420 m_Count = 0;
4421 }
4422}
4423
4424template<typename T>
4425VmaListItem<T>* VmaRawList<T>::PushBack()
4426{
4427 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4428 pNewItem->pNext = VMA_NULL;
4429 if(IsEmpty())
4430 {
4431 pNewItem->pPrev = VMA_NULL;
4432 m_pFront = pNewItem;
4433 m_pBack = pNewItem;
4434 m_Count = 1;
4435 }
4436 else
4437 {
4438 pNewItem->pPrev = m_pBack;
4439 m_pBack->pNext = pNewItem;
4440 m_pBack = pNewItem;
4441 ++m_Count;
4442 }
4443 return pNewItem;
4444}
4445
4446template<typename T>
4447VmaListItem<T>* VmaRawList<T>::PushFront()
4448{
4449 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4450 pNewItem->pPrev = VMA_NULL;
4451 if(IsEmpty())
4452 {
4453 pNewItem->pNext = VMA_NULL;
4454 m_pFront = pNewItem;
4455 m_pBack = pNewItem;
4456 m_Count = 1;
4457 }
4458 else
4459 {
4460 pNewItem->pNext = m_pFront;
4461 m_pFront->pPrev = pNewItem;
4462 m_pFront = pNewItem;
4463 ++m_Count;
4464 }
4465 return pNewItem;
4466}
4467
4468template<typename T>
4469VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4470{
4471 ItemType* const pNewItem = PushBack();
4472 pNewItem->Value = value;
4473 return pNewItem;
4474}
4475
4476template<typename T>
4477VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4478{
4479 ItemType* const pNewItem = PushFront();
4480 pNewItem->Value = value;
4481 return pNewItem;
4482}
4483
4484template<typename T>
4485void VmaRawList<T>::PopBack()
4486{
4487 VMA_HEAVY_ASSERT(m_Count > 0);
4488 ItemType* const pBackItem = m_pBack;
4489 ItemType* const pPrevItem = pBackItem->pPrev;
4490 if(pPrevItem != VMA_NULL)
4491 {
4492 pPrevItem->pNext = VMA_NULL;
4493 }
4494 m_pBack = pPrevItem;
4495 m_ItemAllocator.Free(pBackItem);
4496 --m_Count;
4497}
4498
4499template<typename T>
4500void VmaRawList<T>::PopFront()
4501{
4502 VMA_HEAVY_ASSERT(m_Count > 0);
4503 ItemType* const pFrontItem = m_pFront;
4504 ItemType* const pNextItem = pFrontItem->pNext;
4505 if(pNextItem != VMA_NULL)
4506 {
4507 pNextItem->pPrev = VMA_NULL;
4508 }
4509 m_pFront = pNextItem;
4510 m_ItemAllocator.Free(pFrontItem);
4511 --m_Count;
4512}
4513
4514template<typename T>
4515void VmaRawList<T>::Remove(ItemType* pItem)
4516{
4517 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4518 VMA_HEAVY_ASSERT(m_Count > 0);
4519
4520 if(pItem->pPrev != VMA_NULL)
4521 {
4522 pItem->pPrev->pNext = pItem->pNext;
4523 }
4524 else
4525 {
4526 VMA_HEAVY_ASSERT(m_pFront == pItem);
4527 m_pFront = pItem->pNext;
4528 }
4529
4530 if(pItem->pNext != VMA_NULL)
4531 {
4532 pItem->pNext->pPrev = pItem->pPrev;
4533 }
4534 else
4535 {
4536 VMA_HEAVY_ASSERT(m_pBack == pItem);
4537 m_pBack = pItem->pPrev;
4538 }
4539
4540 m_ItemAllocator.Free(pItem);
4541 --m_Count;
4542}
4543
4544template<typename T>
4545VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4546{
4547 if(pItem != VMA_NULL)
4548 {
4549 ItemType* const prevItem = pItem->pPrev;
4550 ItemType* const newItem = m_ItemAllocator.Alloc();
4551 newItem->pPrev = prevItem;
4552 newItem->pNext = pItem;
4553 pItem->pPrev = newItem;
4554 if(prevItem != VMA_NULL)
4555 {
4556 prevItem->pNext = newItem;
4557 }
4558 else
4559 {
4560 VMA_HEAVY_ASSERT(m_pFront == pItem);
4561 m_pFront = newItem;
4562 }
4563 ++m_Count;
4564 return newItem;
4565 }
4566 else
4567 return PushBack();
4568}
4569
4570template<typename T>
4571VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4572{
4573 if(pItem != VMA_NULL)
4574 {
4575 ItemType* const nextItem = pItem->pNext;
4576 ItemType* const newItem = m_ItemAllocator.Alloc();
4577 newItem->pNext = nextItem;
4578 newItem->pPrev = pItem;
4579 pItem->pNext = newItem;
4580 if(nextItem != VMA_NULL)
4581 {
4582 nextItem->pPrev = newItem;
4583 }
4584 else
4585 {
4586 VMA_HEAVY_ASSERT(m_pBack == pItem);
4587 m_pBack = newItem;
4588 }
4589 ++m_Count;
4590 return newItem;
4591 }
4592 else
4593 return PushFront();
4594}
4595
4596template<typename T>
4597VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4598{
4599 ItemType* const newItem = InsertBefore(pItem);
4600 newItem->Value = value;
4601 return newItem;
4602}
4603
4604template<typename T>
4605VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4606{
4607 ItemType* const newItem = InsertAfter(pItem);
4608 newItem->Value = value;
4609 return newItem;
4610}
4611
4612template<typename T, typename AllocatorT>
4613class VmaList
4614{
4615 VMA_CLASS_NO_COPY(VmaList)
4616public:
4617 class iterator
4618 {
4619 public:
4620 iterator() :
4621 m_pList(VMA_NULL),
4622 m_pItem(VMA_NULL)
4623 {
4624 }
4625
4626 T& operator*() const
4627 {
4628 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4629 return m_pItem->Value;
4630 }
4631 T* operator->() const
4632 {
4633 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4634 return &m_pItem->Value;
4635 }
4636
4637 iterator& operator++()
4638 {
4639 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4640 m_pItem = m_pItem->pNext;
4641 return *this;
4642 }
4643 iterator& operator--()
4644 {
4645 if(m_pItem != VMA_NULL)
4646 {
4647 m_pItem = m_pItem->pPrev;
4648 }
4649 else
4650 {
4651 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4652 m_pItem = m_pList->Back();
4653 }
4654 return *this;
4655 }
4656
4657 iterator operator++(int)
4658 {
4659 iterator result = *this;
4660 ++*this;
4661 return result;
4662 }
4663 iterator operator--(int)
4664 {
4665 iterator result = *this;
4666 --*this;
4667 return result;
4668 }
4669
4670 bool operator==(const iterator& rhs) const
4671 {
4672 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4673 return m_pItem == rhs.m_pItem;
4674 }
4675 bool operator!=(const iterator& rhs) const
4676 {
4677 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4678 return m_pItem != rhs.m_pItem;
4679 }
4680
4681 private:
4682 VmaRawList<T>* m_pList;
4683 VmaListItem<T>* m_pItem;
4684
4685 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
4686 m_pList(pList),
4687 m_pItem(pItem)
4688 {
4689 }
4690
4691 friend class VmaList<T, AllocatorT>;
4692 };
4693
4694 class const_iterator
4695 {
4696 public:
4697 const_iterator() :
4698 m_pList(VMA_NULL),
4699 m_pItem(VMA_NULL)
4700 {
4701 }
4702
4703 const_iterator(const iterator& src) :
4704 m_pList(src.m_pList),
4705 m_pItem(src.m_pItem)
4706 {
4707 }
4708
4709 const T& operator*() const
4710 {
4711 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4712 return m_pItem->Value;
4713 }
4714 const T* operator->() const
4715 {
4716 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4717 return &m_pItem->Value;
4718 }
4719
4720 const_iterator& operator++()
4721 {
4722 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
4723 m_pItem = m_pItem->pNext;
4724 return *this;
4725 }
4726 const_iterator& operator--()
4727 {
4728 if(m_pItem != VMA_NULL)
4729 {
4730 m_pItem = m_pItem->pPrev;
4731 }
4732 else
4733 {
4734 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4735 m_pItem = m_pList->Back();
4736 }
4737 return *this;
4738 }
4739
4740 const_iterator operator++(int)
4741 {
4742 const_iterator result = *this;
4743 ++*this;
4744 return result;
4745 }
4746 const_iterator operator--(int)
4747 {
4748 const_iterator result = *this;
4749 --*this;
4750 return result;
4751 }
4752
4753 bool operator==(const const_iterator& rhs) const
4754 {
4755 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4756 return m_pItem == rhs.m_pItem;
4757 }
4758 bool operator!=(const const_iterator& rhs) const
4759 {
4760 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
4761 return m_pItem != rhs.m_pItem;
4762 }
4763
4764 private:
4765 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
4766 m_pList(pList),
4767 m_pItem(pItem)
4768 {
4769 }
4770
4771 const VmaRawList<T>* m_pList;
4772 const VmaListItem<T>* m_pItem;
4773
4774 friend class VmaList<T, AllocatorT>;
4775 };
4776
4777 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
4778
4779 bool empty() const { return m_RawList.IsEmpty(); }
4780 size_t size() const { return m_RawList.GetCount(); }
4781
4782 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
4783 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4784
4785 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
4786 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4787
4788 void clear() { m_RawList.Clear(); }
4789 void push_back(const T& value) { m_RawList.PushBack(value); }
4790 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4791 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4792
4793private:
4794 VmaRawList<T> m_RawList;
4795};
4796
4797#endif // #if VMA_USE_STL_LIST
4798
4799////////////////////////////////////////////////////////////////////////////////
4800// class VmaMap
4801
4802// Unused in this version.
4803#if 0
4804
4805#if VMA_USE_STL_UNORDERED_MAP
4806
4807#define VmaPair std::pair
4808
4809#define VMA_MAP_TYPE(KeyT, ValueT) \
4810 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
4811
4812#else // #if VMA_USE_STL_UNORDERED_MAP
4813
4814template<typename T1, typename T2>
4815struct VmaPair
4816{
4817 T1 first;
4818 T2 second;
4819
4820 VmaPair() : first(), second() { }
4821 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
4822};
4823
4824/* Class compatible with subset of interface of std::unordered_map.
4825KeyT, ValueT must be POD because they will be stored in VmaVector.
4826*/
4827template<typename KeyT, typename ValueT>
4828class VmaMap
4829{
4830public:
4831 typedef VmaPair<KeyT, ValueT> PairType;
4832 typedef PairType* iterator;
4833
4834 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
4835
4836 iterator begin() { return m_Vector.begin(); }
4837 iterator end() { return m_Vector.end(); }
4838
4839 void insert(const PairType& pair);
4840 iterator find(const KeyT& key);
4841 void erase(iterator it);
4842
4843private:
4844 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
4845};
4846
4847#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
4848
4849template<typename FirstT, typename SecondT>
4850struct VmaPairFirstLess
4851{
4852 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
4853 {
4854 return lhs.first < rhs.first;
4855 }
4856 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
4857 {
4858 return lhs.first < rhsFirst;
4859 }
4860};
4861
4862template<typename KeyT, typename ValueT>
4863void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
4864{
4865 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
4866 m_Vector.data(),
4867 m_Vector.data() + m_Vector.size(),
4868 pair,
4869 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
4870 VmaVectorInsert(m_Vector, indexToInsert, pair);
4871}
4872
4873template<typename KeyT, typename ValueT>
4874VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
4875{
4876 PairType* it = VmaBinaryFindFirstNotLess(
4877 m_Vector.data(),
4878 m_Vector.data() + m_Vector.size(),
4879 key,
4880 VmaPairFirstLess<KeyT, ValueT>());
4881 if((it != m_Vector.end()) && (it->first == key))
4882 {
4883 return it;
4884 }
4885 else
4886 {
4887 return m_Vector.end();
4888 }
4889}
4890
4891template<typename KeyT, typename ValueT>
4892void VmaMap<KeyT, ValueT>::erase(iterator it)
4893{
4894 VmaVectorRemove(m_Vector, it - m_Vector.begin());
4895}
4896
4897#endif // #if VMA_USE_STL_UNORDERED_MAP
4898
4899#endif // #if 0
4900
4901////////////////////////////////////////////////////////////////////////////////
4902
4903class VmaDeviceMemoryBlock;
4904
4905enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
4906
4907struct VmaAllocation_T
4908{
4909 VMA_CLASS_NO_COPY(VmaAllocation_T)
4910private:
4911 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
4912
4913 enum FLAGS
4914 {
4915 FLAG_USER_DATA_STRING = 0x01,
4916 };
4917
4918public:
4919 enum ALLOCATION_TYPE
4920 {
4921 ALLOCATION_TYPE_NONE,
4922 ALLOCATION_TYPE_BLOCK,
4923 ALLOCATION_TYPE_DEDICATED,
4924 };
4925
4926 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
4927 m_Alignment(1),
4928 m_Size(0),
4929 m_pUserData(VMA_NULL),
4930 m_LastUseFrameIndex(currentFrameIndex),
4931 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
4932 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
4933 m_MapCount(0),
4934 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
4935 {
4936#if VMA_STATS_STRING_ENABLED
4937 m_CreationFrameIndex = currentFrameIndex;
4938 m_BufferImageUsage = 0;
4939#endif
4940 }
4941
4942 ~VmaAllocation_T()
4943 {
4944 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
4945
4946 // Check if owned string was freed.
4947 VMA_ASSERT(m_pUserData == VMA_NULL);
4948 }
4949
4950 void InitBlockAllocation(
4951 VmaPool hPool,
4952 VmaDeviceMemoryBlock* block,
4953 VkDeviceSize offset,
4954 VkDeviceSize alignment,
4955 VkDeviceSize size,
4956 VmaSuballocationType suballocationType,
4957 bool mapped,
4958 bool canBecomeLost)
4959 {
4960 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4961 VMA_ASSERT(block != VMA_NULL);
4962 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4963 m_Alignment = alignment;
4964 m_Size = size;
4965 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
4966 m_SuballocationType = (uint8_t)suballocationType;
4967 m_BlockAllocation.m_hPool = hPool;
4968 m_BlockAllocation.m_Block = block;
4969 m_BlockAllocation.m_Offset = offset;
4970 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
4971 }
4972
4973 void InitLost()
4974 {
4975 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
4976 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
4977 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
4978 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
4979 m_BlockAllocation.m_Block = VMA_NULL;
4980 m_BlockAllocation.m_Offset = 0;
4981 m_BlockAllocation.m_CanBecomeLost = true;
4982 }
4983
4984 void ChangeBlockAllocation(
4985 VmaAllocator hAllocator,
4986 VmaDeviceMemoryBlock* block,
4987 VkDeviceSize offset);
4988
4989 void ChangeSize(VkDeviceSize newSize);
4990 void ChangeOffset(VkDeviceSize newOffset);
4991
4992 // pMappedData not null means allocation is created with MAPPED flag.
4993 void InitDedicatedAllocation(
4994 uint32_t memoryTypeIndex,
4995 VkDeviceMemory hMemory,
4996 VmaSuballocationType suballocationType,
4997 void* pMappedData,
4998 VkDeviceSize size)
4999 {
5000 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
5001 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
5002 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
5003 m_Alignment = 0;
5004 m_Size = size;
5005 m_SuballocationType = (uint8_t)suballocationType;
5006 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
5007 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
5008 m_DedicatedAllocation.m_hMemory = hMemory;
5009 m_DedicatedAllocation.m_pMappedData = pMappedData;
5010 }
5011
5012 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
5013 VkDeviceSize GetAlignment() const { return m_Alignment; }
5014 VkDeviceSize GetSize() const { return m_Size; }
5015 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
5016 void* GetUserData() const { return m_pUserData; }
5017 void SetUserData(VmaAllocator hAllocator, void* pUserData);
5018 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
5019
5020 VmaDeviceMemoryBlock* GetBlock() const
5021 {
5022 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5023 return m_BlockAllocation.m_Block;
5024 }
5025 VkDeviceSize GetOffset() const;
5026 VkDeviceMemory GetMemory() const;
5027 uint32_t GetMemoryTypeIndex() const;
5028 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
5029 void* GetMappedData() const;
5030 bool CanBecomeLost() const;
5031 VmaPool GetPool() const;
5032
5033 uint32_t GetLastUseFrameIndex() const
5034 {
5035 return m_LastUseFrameIndex.load();
5036 }
5037 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
5038 {
5039 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
5040 }
5041 /*
5042 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
5043 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
5044 - Else, returns false.
5045
5046 If hAllocation is already lost, assert - you should not call it then.
5047 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
5048 */
5049 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5050
5051 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
5052 {
5053 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
5054 outInfo.blockCount = 1;
5055 outInfo.allocationCount = 1;
5056 outInfo.unusedRangeCount = 0;
5057 outInfo.usedBytes = m_Size;
5058 outInfo.unusedBytes = 0;
5059 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
5060 outInfo.unusedRangeSizeMin = UINT64_MAX;
5061 outInfo.unusedRangeSizeMax = 0;
5062 }
5063
5064 void BlockAllocMap();
5065 void BlockAllocUnmap();
5066 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
5067 void DedicatedAllocUnmap(VmaAllocator hAllocator);
5068
5069#if VMA_STATS_STRING_ENABLED
5070 uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
5071 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
5072
5073 void InitBufferImageUsage(uint32_t bufferImageUsage)
5074 {
5075 VMA_ASSERT(m_BufferImageUsage == 0);
5076 m_BufferImageUsage = bufferImageUsage;
5077 }
5078
5079 void PrintParameters(class VmaJsonWriter& json) const;
5080#endif
5081
5082private:
5083 VkDeviceSize m_Alignment;
5084 VkDeviceSize m_Size;
5085 void* m_pUserData;
5086 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
5087 uint8_t m_Type; // ALLOCATION_TYPE
5088 uint8_t m_SuballocationType; // VmaSuballocationType
5089 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
5090 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
5091 uint8_t m_MapCount;
5092 uint8_t m_Flags; // enum FLAGS
5093
5094 // Allocation out of VmaDeviceMemoryBlock.
5095 struct BlockAllocation
5096 {
5097 VmaPool m_hPool; // Null if belongs to general memory.
5098 VmaDeviceMemoryBlock* m_Block;
5099 VkDeviceSize m_Offset;
5100 bool m_CanBecomeLost;
5101 };
5102
5103 // Allocation for an object that has its own private VkDeviceMemory.
5104 struct DedicatedAllocation
5105 {
5106 uint32_t m_MemoryTypeIndex;
5107 VkDeviceMemory m_hMemory;
5108 void* m_pMappedData; // Not null means memory is mapped.
5109 };
5110
5111 union
5112 {
5113 // Allocation out of VmaDeviceMemoryBlock.
5114 BlockAllocation m_BlockAllocation;
5115 // Allocation for an object that has its own private VkDeviceMemory.
5116 DedicatedAllocation m_DedicatedAllocation;
5117 };
5118
5119#if VMA_STATS_STRING_ENABLED
5120 uint32_t m_CreationFrameIndex;
5121 uint32_t m_BufferImageUsage; // 0 if unknown.
5122#endif
5123
5124 void FreeUserDataString(VmaAllocator hAllocator);
5125};
5126
5127/*
5128Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
5129allocated memory block or free.
5130*/
5131struct VmaSuballocation
5132{
5133 VkDeviceSize offset;
5134 VkDeviceSize size;
5135 VmaAllocation hAllocation;
5136 VmaSuballocationType type;
5137};
5138
5139// Comparator for offsets.
5140struct VmaSuballocationOffsetLess
5141{
5142 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5143 {
5144 return lhs.offset < rhs.offset;
5145 }
5146};
5147struct VmaSuballocationOffsetGreater
5148{
5149 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
5150 {
5151 return lhs.offset > rhs.offset;
5152 }
5153};
5154
5155typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
5156
5157// Cost of one additional allocation lost, as equivalent in bytes.
5158static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
5159
5160/*
5161Parameters of planned allocation inside a VmaDeviceMemoryBlock.
5162
5163If canMakeOtherLost was false:
5164- item points to a FREE suballocation.
5165- itemsToMakeLostCount is 0.
5166
5167If canMakeOtherLost was true:
5168- item points to first of sequence of suballocations, which are either FREE,
5169 or point to VmaAllocations that can become lost.
5170- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
5171 the requested allocation to succeed.
5172*/
5173struct VmaAllocationRequest
5174{
5175 VkDeviceSize offset;
5176 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
5177 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
5178 VmaSuballocationList::iterator item;
5179 size_t itemsToMakeLostCount;
5180 void* customData;
5181
5182 VkDeviceSize CalcCost() const
5183 {
5184 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
5185 }
5186};
5187
5188/*
5189Data structure used for bookkeeping of allocations and unused ranges of memory
5190in a single VkDeviceMemory block.
5191*/
5192class VmaBlockMetadata
5193{
5194public:
5195 VmaBlockMetadata(VmaAllocator hAllocator);
5196 virtual ~VmaBlockMetadata() { }
5197 virtual void Init(VkDeviceSize size) { m_Size = size; }
5198
5199 // Validates all data structures inside this object. If not valid, returns false.
5200 virtual bool Validate() const = 0;
5201 VkDeviceSize GetSize() const { return m_Size; }
5202 virtual size_t GetAllocationCount() const = 0;
5203 virtual VkDeviceSize GetSumFreeSize() const = 0;
5204 virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
5205 // Returns true if this block is empty - contains only single free suballocation.
5206 virtual bool IsEmpty() const = 0;
5207
5208 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
5209 // Shouldn't modify blockCount.
5210 virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
5211
5212#if VMA_STATS_STRING_ENABLED
5213 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
5214#endif
5215
5216 // Tries to find a place for suballocation with given parameters inside this block.
5217 // If succeeded, fills pAllocationRequest and returns true.
5218 // If failed, returns false.
5219 virtual bool CreateAllocationRequest(
5220 uint32_t currentFrameIndex,
5221 uint32_t frameInUseCount,
5222 VkDeviceSize bufferImageGranularity,
5223 VkDeviceSize allocSize,
5224 VkDeviceSize allocAlignment,
5225 bool upperAddress,
5226 VmaSuballocationType allocType,
5227 bool canMakeOtherLost,
5228 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
5229 uint32_t strategy,
5230 VmaAllocationRequest* pAllocationRequest) = 0;
5231
5232 virtual bool MakeRequestedAllocationsLost(
5233 uint32_t currentFrameIndex,
5234 uint32_t frameInUseCount,
5235 VmaAllocationRequest* pAllocationRequest) = 0;
5236
5237 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
5238
5239 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
5240
5241 // Makes actual allocation based on request. Request must already be checked and valid.
5242 virtual void Alloc(
5243 const VmaAllocationRequest& request,
5244 VmaSuballocationType type,
5245 VkDeviceSize allocSize,
5246 bool upperAddress,
5247 VmaAllocation hAllocation) = 0;
5248
5249 // Frees suballocation assigned to given memory region.
5250 virtual void Free(const VmaAllocation allocation) = 0;
5251 virtual void FreeAtOffset(VkDeviceSize offset) = 0;
5252
5253 // Tries to resize (grow or shrink) space for given allocation, in place.
5254 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
5255
5256protected:
5257 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
5258
5259#if VMA_STATS_STRING_ENABLED
5260 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
5261 VkDeviceSize unusedBytes,
5262 size_t allocationCount,
5263 size_t unusedRangeCount) const;
5264 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
5265 VkDeviceSize offset,
5266 VmaAllocation hAllocation) const;
5267 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
5268 VkDeviceSize offset,
5269 VkDeviceSize size) const;
5270 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
5271#endif
5272
5273private:
5274 VkDeviceSize m_Size;
5275 const VkAllocationCallbacks* m_pAllocationCallbacks;
5276};
5277
5278#define VMA_VALIDATE(cond) do { if(!(cond)) { \
5279 VMA_ASSERT(0 && "Validation failed: " #cond); \
5280 return false; \
5281 } } while(false)
5282
5283class VmaBlockMetadata_Generic : public VmaBlockMetadata
5284{
5285 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
5286public:
5287 VmaBlockMetadata_Generic(VmaAllocator hAllocator);
5288 virtual ~VmaBlockMetadata_Generic();
5289 virtual void Init(VkDeviceSize size);
5290
5291 virtual bool Validate() const;
5292 virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
5293 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5294 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5295 virtual bool IsEmpty() const;
5296
5297 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5298 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5299
5300#if VMA_STATS_STRING_ENABLED
5301 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5302#endif
5303
5304 virtual bool CreateAllocationRequest(
5305 uint32_t currentFrameIndex,
5306 uint32_t frameInUseCount,
5307 VkDeviceSize bufferImageGranularity,
5308 VkDeviceSize allocSize,
5309 VkDeviceSize allocAlignment,
5310 bool upperAddress,
5311 VmaSuballocationType allocType,
5312 bool canMakeOtherLost,
5313 uint32_t strategy,
5314 VmaAllocationRequest* pAllocationRequest);
5315
5316 virtual bool MakeRequestedAllocationsLost(
5317 uint32_t currentFrameIndex,
5318 uint32_t frameInUseCount,
5319 VmaAllocationRequest* pAllocationRequest);
5320
5321 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5322
5323 virtual VkResult CheckCorruption(const void* pBlockData);
5324
5325 virtual void Alloc(
5326 const VmaAllocationRequest& request,
5327 VmaSuballocationType type,
5328 VkDeviceSize allocSize,
5329 bool upperAddress,
5330 VmaAllocation hAllocation);
5331
5332 virtual void Free(const VmaAllocation allocation);
5333 virtual void FreeAtOffset(VkDeviceSize offset);
5334
5335 virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
5336
5337 ////////////////////////////////////////////////////////////////////////////////
5338 // For defragmentation
5339
5340 bool IsBufferImageGranularityConflictPossible(
5341 VkDeviceSize bufferImageGranularity,
5342 VmaSuballocationType& inOutPrevSuballocType) const;
5343
5344private:
5345 friend class VmaDefragmentationAlgorithm_Generic;
5346 friend class VmaDefragmentationAlgorithm_Fast;
5347
5348 uint32_t m_FreeCount;
5349 VkDeviceSize m_SumFreeSize;
5350 VmaSuballocationList m_Suballocations;
5351 // Suballocations that are free and have size greater than certain threshold.
5352 // Sorted by size, ascending.
5353 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
5354
5355 bool ValidateFreeSuballocationList() const;
5356
5357 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
5358 // If yes, fills pOffset and returns true. If no, returns false.
5359 bool CheckAllocation(
5360 uint32_t currentFrameIndex,
5361 uint32_t frameInUseCount,
5362 VkDeviceSize bufferImageGranularity,
5363 VkDeviceSize allocSize,
5364 VkDeviceSize allocAlignment,
5365 VmaSuballocationType allocType,
5366 VmaSuballocationList::const_iterator suballocItem,
5367 bool canMakeOtherLost,
5368 VkDeviceSize* pOffset,
5369 size_t* itemsToMakeLostCount,
5370 VkDeviceSize* pSumFreeSize,
5371 VkDeviceSize* pSumItemSize) const;
5372 // Given free suballocation, it merges it with following one, which must also be free.
5373 void MergeFreeWithNext(VmaSuballocationList::iterator item);
5374 // Releases given suballocation, making it free.
5375 // Merges it with adjacent free suballocations if applicable.
5376 // Returns iterator to new free suballocation at this place.
5377 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
5378 // Given free suballocation, it inserts it into sorted list of
5379 // m_FreeSuballocationsBySize if it's suitable.
5380 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
5381 // Given free suballocation, it removes it from sorted list of
5382 // m_FreeSuballocationsBySize if it's suitable.
5383 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
5384};
5385
5386/*
5387Allocations and their references in internal data structure look like this:
5388
5389if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
5390
5391 0 +-------+
5392 | |
5393 | |
5394 | |
5395 +-------+
5396 | Alloc | 1st[m_1stNullItemsBeginCount]
5397 +-------+
5398 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5399 +-------+
5400 | ... |
5401 +-------+
5402 | Alloc | 1st[1st.size() - 1]
5403 +-------+
5404 | |
5405 | |
5406 | |
5407GetSize() +-------+
5408
5409if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
5410
5411 0 +-------+
5412 | Alloc | 2nd[0]
5413 +-------+
5414 | Alloc | 2nd[1]
5415 +-------+
5416 | ... |
5417 +-------+
5418 | Alloc | 2nd[2nd.size() - 1]
5419 +-------+
5420 | |
5421 | |
5422 | |
5423 +-------+
5424 | Alloc | 1st[m_1stNullItemsBeginCount]
5425 +-------+
5426 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5427 +-------+
5428 | ... |
5429 +-------+
5430 | Alloc | 1st[1st.size() - 1]
5431 +-------+
5432 | |
5433GetSize() +-------+
5434
5435if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
5436
5437 0 +-------+
5438 | |
5439 | |
5440 | |
5441 +-------+
5442 | Alloc | 1st[m_1stNullItemsBeginCount]
5443 +-------+
5444 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
5445 +-------+
5446 | ... |
5447 +-------+
5448 | Alloc | 1st[1st.size() - 1]
5449 +-------+
5450 | |
5451 | |
5452 | |
5453 +-------+
5454 | Alloc | 2nd[2nd.size() - 1]
5455 +-------+
5456 | ... |
5457 +-------+
5458 | Alloc | 2nd[1]
5459 +-------+
5460 | Alloc | 2nd[0]
5461GetSize() +-------+
5462
5463*/
5464class VmaBlockMetadata_Linear : public VmaBlockMetadata
5465{
5466 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
5467public:
5468 VmaBlockMetadata_Linear(VmaAllocator hAllocator);
5469 virtual ~VmaBlockMetadata_Linear();
5470 virtual void Init(VkDeviceSize size);
5471
5472 virtual bool Validate() const;
5473 virtual size_t GetAllocationCount() const;
5474 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
5475 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5476 virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
5477
5478 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5479 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5480
5481#if VMA_STATS_STRING_ENABLED
5482 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5483#endif
5484
5485 virtual bool CreateAllocationRequest(
5486 uint32_t currentFrameIndex,
5487 uint32_t frameInUseCount,
5488 VkDeviceSize bufferImageGranularity,
5489 VkDeviceSize allocSize,
5490 VkDeviceSize allocAlignment,
5491 bool upperAddress,
5492 VmaSuballocationType allocType,
5493 bool canMakeOtherLost,
5494 uint32_t strategy,
5495 VmaAllocationRequest* pAllocationRequest);
5496
5497 virtual bool MakeRequestedAllocationsLost(
5498 uint32_t currentFrameIndex,
5499 uint32_t frameInUseCount,
5500 VmaAllocationRequest* pAllocationRequest);
5501
5502 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5503
5504 virtual VkResult CheckCorruption(const void* pBlockData);
5505
5506 virtual void Alloc(
5507 const VmaAllocationRequest& request,
5508 VmaSuballocationType type,
5509 VkDeviceSize allocSize,
5510 bool upperAddress,
5511 VmaAllocation hAllocation);
5512
5513 virtual void Free(const VmaAllocation allocation);
5514 virtual void FreeAtOffset(VkDeviceSize offset);
5515
5516private:
5517 /*
5518 There are two suballocation vectors, used in ping-pong way.
5519 The one with index m_1stVectorIndex is called 1st.
5520 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
5521 2nd can be non-empty only when 1st is not empty.
5522 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
5523 */
5524 typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
5525
5526 enum SECOND_VECTOR_MODE
5527 {
5528 SECOND_VECTOR_EMPTY,
5529 /*
5530 Suballocations in 2nd vector are created later than the ones in 1st, but they
5531 all have smaller offset.
5532 */
5533 SECOND_VECTOR_RING_BUFFER,
5534 /*
5535 Suballocations in 2nd vector are upper side of double stack.
5536 They all have offsets higher than those in 1st vector.
5537 Top of this stack means smaller offsets, but higher indices in this vector.
5538 */
5539 SECOND_VECTOR_DOUBLE_STACK,
5540 };
5541
5542 VkDeviceSize m_SumFreeSize;
5543 SuballocationVectorType m_Suballocations0, m_Suballocations1;
5544 uint32_t m_1stVectorIndex;
5545 SECOND_VECTOR_MODE m_2ndVectorMode;
5546
5547 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5548 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5549 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
5550 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
5551
5552 // Number of items in 1st vector with hAllocation = null at the beginning.
5553 size_t m_1stNullItemsBeginCount;
5554 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
5555 size_t m_1stNullItemsMiddleCount;
5556 // Number of items in 2nd vector with hAllocation = null.
5557 size_t m_2ndNullItemsCount;
5558
5559 bool ShouldCompact1st() const;
5560 void CleanupAfterFree();
5561};
5562
5563/*
5564- GetSize() is the original size of allocated memory block.
5565- m_UsableSize is this size aligned down to a power of two.
5566 All allocations and calculations happen relative to m_UsableSize.
5567- GetUnusableSize() is the difference between them.
5568 It is repoted as separate, unused range, not available for allocations.
5569
5570Node at level 0 has size = m_UsableSize.
5571Each next level contains nodes with size 2 times smaller than current level.
5572m_LevelCount is the maximum number of levels to use in the current object.
5573*/
5574class VmaBlockMetadata_Buddy : public VmaBlockMetadata
5575{
5576 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
5577public:
5578 VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
5579 virtual ~VmaBlockMetadata_Buddy();
5580 virtual void Init(VkDeviceSize size);
5581
5582 virtual bool Validate() const;
5583 virtual size_t GetAllocationCount() const { return m_AllocationCount; }
5584 virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
5585 virtual VkDeviceSize GetUnusedRangeSizeMax() const;
5586 virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
5587
5588 virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
5589 virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
5590
5591#if VMA_STATS_STRING_ENABLED
5592 virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
5593#endif
5594
5595 virtual bool CreateAllocationRequest(
5596 uint32_t currentFrameIndex,
5597 uint32_t frameInUseCount,
5598 VkDeviceSize bufferImageGranularity,
5599 VkDeviceSize allocSize,
5600 VkDeviceSize allocAlignment,
5601 bool upperAddress,
5602 VmaSuballocationType allocType,
5603 bool canMakeOtherLost,
5604 uint32_t strategy,
5605 VmaAllocationRequest* pAllocationRequest);
5606
5607 virtual bool MakeRequestedAllocationsLost(
5608 uint32_t currentFrameIndex,
5609 uint32_t frameInUseCount,
5610 VmaAllocationRequest* pAllocationRequest);
5611
5612 virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
5613
5614 virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
5615
5616 virtual void Alloc(
5617 const VmaAllocationRequest& request,
5618 VmaSuballocationType type,
5619 VkDeviceSize allocSize,
5620 bool upperAddress,
5621 VmaAllocation hAllocation);
5622
5623 virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
5624 virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
5625
5626private:
5627 static const VkDeviceSize MIN_NODE_SIZE = 32;
5628 static const size_t MAX_LEVELS = 30;
5629
5630 struct ValidationContext
5631 {
5632 size_t calculatedAllocationCount;
5633 size_t calculatedFreeCount;
5634 VkDeviceSize calculatedSumFreeSize;
5635
5636 ValidationContext() :
5637 calculatedAllocationCount(0),
5638 calculatedFreeCount(0),
5639 calculatedSumFreeSize(0) { }
5640 };
5641
5642 struct Node
5643 {
5644 VkDeviceSize offset;
5645 enum TYPE
5646 {
5647 TYPE_FREE,
5648 TYPE_ALLOCATION,
5649 TYPE_SPLIT,
5650 TYPE_COUNT
5651 } type;
5652 Node* parent;
5653 Node* buddy;
5654
5655 union
5656 {
5657 struct
5658 {
5659 Node* prev;
5660 Node* next;
5661 } free;
5662 struct
5663 {
5664 VmaAllocation alloc;
5665 } allocation;
5666 struct
5667 {
5668 Node* leftChild;
5669 } split;
5670 };
5671 };
5672
5673 // Size of the memory block aligned down to a power of two.
5674 VkDeviceSize m_UsableSize;
5675 uint32_t m_LevelCount;
5676
5677 Node* m_Root;
5678 struct {
5679 Node* front;
5680 Node* back;
5681 } m_FreeList[MAX_LEVELS];
5682 // Number of nodes in the tree with type == TYPE_ALLOCATION.
5683 size_t m_AllocationCount;
5684 // Number of nodes in the tree with type == TYPE_FREE.
5685 size_t m_FreeCount;
5686 // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
5687 VkDeviceSize m_SumFreeSize;
5688
5689 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
5690 void DeleteNode(Node* node);
5691 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
5692 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
5693 inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
5694 // Alloc passed just for validation. Can be null.
5695 void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
5696 void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
5697 // Adds node to the front of FreeList at given level.
5698 // node->type must be FREE.
5699 // node->free.prev, next can be undefined.
5700 void AddToFreeListFront(uint32_t level, Node* node);
5701 // Removes node from FreeList at given level.
5702 // node->type must be FREE.
5703 // node->free.prev, next stay untouched.
5704 void RemoveFromFreeList(uint32_t level, Node* node);
5705
5706#if VMA_STATS_STRING_ENABLED
5707 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
5708#endif
5709};
5710
5711/*
5712Represents a single block of device memory (`VkDeviceMemory`) with all the
5713data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5714
5715Thread-safety: This class must be externally synchronized.
5716*/
5717class VmaDeviceMemoryBlock
5718{
5719 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5720public:
5721 VmaBlockMetadata* m_pMetadata;
5722
5723 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5724
5725 ~VmaDeviceMemoryBlock()
5726 {
5727 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
5728 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
5729 }
5730
5731 // Always call after construction.
5732 void Init(
5733 VmaAllocator hAllocator,
5734 uint32_t newMemoryTypeIndex,
5735 VkDeviceMemory newMemory,
5736 VkDeviceSize newSize,
5737 uint32_t id,
5738 uint32_t algorithm);
5739 // Always call before destruction.
5740 void Destroy(VmaAllocator allocator);
5741
5742 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
5743 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5744 uint32_t GetId() const { return m_Id; }
5745 void* GetMappedData() const { return m_pMappedData; }
5746
5747 // Validates all data structures inside this object. If not valid, returns false.
5748 bool Validate() const;
5749
5750 VkResult CheckCorruption(VmaAllocator hAllocator);
5751
5752 // ppData can be null.
5753 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5754 void Unmap(VmaAllocator hAllocator, uint32_t count);
5755
5756 VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5757 VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5758
5759 VkResult BindBufferMemory(
5760 const VmaAllocator hAllocator,
5761 const VmaAllocation hAllocation,
5762 VkBuffer hBuffer);
5763 VkResult BindImageMemory(
5764 const VmaAllocator hAllocator,
5765 const VmaAllocation hAllocation,
5766 VkImage hImage);
5767
5768private:
5769 uint32_t m_MemoryTypeIndex;
5770 uint32_t m_Id;
5771 VkDeviceMemory m_hMemory;
5772
5773 /*
5774 Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5775 Also protects m_MapCount, m_pMappedData.
5776 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5777 */
5778 VMA_MUTEX m_Mutex;
5779 uint32_t m_MapCount;
5780 void* m_pMappedData;
5781};
5782
5783struct VmaPointerLess
5784{
5785 bool operator()(const void* lhs, const void* rhs) const
5786 {
5787 return lhs < rhs;
5788 }
5789};
5790
5791struct VmaDefragmentationMove
5792{
5793 size_t srcBlockIndex;
5794 size_t dstBlockIndex;
5795 VkDeviceSize srcOffset;
5796 VkDeviceSize dstOffset;
5797 VkDeviceSize size;
5798};
5799
5800class VmaDefragmentationAlgorithm;
5801
5802/*
5803Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
5804Vulkan memory type.
5805
5806Synchronized internally with a mutex.
5807*/
5808struct VmaBlockVector
5809{
5810 VMA_CLASS_NO_COPY(VmaBlockVector)
5811public:
5812 VmaBlockVector(
5813 VmaAllocator hAllocator,
5814 uint32_t memoryTypeIndex,
5815 VkDeviceSize preferredBlockSize,
5816 size_t minBlockCount,
5817 size_t maxBlockCount,
5818 VkDeviceSize bufferImageGranularity,
5819 uint32_t frameInUseCount,
5820 bool isCustomPool,
5821 bool explicitBlockSize,
5822 uint32_t algorithm);
5823 ~VmaBlockVector();
5824
5825 VkResult CreateMinBlocks();
5826
5827 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
5828 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
5829 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
5830 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
5831 uint32_t GetAlgorithm() const { return m_Algorithm; }
5832
5833 void GetPoolStats(VmaPoolStats* pStats);
5834
5835 bool IsEmpty() const { return m_Blocks.empty(); }
5836 bool IsCorruptionDetectionEnabled() const;
5837
5838 VkResult Allocate(
5839 VmaPool hCurrentPool,
5840 uint32_t currentFrameIndex,
5841 VkDeviceSize size,
5842 VkDeviceSize alignment,
5843 const VmaAllocationCreateInfo& createInfo,
5844 VmaSuballocationType suballocType,
5845 size_t allocationCount,
5846 VmaAllocation* pAllocations);
5847
5848 void Free(
5849 VmaAllocation hAllocation);
5850
5851 // Adds statistics of this BlockVector to pStats.
5852 void AddStats(VmaStats* pStats);
5853
5854#if VMA_STATS_STRING_ENABLED
5855 void PrintDetailedMap(class VmaJsonWriter& json);
5856#endif
5857
5858 void MakePoolAllocationsLost(
5859 uint32_t currentFrameIndex,
5860 size_t* pLostAllocationCount);
5861 VkResult CheckCorruption();
5862
5863 // Saves results in pCtx->res.
5864 void Defragment(
5865 class VmaBlockVectorDefragmentationContext* pCtx,
5866 VmaDefragmentationStats* pStats,
5867 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
5868 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
5869 VkCommandBuffer commandBuffer);
5870 void DefragmentationEnd(
5871 class VmaBlockVectorDefragmentationContext* pCtx,
5872 VmaDefragmentationStats* pStats);
5873
5874 ////////////////////////////////////////////////////////////////////////////////
5875 // To be used only while the m_Mutex is locked. Used during defragmentation.
5876
5877 size_t GetBlockCount() const { return m_Blocks.size(); }
5878 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
5879 size_t CalcAllocationCount() const;
5880 bool IsBufferImageGranularityConflictPossible() const;
5881
5882private:
5883 friend class VmaDefragmentationAlgorithm_Generic;
5884
5885 const VmaAllocator m_hAllocator;
5886 const uint32_t m_MemoryTypeIndex;
5887 const VkDeviceSize m_PreferredBlockSize;
5888 const size_t m_MinBlockCount;
5889 const size_t m_MaxBlockCount;
5890 const VkDeviceSize m_BufferImageGranularity;
5891 const uint32_t m_FrameInUseCount;
5892 const bool m_IsCustomPool;
5893 const bool m_ExplicitBlockSize;
5894 const uint32_t m_Algorithm;
5895 /* There can be at most one allocation that is completely empty - a
5896 hysteresis to avoid pessimistic case of alternating creation and destruction
5897 of a VkDeviceMemory. */
5898 bool m_HasEmptyBlock;
5899 VMA_RW_MUTEX m_Mutex;
5900 // Incrementally sorted by sumFreeSize, ascending.
5901 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
5902 uint32_t m_NextBlockId;
5903
5904 VkDeviceSize CalcMaxBlockSize() const;
5905
5906 // Finds and removes given block from vector.
5907 void Remove(VmaDeviceMemoryBlock* pBlock);
5908
5909 // Performs single step in sorting m_Blocks. They may not be fully sorted
5910 // after this call.
5911 void IncrementallySortBlocks();
5912
5913 VkResult AllocatePage(
5914 VmaPool hCurrentPool,
5915 uint32_t currentFrameIndex,
5916 VkDeviceSize size,
5917 VkDeviceSize alignment,
5918 const VmaAllocationCreateInfo& createInfo,
5919 VmaSuballocationType suballocType,
5920 VmaAllocation* pAllocation);
5921
5922 // To be used only without CAN_MAKE_OTHER_LOST flag.
5923 VkResult AllocateFromBlock(
5924 VmaDeviceMemoryBlock* pBlock,
5925 VmaPool hCurrentPool,
5926 uint32_t currentFrameIndex,
5927 VkDeviceSize size,
5928 VkDeviceSize alignment,
5929 VmaAllocationCreateFlags allocFlags,
5930 void* pUserData,
5931 VmaSuballocationType suballocType,
5932 uint32_t strategy,
5933 VmaAllocation* pAllocation);
5934
5935 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
5936
5937 // Saves result to pCtx->res.
5938 void ApplyDefragmentationMovesCpu(
5939 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5940 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
5941 // Saves result to pCtx->res.
5942 void ApplyDefragmentationMovesGpu(
5943 class VmaBlockVectorDefragmentationContext* pDefragCtx,
5944 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
5945 VkCommandBuffer commandBuffer);
5946
5947 /*
5948 Used during defragmentation. pDefragmentationStats is optional. It's in/out
5949 - updated with new data.
5950 */
5951 void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
5952};
5953
5954struct VmaPool_T
5955{
5956 VMA_CLASS_NO_COPY(VmaPool_T)
5957public:
5958 VmaBlockVector m_BlockVector;
5959
5960 VmaPool_T(
5961 VmaAllocator hAllocator,
5962 const VmaPoolCreateInfo& createInfo,
5963 VkDeviceSize preferredBlockSize);
5964 ~VmaPool_T();
5965
5966 uint32_t GetId() const { return m_Id; }
5967 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
5968
5969#if VMA_STATS_STRING_ENABLED
5970 //void PrintDetailedMap(class VmaStringBuilder& sb);
5971#endif
5972
5973private:
5974 uint32_t m_Id;
5975};
5976
5977/*
5978Performs defragmentation:
5979
5980- Updates `pBlockVector->m_pMetadata`.
5981- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
5982- Does not move actual data, only returns requested moves as `moves`.
5983*/
5984class VmaDefragmentationAlgorithm
5985{
5986 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
5987public:
5988 VmaDefragmentationAlgorithm(
5989 VmaAllocator hAllocator,
5990 VmaBlockVector* pBlockVector,
5991 uint32_t currentFrameIndex) :
5992 m_hAllocator(hAllocator),
5993 m_pBlockVector(pBlockVector),
5994 m_CurrentFrameIndex(currentFrameIndex)
5995 {
5996 }
5997 virtual ~VmaDefragmentationAlgorithm()
5998 {
5999 }
6000
6001 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
6002 virtual void AddAll() = 0;
6003
6004 virtual VkResult Defragment(
6005 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6006 VkDeviceSize maxBytesToMove,
6007 uint32_t maxAllocationsToMove) = 0;
6008
6009 virtual VkDeviceSize GetBytesMoved() const = 0;
6010 virtual uint32_t GetAllocationsMoved() const = 0;
6011
6012protected:
6013 VmaAllocator const m_hAllocator;
6014 VmaBlockVector* const m_pBlockVector;
6015 const uint32_t m_CurrentFrameIndex;
6016
6017 struct AllocationInfo
6018 {
6019 VmaAllocation m_hAllocation;
6020 VkBool32* m_pChanged;
6021
6022 AllocationInfo() :
6023 m_hAllocation(VK_NULL_HANDLE),
6024 m_pChanged(VMA_NULL)
6025 {
6026 }
6027 AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
6028 m_hAllocation(hAlloc),
6029 m_pChanged(pChanged)
6030 {
6031 }
6032 };
6033};
6034
6035class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
6036{
6037 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
6038public:
6039 VmaDefragmentationAlgorithm_Generic(
6040 VmaAllocator hAllocator,
6041 VmaBlockVector* pBlockVector,
6042 uint32_t currentFrameIndex,
6043 bool overlappingMoveSupported);
6044 virtual ~VmaDefragmentationAlgorithm_Generic();
6045
6046 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6047 virtual void AddAll() { m_AllAllocations = true; }
6048
6049 virtual VkResult Defragment(
6050 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6051 VkDeviceSize maxBytesToMove,
6052 uint32_t maxAllocationsToMove);
6053
6054 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6055 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6056
6057private:
6058 uint32_t m_AllocationCount;
6059 bool m_AllAllocations;
6060
6061 VkDeviceSize m_BytesMoved;
6062 uint32_t m_AllocationsMoved;
6063
6064 struct AllocationInfoSizeGreater
6065 {
6066 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6067 {
6068 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
6069 }
6070 };
6071
6072 struct AllocationInfoOffsetGreater
6073 {
6074 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
6075 {
6076 return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
6077 }
6078 };
6079
6080 struct BlockInfo
6081 {
6082 size_t m_OriginalBlockIndex;
6083 VmaDeviceMemoryBlock* m_pBlock;
6084 bool m_HasNonMovableAllocations;
6085 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
6086
6087 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
6088 m_OriginalBlockIndex(SIZE_MAX),
6089 m_pBlock(VMA_NULL),
6090 m_HasNonMovableAllocations(true),
6091 m_Allocations(pAllocationCallbacks)
6092 {
6093 }
6094
6095 void CalcHasNonMovableAllocations()
6096 {
6097 const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
6098 const size_t defragmentAllocCount = m_Allocations.size();
6099 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
6100 }
6101
6102 void SortAllocationsBySizeDescending()
6103 {
6104 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
6105 }
6106
6107 void SortAllocationsByOffsetDescending()
6108 {
6109 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
6110 }
6111 };
6112
6113 struct BlockPointerLess
6114 {
6115 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
6116 {
6117 return pLhsBlockInfo->m_pBlock < pRhsBlock;
6118 }
6119 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6120 {
6121 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
6122 }
6123 };
6124
6125 // 1. Blocks with some non-movable allocations go first.
6126 // 2. Blocks with smaller sumFreeSize go first.
6127 struct BlockInfoCompareMoveDestination
6128 {
6129 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
6130 {
6131 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
6132 {
6133 return true;
6134 }
6135 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
6136 {
6137 return false;
6138 }
6139 if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
6140 {
6141 return true;
6142 }
6143 return false;
6144 }
6145 };
6146
6147 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
6148 BlockInfoVector m_Blocks;
6149
6150 VkResult DefragmentRound(
6151 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6152 VkDeviceSize maxBytesToMove,
6153 uint32_t maxAllocationsToMove);
6154
6155 size_t CalcBlocksWithNonMovableCount() const;
6156
6157 static bool MoveMakesSense(
6158 size_t dstBlockIndex, VkDeviceSize dstOffset,
6159 size_t srcBlockIndex, VkDeviceSize srcOffset);
6160};
6161
6162class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
6163{
6164 VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
6165public:
6166 VmaDefragmentationAlgorithm_Fast(
6167 VmaAllocator hAllocator,
6168 VmaBlockVector* pBlockVector,
6169 uint32_t currentFrameIndex,
6170 bool overlappingMoveSupported);
6171 virtual ~VmaDefragmentationAlgorithm_Fast();
6172
6173 virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
6174 virtual void AddAll() { m_AllAllocations = true; }
6175
6176 virtual VkResult Defragment(
6177 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
6178 VkDeviceSize maxBytesToMove,
6179 uint32_t maxAllocationsToMove);
6180
6181 virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
6182 virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
6183
6184private:
6185 struct BlockInfo
6186 {
6187 size_t origBlockIndex;
6188 };
6189
6190 class FreeSpaceDatabase
6191 {
6192 public:
6193 FreeSpaceDatabase()
6194 {
6195 FreeSpace s = {};
6196 s.blockInfoIndex = SIZE_MAX;
6197 for(size_t i = 0; i < MAX_COUNT; ++i)
6198 {
6199 m_FreeSpaces[i] = s;
6200 }
6201 }
6202
6203 void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
6204 {
6205 if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6206 {
6207 return;
6208 }
6209
6210 // Find first invalid or the smallest structure.
6211 size_t bestIndex = SIZE_MAX;
6212 for(size_t i = 0; i < MAX_COUNT; ++i)
6213 {
6214 // Empty structure.
6215 if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
6216 {
6217 bestIndex = i;
6218 break;
6219 }
6220 if(m_FreeSpaces[i].size < size &&
6221 (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
6222 {
6223 bestIndex = i;
6224 }
6225 }
6226
6227 if(bestIndex != SIZE_MAX)
6228 {
6229 m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
6230 m_FreeSpaces[bestIndex].offset = offset;
6231 m_FreeSpaces[bestIndex].size = size;
6232 }
6233 }
6234
6235 bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
6236 size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
6237 {
6238 size_t bestIndex = SIZE_MAX;
6239 VkDeviceSize bestFreeSpaceAfter = 0;
6240 for(size_t i = 0; i < MAX_COUNT; ++i)
6241 {
6242 // Structure is valid.
6243 if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
6244 {
6245 const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
6246 // Allocation fits into this structure.
6247 if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
6248 {
6249 const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
6250 (dstOffset + size);
6251 if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
6252 {
6253 bestIndex = i;
6254 bestFreeSpaceAfter = freeSpaceAfter;
6255 }
6256 }
6257 }
6258 }
6259
6260 if(bestIndex != SIZE_MAX)
6261 {
6262 outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
6263 outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
6264
6265 if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6266 {
6267 // Leave this structure for remaining empty space.
6268 const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
6269 m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
6270 m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
6271 }
6272 else
6273 {
6274 // This structure becomes invalid.
6275 m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
6276 }
6277
6278 return true;
6279 }
6280
6281 return false;
6282 }
6283
6284 private:
6285 static const size_t MAX_COUNT = 4;
6286
6287 struct FreeSpace
6288 {
6289 size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
6290 VkDeviceSize offset;
6291 VkDeviceSize size;
6292 } m_FreeSpaces[MAX_COUNT];
6293 };
6294
6295 const bool m_OverlappingMoveSupported;
6296
6297 uint32_t m_AllocationCount;
6298 bool m_AllAllocations;
6299
6300 VkDeviceSize m_BytesMoved;
6301 uint32_t m_AllocationsMoved;
6302
6303 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
6304
6305 void PreprocessMetadata();
6306 void PostprocessMetadata();
6307 void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
6308};
6309
6310struct VmaBlockDefragmentationContext
6311{
6312 enum BLOCK_FLAG
6313 {
6314 BLOCK_FLAG_USED = 0x00000001,
6315 };
6316 uint32_t flags;
6317 VkBuffer hBuffer;
6318
6319 VmaBlockDefragmentationContext() :
6320 flags(0),
6321 hBuffer(VK_NULL_HANDLE)
6322 {
6323 }
6324};
6325
6326class VmaBlockVectorDefragmentationContext
6327{
6328 VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
6329public:
6330 VkResult res;
6331 bool mutexLocked;
6332 VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
6333
6334 VmaBlockVectorDefragmentationContext(
6335 VmaAllocator hAllocator,
6336 VmaPool hCustomPool, // Optional.
6337 VmaBlockVector* pBlockVector,
6338 uint32_t currFrameIndex,
6339 uint32_t flags);
6340 ~VmaBlockVectorDefragmentationContext();
6341
6342 VmaPool GetCustomPool() const { return m_hCustomPool; }
6343 VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
6344 VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
6345
6346 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
6347 void AddAll() { m_AllAllocations = true; }
6348
6349 void Begin(bool overlappingMoveSupported);
6350
6351private:
6352 const VmaAllocator m_hAllocator;
6353 // Null if not from custom pool.
6354 const VmaPool m_hCustomPool;
6355 // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
6356 VmaBlockVector* const m_pBlockVector;
6357 const uint32_t m_CurrFrameIndex;
Mike Schuchardte48dc142019-04-18 09:12:03 -07006358 //const uint32_t m_AlgorithmFlags;
Tony-LunarG7b7e4e62019-03-18 15:01:55 -06006359 // Owner of this object.
6360 VmaDefragmentationAlgorithm* m_pAlgorithm;
6361
6362 struct AllocInfo
6363 {
6364 VmaAllocation hAlloc;
6365 VkBool32* pChanged;
6366 };
6367 // Used between constructor and Begin.
6368 VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
6369 bool m_AllAllocations;
6370};
6371
6372struct VmaDefragmentationContext_T
6373{
6374private:
6375 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
6376public:
6377 VmaDefragmentationContext_T(
6378 VmaAllocator hAllocator,
6379 uint32_t currFrameIndex,
6380 uint32_t flags,
6381 VmaDefragmentationStats* pStats);
6382 ~VmaDefragmentationContext_T();
6383
6384 void AddPools(uint32_t poolCount, VmaPool* pPools);
6385 void AddAllocations(
6386 uint32_t allocationCount,
6387 VmaAllocation* pAllocations,
6388 VkBool32* pAllocationsChanged);
6389
6390 /*
6391 Returns:
6392 - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
6393 - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
6394 - Negative value if error occured and object can be destroyed immediately.
6395 */
6396 VkResult Defragment(
6397 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
6398 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
6399 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
6400
6401private:
6402 const VmaAllocator m_hAllocator;
6403 const uint32_t m_CurrFrameIndex;
6404 const uint32_t m_Flags;
6405 VmaDefragmentationStats* const m_pStats;
6406 // Owner of these objects.
6407 VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
6408 // Owner of these objects.
6409 VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
6410};
6411
6412#if VMA_RECORDING_ENABLED
6413
6414class VmaRecorder
6415{
6416public:
6417 VmaRecorder();
6418 VkResult Init(const VmaRecordSettings& settings, bool useMutex);
6419 void WriteConfiguration(
6420 const VkPhysicalDeviceProperties& devProps,
6421 const VkPhysicalDeviceMemoryProperties& memProps,
6422 bool dedicatedAllocationExtensionEnabled);
6423 ~VmaRecorder();
6424
6425 void RecordCreateAllocator(uint32_t frameIndex);
6426 void RecordDestroyAllocator(uint32_t frameIndex);
6427 void RecordCreatePool(uint32_t frameIndex,
6428 const VmaPoolCreateInfo& createInfo,
6429 VmaPool pool);
6430 void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
6431 void RecordAllocateMemory(uint32_t frameIndex,
6432 const VkMemoryRequirements& vkMemReq,
6433 const VmaAllocationCreateInfo& createInfo,
6434 VmaAllocation allocation);
6435 void RecordAllocateMemoryPages(uint32_t frameIndex,
6436 const VkMemoryRequirements& vkMemReq,
6437 const VmaAllocationCreateInfo& createInfo,
6438 uint64_t allocationCount,
6439 const VmaAllocation* pAllocations);
6440 void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
6441 const VkMemoryRequirements& vkMemReq,
6442 bool requiresDedicatedAllocation,
6443 bool prefersDedicatedAllocation,
6444 const VmaAllocationCreateInfo& createInfo,
6445 VmaAllocation allocation);
6446 void RecordAllocateMemoryForImage(uint32_t frameIndex,
6447 const VkMemoryRequirements& vkMemReq,
6448 bool requiresDedicatedAllocation,
6449 bool prefersDedicatedAllocation,
6450 const VmaAllocationCreateInfo& createInfo,
6451 VmaAllocation allocation);
6452 void RecordFreeMemory(uint32_t frameIndex,
6453 VmaAllocation allocation);
6454 void RecordFreeMemoryPages(uint32_t frameIndex,
6455 uint64_t allocationCount,
6456 const VmaAllocation* pAllocations);
6457 void RecordResizeAllocation(
6458 uint32_t frameIndex,
6459 VmaAllocation allocation,
6460 VkDeviceSize newSize);
6461 void RecordSetAllocationUserData(uint32_t frameIndex,
6462 VmaAllocation allocation,
6463 const void* pUserData);
6464 void RecordCreateLostAllocation(uint32_t frameIndex,
6465 VmaAllocation allocation);
6466 void RecordMapMemory(uint32_t frameIndex,
6467 VmaAllocation allocation);
6468 void RecordUnmapMemory(uint32_t frameIndex,
6469 VmaAllocation allocation);
6470 void RecordFlushAllocation(uint32_t frameIndex,
6471 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6472 void RecordInvalidateAllocation(uint32_t frameIndex,
6473 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
6474 void RecordCreateBuffer(uint32_t frameIndex,
6475 const VkBufferCreateInfo& bufCreateInfo,
6476 const VmaAllocationCreateInfo& allocCreateInfo,
6477 VmaAllocation allocation);
6478 void RecordCreateImage(uint32_t frameIndex,
6479 const VkImageCreateInfo& imageCreateInfo,
6480 const VmaAllocationCreateInfo& allocCreateInfo,
6481 VmaAllocation allocation);
6482 void RecordDestroyBuffer(uint32_t frameIndex,
6483 VmaAllocation allocation);
6484 void RecordDestroyImage(uint32_t frameIndex,
6485 VmaAllocation allocation);
6486 void RecordTouchAllocation(uint32_t frameIndex,
6487 VmaAllocation allocation);
6488 void RecordGetAllocationInfo(uint32_t frameIndex,
6489 VmaAllocation allocation);
6490 void RecordMakePoolAllocationsLost(uint32_t frameIndex,
6491 VmaPool pool);
6492 void RecordDefragmentationBegin(uint32_t frameIndex,
6493 const VmaDefragmentationInfo2& info,
6494 VmaDefragmentationContext ctx);
6495 void RecordDefragmentationEnd(uint32_t frameIndex,
6496 VmaDefragmentationContext ctx);
6497
6498private:
6499 struct CallParams
6500 {
6501 uint32_t threadId;
6502 double time;
6503 };
6504
6505 class UserDataString
6506 {
6507 public:
6508 UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
6509 const char* GetString() const { return m_Str; }
6510
6511 private:
6512 char m_PtrStr[17];
6513 const char* m_Str;
6514 };
6515
6516 bool m_UseMutex;
6517 VmaRecordFlags m_Flags;
6518 FILE* m_File;
6519 VMA_MUTEX m_FileMutex;
6520 int64_t m_Freq;
6521 int64_t m_StartCounter;
6522
6523 void GetBasicParams(CallParams& outParams);
6524
6525 // T must be a pointer type, e.g. VmaAllocation, VmaPool.
6526 template<typename T>
6527 void PrintPointerList(uint64_t count, const T* pItems)
6528 {
6529 if(count)
6530 {
6531 fprintf(m_File, "%p", pItems[0]);
6532 for(uint64_t i = 1; i < count; ++i)
6533 {
6534 fprintf(m_File, " %p", pItems[i]);
6535 }
6536 }
6537 }
6538
6539 void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
6540 void Flush();
6541};
6542
6543#endif // #if VMA_RECORDING_ENABLED
6544
6545// Main allocator object.
6546struct VmaAllocator_T
6547{
6548 VMA_CLASS_NO_COPY(VmaAllocator_T)
6549public:
6550 bool m_UseMutex;
6551 bool m_UseKhrDedicatedAllocation;
6552 VkDevice m_hDevice;
6553 bool m_AllocationCallbacksSpecified;
6554 VkAllocationCallbacks m_AllocationCallbacks;
6555 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
6556
6557 // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
6558 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
6559 VMA_MUTEX m_HeapSizeLimitMutex;
6560
6561 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
6562 VkPhysicalDeviceMemoryProperties m_MemProps;
6563
6564 // Default pools.
6565 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
6566
6567 // Each vector is sorted by memory (handle value).
6568 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
6569 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
6570 VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
6571
6572 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
6573 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
6574 ~VmaAllocator_T();
6575
6576 const VkAllocationCallbacks* GetAllocationCallbacks() const
6577 {
6578 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
6579 }
6580 const VmaVulkanFunctions& GetVulkanFunctions() const
6581 {
6582 return m_VulkanFunctions;
6583 }
6584
6585 VkDeviceSize GetBufferImageGranularity() const
6586 {
6587 return VMA_MAX(
6588 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
6589 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
6590 }
6591
6592 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
6593 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
6594
6595 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
6596 {
6597 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
6598 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
6599 }
6600 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
6601 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
6602 {
6603 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
6604 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
6605 }
6606 // Minimum alignment for all allocations in specific memory type.
6607 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
6608 {
6609 return IsMemoryTypeNonCoherent(memTypeIndex) ?
6610 VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
6611 (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
6612 }
6613
6614 bool IsIntegratedGpu() const
6615 {
6616 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
6617 }
6618
6619#if VMA_RECORDING_ENABLED
6620 VmaRecorder* GetRecorder() const { return m_pRecorder; }
6621#endif
6622
6623 void GetBufferMemoryRequirements(
6624 VkBuffer hBuffer,
6625 VkMemoryRequirements& memReq,
6626 bool& requiresDedicatedAllocation,
6627 bool& prefersDedicatedAllocation) const;
6628 void GetImageMemoryRequirements(
6629 VkImage hImage,
6630 VkMemoryRequirements& memReq,
6631 bool& requiresDedicatedAllocation,
6632 bool& prefersDedicatedAllocation) const;
6633
6634 // Main allocation function.
6635 VkResult AllocateMemory(
6636 const VkMemoryRequirements& vkMemReq,
6637 bool requiresDedicatedAllocation,
6638 bool prefersDedicatedAllocation,
6639 VkBuffer dedicatedBuffer,
6640 VkImage dedicatedImage,
6641 const VmaAllocationCreateInfo& createInfo,
6642 VmaSuballocationType suballocType,
6643 size_t allocationCount,
6644 VmaAllocation* pAllocations);
6645
6646 // Main deallocation function.
6647 void FreeMemory(
6648 size_t allocationCount,
6649 const VmaAllocation* pAllocations);
6650
6651 VkResult ResizeAllocation(
6652 const VmaAllocation alloc,
6653 VkDeviceSize newSize);
6654
6655 void CalculateStats(VmaStats* pStats);
6656
6657#if VMA_STATS_STRING_ENABLED
6658 void PrintDetailedMap(class VmaJsonWriter& json);
6659#endif
6660
6661 VkResult DefragmentationBegin(
6662 const VmaDefragmentationInfo2& info,
6663 VmaDefragmentationStats* pStats,
6664 VmaDefragmentationContext* pContext);
6665 VkResult DefragmentationEnd(
6666 VmaDefragmentationContext context);
6667
6668 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
6669 bool TouchAllocation(VmaAllocation hAllocation);
6670
6671 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
6672 void DestroyPool(VmaPool pool);
6673 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
6674
6675 void SetCurrentFrameIndex(uint32_t frameIndex);
6676 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
6677
6678 void MakePoolAllocationsLost(
6679 VmaPool hPool,
6680 size_t* pLostAllocationCount);
6681 VkResult CheckPoolCorruption(VmaPool hPool);
6682 VkResult CheckCorruption(uint32_t memoryTypeBits);
6683
6684 void CreateLostAllocation(VmaAllocation* pAllocation);
6685
6686 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
6687 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
6688
6689 VkResult Map(VmaAllocation hAllocation, void** ppData);
6690 void Unmap(VmaAllocation hAllocation);
6691
6692 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
6693 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
6694
6695 void FlushOrInvalidateAllocation(
6696 VmaAllocation hAllocation,
6697 VkDeviceSize offset, VkDeviceSize size,
6698 VMA_CACHE_OPERATION op);
6699
6700 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
6701
6702private:
6703 VkDeviceSize m_PreferredLargeHeapBlockSize;
6704
6705 VkPhysicalDevice m_PhysicalDevice;
6706 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
6707
6708 VMA_RW_MUTEX m_PoolsMutex;
6709 // Protected by m_PoolsMutex. Sorted by pointer value.
6710 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
6711 uint32_t m_NextPoolId;
6712
6713 VmaVulkanFunctions m_VulkanFunctions;
6714
6715#if VMA_RECORDING_ENABLED
6716 VmaRecorder* m_pRecorder;
6717#endif
6718
6719 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
6720
6721 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
6722
6723 VkResult AllocateMemoryOfType(
6724 VkDeviceSize size,
6725 VkDeviceSize alignment,
6726 bool dedicatedAllocation,
6727 VkBuffer dedicatedBuffer,
6728 VkImage dedicatedImage,
6729 const VmaAllocationCreateInfo& createInfo,
6730 uint32_t memTypeIndex,
6731 VmaSuballocationType suballocType,
6732 size_t allocationCount,
6733 VmaAllocation* pAllocations);
6734
6735 // Helper function only to be used inside AllocateDedicatedMemory.
6736 VkResult AllocateDedicatedMemoryPage(
6737 VkDeviceSize size,
6738 VmaSuballocationType suballocType,
6739 uint32_t memTypeIndex,
6740 const VkMemoryAllocateInfo& allocInfo,
6741 bool map,
6742 bool isUserDataString,
6743 void* pUserData,
6744 VmaAllocation* pAllocation);
6745
6746 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
6747 VkResult AllocateDedicatedMemory(
6748 VkDeviceSize size,
6749 VmaSuballocationType suballocType,
6750 uint32_t memTypeIndex,
6751 bool map,
6752 bool isUserDataString,
6753 void* pUserData,
6754 VkBuffer dedicatedBuffer,
6755 VkImage dedicatedImage,
6756 size_t allocationCount,
6757 VmaAllocation* pAllocations);
6758
6759 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
6760 void FreeDedicatedMemory(VmaAllocation allocation);
6761};
6762
6763////////////////////////////////////////////////////////////////////////////////
6764// Memory allocation #2 after VmaAllocator_T definition
6765
6766static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
6767{
6768 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
6769}
6770
6771static void VmaFree(VmaAllocator hAllocator, void* ptr)
6772{
6773 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
6774}
6775
6776template<typename T>
6777static T* VmaAllocate(VmaAllocator hAllocator)
6778{
6779 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
6780}
6781
6782template<typename T>
6783static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
6784{
6785 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
6786}
6787
6788template<typename T>
6789static void vma_delete(VmaAllocator hAllocator, T* ptr)
6790{
6791 if(ptr != VMA_NULL)
6792 {
6793 ptr->~T();
6794 VmaFree(hAllocator, ptr);
6795 }
6796}
6797
6798template<typename T>
6799static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
6800{
6801 if(ptr != VMA_NULL)
6802 {
6803 for(size_t i = count; i--; )
6804 ptr[i].~T();
6805 VmaFree(hAllocator, ptr);
6806 }
6807}
6808
6809////////////////////////////////////////////////////////////////////////////////
6810// VmaStringBuilder
6811
6812#if VMA_STATS_STRING_ENABLED
6813
6814class VmaStringBuilder
6815{
6816public:
6817 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
6818 size_t GetLength() const { return m_Data.size(); }
6819 const char* GetData() const { return m_Data.data(); }
6820
6821 void Add(char ch) { m_Data.push_back(ch); }
6822 void Add(const char* pStr);
6823 void AddNewLine() { Add('\n'); }
6824 void AddNumber(uint32_t num);
6825 void AddNumber(uint64_t num);
6826 void AddPointer(const void* ptr);
6827
6828private:
6829 VmaVector< char, VmaStlAllocator<char> > m_Data;
6830};
6831
6832void VmaStringBuilder::Add(const char* pStr)
6833{
6834 const size_t strLen = strlen(pStr);
6835 if(strLen > 0)
6836 {
6837 const size_t oldCount = m_Data.size();
6838 m_Data.resize(oldCount + strLen);
6839 memcpy(m_Data.data() + oldCount, pStr, strLen);
6840 }
6841}
6842
6843void VmaStringBuilder::AddNumber(uint32_t num)
6844{
6845 char buf[11];
6846 VmaUint32ToStr(buf, sizeof(buf), num);
6847 Add(buf);
6848}
6849
6850void VmaStringBuilder::AddNumber(uint64_t num)
6851{
6852 char buf[21];
6853 VmaUint64ToStr(buf, sizeof(buf), num);
6854 Add(buf);
6855}
6856
6857void VmaStringBuilder::AddPointer(const void* ptr)
6858{
6859 char buf[21];
6860 VmaPtrToStr(buf, sizeof(buf), ptr);
6861 Add(buf);
6862}
6863
6864#endif // #if VMA_STATS_STRING_ENABLED
6865
6866////////////////////////////////////////////////////////////////////////////////
6867// VmaJsonWriter
6868
6869#if VMA_STATS_STRING_ENABLED
6870
6871class VmaJsonWriter
6872{
6873 VMA_CLASS_NO_COPY(VmaJsonWriter)
6874public:
6875 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
6876 ~VmaJsonWriter();
6877
6878 void BeginObject(bool singleLine = false);
6879 void EndObject();
6880
6881 void BeginArray(bool singleLine = false);
6882 void EndArray();
6883
6884 void WriteString(const char* pStr);
6885 void BeginString(const char* pStr = VMA_NULL);
6886 void ContinueString(const char* pStr);
6887 void ContinueString(uint32_t n);
6888 void ContinueString(uint64_t n);
6889 void ContinueString_Pointer(const void* ptr);
6890 void EndString(const char* pStr = VMA_NULL);
6891
6892 void WriteNumber(uint32_t n);
6893 void WriteNumber(uint64_t n);
6894 void WriteBool(bool b);
6895 void WriteNull();
6896
6897private:
6898 static const char* const INDENT;
6899
6900 enum COLLECTION_TYPE
6901 {
6902 COLLECTION_TYPE_OBJECT,
6903 COLLECTION_TYPE_ARRAY,
6904 };
6905 struct StackItem
6906 {
6907 COLLECTION_TYPE type;
6908 uint32_t valueCount;
6909 bool singleLineMode;
6910 };
6911
6912 VmaStringBuilder& m_SB;
6913 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
6914 bool m_InsideString;
6915
6916 void BeginValue(bool isString);
6917 void WriteIndent(bool oneLess = false);
6918};
6919
6920const char* const VmaJsonWriter::INDENT = " ";
6921
6922VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
6923 m_SB(sb),
6924 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
6925 m_InsideString(false)
6926{
6927}
6928
6929VmaJsonWriter::~VmaJsonWriter()
6930{
6931 VMA_ASSERT(!m_InsideString);
6932 VMA_ASSERT(m_Stack.empty());
6933}
6934
6935void VmaJsonWriter::BeginObject(bool singleLine)
6936{
6937 VMA_ASSERT(!m_InsideString);
6938
6939 BeginValue(false);
6940 m_SB.Add('{');
6941
6942 StackItem item;
6943 item.type = COLLECTION_TYPE_OBJECT;
6944 item.valueCount = 0;
6945 item.singleLineMode = singleLine;
6946 m_Stack.push_back(item);
6947}
6948
6949void VmaJsonWriter::EndObject()
6950{
6951 VMA_ASSERT(!m_InsideString);
6952
6953 WriteIndent(true);
6954 m_SB.Add('}');
6955
6956 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
6957 m_Stack.pop_back();
6958}
6959
6960void VmaJsonWriter::BeginArray(bool singleLine)
6961{
6962 VMA_ASSERT(!m_InsideString);
6963
6964 BeginValue(false);
6965 m_SB.Add('[');
6966
6967 StackItem item;
6968 item.type = COLLECTION_TYPE_ARRAY;
6969 item.valueCount = 0;
6970 item.singleLineMode = singleLine;
6971 m_Stack.push_back(item);
6972}
6973
6974void VmaJsonWriter::EndArray()
6975{
6976 VMA_ASSERT(!m_InsideString);
6977
6978 WriteIndent(true);
6979 m_SB.Add(']');
6980
6981 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
6982 m_Stack.pop_back();
6983}
6984
6985void VmaJsonWriter::WriteString(const char* pStr)
6986{
6987 BeginString(pStr);
6988 EndString();
6989}
6990
6991void VmaJsonWriter::BeginString(const char* pStr)
6992{
6993 VMA_ASSERT(!m_InsideString);
6994
6995 BeginValue(true);
6996 m_SB.Add('"');
6997 m_InsideString = true;
6998 if(pStr != VMA_NULL && pStr[0] != '\0')
6999 {
7000 ContinueString(pStr);
7001 }
7002}
7003
7004void VmaJsonWriter::ContinueString(const char* pStr)
7005{
7006 VMA_ASSERT(m_InsideString);
7007
7008 const size_t strLen = strlen(pStr);
7009 for(size_t i = 0; i < strLen; ++i)
7010 {
7011 char ch = pStr[i];
7012 if(ch == '\\')
7013 {
7014 m_SB.Add("\\\\");
7015 }
7016 else if(ch == '"')
7017 {
7018 m_SB.Add("\\\"");
7019 }
7020 else if(ch >= 32)
7021 {
7022 m_SB.Add(ch);
7023 }
7024 else switch(ch)
7025 {
7026 case '\b':
7027 m_SB.Add("\\b");
7028 break;
7029 case '\f':
7030 m_SB.Add("\\f");
7031 break;
7032 case '\n':
7033 m_SB.Add("\\n");
7034 break;
7035 case '\r':
7036 m_SB.Add("\\r");
7037 break;
7038 case '\t':
7039 m_SB.Add("\\t");
7040 break;
7041 default:
7042 VMA_ASSERT(0 && "Character not currently supported.");
7043 break;
7044 }
7045 }
7046}
7047
7048void VmaJsonWriter::ContinueString(uint32_t n)
7049{
7050 VMA_ASSERT(m_InsideString);
7051 m_SB.AddNumber(n);
7052}
7053
7054void VmaJsonWriter::ContinueString(uint64_t n)
7055{
7056 VMA_ASSERT(m_InsideString);
7057 m_SB.AddNumber(n);
7058}
7059
7060void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
7061{
7062 VMA_ASSERT(m_InsideString);
7063 m_SB.AddPointer(ptr);
7064}
7065
7066void VmaJsonWriter::EndString(const char* pStr)
7067{
7068 VMA_ASSERT(m_InsideString);
7069 if(pStr != VMA_NULL && pStr[0] != '\0')
7070 {
7071 ContinueString(pStr);
7072 }
7073 m_SB.Add('"');
7074 m_InsideString = false;
7075}
7076
7077void VmaJsonWriter::WriteNumber(uint32_t n)
7078{
7079 VMA_ASSERT(!m_InsideString);
7080 BeginValue(false);
7081 m_SB.AddNumber(n);
7082}
7083
7084void VmaJsonWriter::WriteNumber(uint64_t n)
7085{
7086 VMA_ASSERT(!m_InsideString);
7087 BeginValue(false);
7088 m_SB.AddNumber(n);
7089}
7090
7091void VmaJsonWriter::WriteBool(bool b)
7092{
7093 VMA_ASSERT(!m_InsideString);
7094 BeginValue(false);
7095 m_SB.Add(b ? "true" : "false");
7096}
7097
7098void VmaJsonWriter::WriteNull()
7099{
7100 VMA_ASSERT(!m_InsideString);
7101 BeginValue(false);
7102 m_SB.Add("null");
7103}
7104
7105void VmaJsonWriter::BeginValue(bool isString)
7106{
7107 if(!m_Stack.empty())
7108 {
7109 StackItem& currItem = m_Stack.back();
7110 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7111 currItem.valueCount % 2 == 0)
7112 {
7113 VMA_ASSERT(isString);
7114 }
7115
7116 if(currItem.type == COLLECTION_TYPE_OBJECT &&
7117 currItem.valueCount % 2 != 0)
7118 {
7119 m_SB.Add(": ");
7120 }
7121 else if(currItem.valueCount > 0)
7122 {
7123 m_SB.Add(", ");
7124 WriteIndent();
7125 }
7126 else
7127 {
7128 WriteIndent();
7129 }
7130 ++currItem.valueCount;
7131 }
7132}
7133
7134void VmaJsonWriter::WriteIndent(bool oneLess)
7135{
7136 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
7137 {
7138 m_SB.AddNewLine();
7139
7140 size_t count = m_Stack.size();
7141 if(count > 0 && oneLess)
7142 {
7143 --count;
7144 }
7145 for(size_t i = 0; i < count; ++i)
7146 {
7147 m_SB.Add(INDENT);
7148 }
7149 }
7150}
7151
7152#endif // #if VMA_STATS_STRING_ENABLED
7153
7154////////////////////////////////////////////////////////////////////////////////
7155
7156void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
7157{
7158 if(IsUserDataString())
7159 {
7160 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
7161
7162 FreeUserDataString(hAllocator);
7163
7164 if(pUserData != VMA_NULL)
7165 {
7166 const char* const newStrSrc = (char*)pUserData;
7167 const size_t newStrLen = strlen(newStrSrc);
7168 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
7169 memcpy(newStrDst, newStrSrc, newStrLen + 1);
7170 m_pUserData = newStrDst;
7171 }
7172 }
7173 else
7174 {
7175 m_pUserData = pUserData;
7176 }
7177}
7178
7179void VmaAllocation_T::ChangeBlockAllocation(
7180 VmaAllocator hAllocator,
7181 VmaDeviceMemoryBlock* block,
7182 VkDeviceSize offset)
7183{
7184 VMA_ASSERT(block != VMA_NULL);
7185 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7186
7187 // Move mapping reference counter from old block to new block.
7188 if(block != m_BlockAllocation.m_Block)
7189 {
7190 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
7191 if(IsPersistentMap())
7192 ++mapRefCount;
7193 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
7194 block->Map(hAllocator, mapRefCount, VMA_NULL);
7195 }
7196
7197 m_BlockAllocation.m_Block = block;
7198 m_BlockAllocation.m_Offset = offset;
7199}
7200
7201void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
7202{
7203 VMA_ASSERT(newSize > 0);
7204 m_Size = newSize;
7205}
7206
7207void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
7208{
7209 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7210 m_BlockAllocation.m_Offset = newOffset;
7211}
7212
7213VkDeviceSize VmaAllocation_T::GetOffset() const
7214{
7215 switch(m_Type)
7216 {
7217 case ALLOCATION_TYPE_BLOCK:
7218 return m_BlockAllocation.m_Offset;
7219 case ALLOCATION_TYPE_DEDICATED:
7220 return 0;
7221 default:
7222 VMA_ASSERT(0);
7223 return 0;
7224 }
7225}
7226
7227VkDeviceMemory VmaAllocation_T::GetMemory() const
7228{
7229 switch(m_Type)
7230 {
7231 case ALLOCATION_TYPE_BLOCK:
7232 return m_BlockAllocation.m_Block->GetDeviceMemory();
7233 case ALLOCATION_TYPE_DEDICATED:
7234 return m_DedicatedAllocation.m_hMemory;
7235 default:
7236 VMA_ASSERT(0);
7237 return VK_NULL_HANDLE;
7238 }
7239}
7240
7241uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
7242{
7243 switch(m_Type)
7244 {
7245 case ALLOCATION_TYPE_BLOCK:
7246 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
7247 case ALLOCATION_TYPE_DEDICATED:
7248 return m_DedicatedAllocation.m_MemoryTypeIndex;
7249 default:
7250 VMA_ASSERT(0);
7251 return UINT32_MAX;
7252 }
7253}
7254
7255void* VmaAllocation_T::GetMappedData() const
7256{
7257 switch(m_Type)
7258 {
7259 case ALLOCATION_TYPE_BLOCK:
7260 if(m_MapCount != 0)
7261 {
7262 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
7263 VMA_ASSERT(pBlockData != VMA_NULL);
7264 return (char*)pBlockData + m_BlockAllocation.m_Offset;
7265 }
7266 else
7267 {
7268 return VMA_NULL;
7269 }
7270 break;
7271 case ALLOCATION_TYPE_DEDICATED:
7272 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
7273 return m_DedicatedAllocation.m_pMappedData;
7274 default:
7275 VMA_ASSERT(0);
7276 return VMA_NULL;
7277 }
7278}
7279
7280bool VmaAllocation_T::CanBecomeLost() const
7281{
7282 switch(m_Type)
7283 {
7284 case ALLOCATION_TYPE_BLOCK:
7285 return m_BlockAllocation.m_CanBecomeLost;
7286 case ALLOCATION_TYPE_DEDICATED:
7287 return false;
7288 default:
7289 VMA_ASSERT(0);
7290 return false;
7291 }
7292}
7293
7294VmaPool VmaAllocation_T::GetPool() const
7295{
7296 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
7297 return m_BlockAllocation.m_hPool;
7298}
7299
7300bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
7301{
7302 VMA_ASSERT(CanBecomeLost());
7303
7304 /*
7305 Warning: This is a carefully designed algorithm.
7306 Do not modify unless you really know what you're doing :)
7307 */
7308 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
7309 for(;;)
7310 {
7311 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
7312 {
7313 VMA_ASSERT(0);
7314 return false;
7315 }
7316 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
7317 {
7318 return false;
7319 }
7320 else // Last use time earlier than current time.
7321 {
7322 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
7323 {
7324 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
7325 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
7326 return true;
7327 }
7328 }
7329 }
7330}
7331
7332#if VMA_STATS_STRING_ENABLED
7333
7334// Correspond to values of enum VmaSuballocationType.
7335static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
7336 "FREE",
7337 "UNKNOWN",
7338 "BUFFER",
7339 "IMAGE_UNKNOWN",
7340 "IMAGE_LINEAR",
7341 "IMAGE_OPTIMAL",
7342};
7343
7344void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
7345{
7346 json.WriteString("Type");
7347 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
7348
7349 json.WriteString("Size");
7350 json.WriteNumber(m_Size);
7351
7352 if(m_pUserData != VMA_NULL)
7353 {
7354 json.WriteString("UserData");
7355 if(IsUserDataString())
7356 {
7357 json.WriteString((const char*)m_pUserData);
7358 }
7359 else
7360 {
7361 json.BeginString();
7362 json.ContinueString_Pointer(m_pUserData);
7363 json.EndString();
7364 }
7365 }
7366
7367 json.WriteString("CreationFrameIndex");
7368 json.WriteNumber(m_CreationFrameIndex);
7369
7370 json.WriteString("LastUseFrameIndex");
7371 json.WriteNumber(GetLastUseFrameIndex());
7372
7373 if(m_BufferImageUsage != 0)
7374 {
7375 json.WriteString("Usage");
7376 json.WriteNumber(m_BufferImageUsage);
7377 }
7378}
7379
7380#endif
7381
7382void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
7383{
7384 VMA_ASSERT(IsUserDataString());
7385 if(m_pUserData != VMA_NULL)
7386 {
7387 char* const oldStr = (char*)m_pUserData;
7388 const size_t oldStrLen = strlen(oldStr);
7389 vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
7390 m_pUserData = VMA_NULL;
7391 }
7392}
7393
7394void VmaAllocation_T::BlockAllocMap()
7395{
7396 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7397
7398 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7399 {
7400 ++m_MapCount;
7401 }
7402 else
7403 {
7404 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
7405 }
7406}
7407
7408void VmaAllocation_T::BlockAllocUnmap()
7409{
7410 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
7411
7412 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7413 {
7414 --m_MapCount;
7415 }
7416 else
7417 {
7418 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
7419 }
7420}
7421
7422VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
7423{
7424 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7425
7426 if(m_MapCount != 0)
7427 {
7428 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
7429 {
7430 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
7431 *ppData = m_DedicatedAllocation.m_pMappedData;
7432 ++m_MapCount;
7433 return VK_SUCCESS;
7434 }
7435 else
7436 {
7437 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
7438 return VK_ERROR_MEMORY_MAP_FAILED;
7439 }
7440 }
7441 else
7442 {
7443 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
7444 hAllocator->m_hDevice,
7445 m_DedicatedAllocation.m_hMemory,
7446 0, // offset
7447 VK_WHOLE_SIZE,
7448 0, // flags
7449 ppData);
7450 if(result == VK_SUCCESS)
7451 {
7452 m_DedicatedAllocation.m_pMappedData = *ppData;
7453 m_MapCount = 1;
7454 }
7455 return result;
7456 }
7457}
7458
7459void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
7460{
7461 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
7462
7463 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
7464 {
7465 --m_MapCount;
7466 if(m_MapCount == 0)
7467 {
7468 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
7469 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
7470 hAllocator->m_hDevice,
7471 m_DedicatedAllocation.m_hMemory);
7472 }
7473 }
7474 else
7475 {
7476 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
7477 }
7478}
7479
7480#if VMA_STATS_STRING_ENABLED
7481
7482static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
7483{
7484 json.BeginObject();
7485
7486 json.WriteString("Blocks");
7487 json.WriteNumber(stat.blockCount);
7488
7489 json.WriteString("Allocations");
7490 json.WriteNumber(stat.allocationCount);
7491
7492 json.WriteString("UnusedRanges");
7493 json.WriteNumber(stat.unusedRangeCount);
7494
7495 json.WriteString("UsedBytes");
7496 json.WriteNumber(stat.usedBytes);
7497
7498 json.WriteString("UnusedBytes");
7499 json.WriteNumber(stat.unusedBytes);
7500
7501 if(stat.allocationCount > 1)
7502 {
7503 json.WriteString("AllocationSize");
7504 json.BeginObject(true);
7505 json.WriteString("Min");
7506 json.WriteNumber(stat.allocationSizeMin);
7507 json.WriteString("Avg");
7508 json.WriteNumber(stat.allocationSizeAvg);
7509 json.WriteString("Max");
7510 json.WriteNumber(stat.allocationSizeMax);
7511 json.EndObject();
7512 }
7513
7514 if(stat.unusedRangeCount > 1)
7515 {
7516 json.WriteString("UnusedRangeSize");
7517 json.BeginObject(true);
7518 json.WriteString("Min");
7519 json.WriteNumber(stat.unusedRangeSizeMin);
7520 json.WriteString("Avg");
7521 json.WriteNumber(stat.unusedRangeSizeAvg);
7522 json.WriteString("Max");
7523 json.WriteNumber(stat.unusedRangeSizeMax);
7524 json.EndObject();
7525 }
7526
7527 json.EndObject();
7528}
7529
7530#endif // #if VMA_STATS_STRING_ENABLED
7531
7532struct VmaSuballocationItemSizeLess
7533{
7534 bool operator()(
7535 const VmaSuballocationList::iterator lhs,
7536 const VmaSuballocationList::iterator rhs) const
7537 {
7538 return lhs->size < rhs->size;
7539 }
7540 bool operator()(
7541 const VmaSuballocationList::iterator lhs,
7542 VkDeviceSize rhsSize) const
7543 {
7544 return lhs->size < rhsSize;
7545 }
7546};
7547
7548
7549////////////////////////////////////////////////////////////////////////////////
7550// class VmaBlockMetadata
7551
7552VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
7553 m_Size(0),
7554 m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
7555{
7556}
7557
7558#if VMA_STATS_STRING_ENABLED
7559
7560void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
7561 VkDeviceSize unusedBytes,
7562 size_t allocationCount,
7563 size_t unusedRangeCount) const
7564{
7565 json.BeginObject();
7566
7567 json.WriteString("TotalBytes");
7568 json.WriteNumber(GetSize());
7569
7570 json.WriteString("UnusedBytes");
7571 json.WriteNumber(unusedBytes);
7572
7573 json.WriteString("Allocations");
7574 json.WriteNumber((uint64_t)allocationCount);
7575
7576 json.WriteString("UnusedRanges");
7577 json.WriteNumber((uint64_t)unusedRangeCount);
7578
7579 json.WriteString("Suballocations");
7580 json.BeginArray();
7581}
7582
7583void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
7584 VkDeviceSize offset,
7585 VmaAllocation hAllocation) const
7586{
7587 json.BeginObject(true);
7588
7589 json.WriteString("Offset");
7590 json.WriteNumber(offset);
7591
7592 hAllocation->PrintParameters(json);
7593
7594 json.EndObject();
7595}
7596
7597void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
7598 VkDeviceSize offset,
7599 VkDeviceSize size) const
7600{
7601 json.BeginObject(true);
7602
7603 json.WriteString("Offset");
7604 json.WriteNumber(offset);
7605
7606 json.WriteString("Type");
7607 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
7608
7609 json.WriteString("Size");
7610 json.WriteNumber(size);
7611
7612 json.EndObject();
7613}
7614
7615void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
7616{
7617 json.EndArray();
7618 json.EndObject();
7619}
7620
7621#endif // #if VMA_STATS_STRING_ENABLED
7622
7623////////////////////////////////////////////////////////////////////////////////
7624// class VmaBlockMetadata_Generic
7625
7626VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
7627 VmaBlockMetadata(hAllocator),
7628 m_FreeCount(0),
7629 m_SumFreeSize(0),
7630 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
7631 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
7632{
7633}
7634
7635VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
7636{
7637}
7638
7639void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
7640{
7641 VmaBlockMetadata::Init(size);
7642
7643 m_FreeCount = 1;
7644 m_SumFreeSize = size;
7645
7646 VmaSuballocation suballoc = {};
7647 suballoc.offset = 0;
7648 suballoc.size = size;
7649 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7650 suballoc.hAllocation = VK_NULL_HANDLE;
7651
7652 VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
7653 m_Suballocations.push_back(suballoc);
7654 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
7655 --suballocItem;
7656 m_FreeSuballocationsBySize.push_back(suballocItem);
7657}
7658
7659bool VmaBlockMetadata_Generic::Validate() const
7660{
7661 VMA_VALIDATE(!m_Suballocations.empty());
7662
7663 // Expected offset of new suballocation as calculated from previous ones.
7664 VkDeviceSize calculatedOffset = 0;
7665 // Expected number of free suballocations as calculated from traversing their list.
7666 uint32_t calculatedFreeCount = 0;
7667 // Expected sum size of free suballocations as calculated from traversing their list.
7668 VkDeviceSize calculatedSumFreeSize = 0;
7669 // Expected number of free suballocations that should be registered in
7670 // m_FreeSuballocationsBySize calculated from traversing their list.
7671 size_t freeSuballocationsToRegister = 0;
7672 // True if previous visited suballocation was free.
7673 bool prevFree = false;
7674
7675 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7676 suballocItem != m_Suballocations.cend();
7677 ++suballocItem)
7678 {
7679 const VmaSuballocation& subAlloc = *suballocItem;
7680
7681 // Actual offset of this suballocation doesn't match expected one.
7682 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
7683
7684 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
7685 // Two adjacent free suballocations are invalid. They should be merged.
7686 VMA_VALIDATE(!prevFree || !currFree);
7687
7688 VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
7689
7690 if(currFree)
7691 {
7692 calculatedSumFreeSize += subAlloc.size;
7693 ++calculatedFreeCount;
7694 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
7695 {
7696 ++freeSuballocationsToRegister;
7697 }
7698
7699 // Margin required between allocations - every free space must be at least that large.
7700 VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
7701 }
7702 else
7703 {
7704 VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
7705 VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
7706
7707 // Margin required between allocations - previous allocation must be free.
7708 VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
7709 }
7710
7711 calculatedOffset += subAlloc.size;
7712 prevFree = currFree;
7713 }
7714
7715 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
7716 // match expected one.
7717 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
7718
7719 VkDeviceSize lastSize = 0;
7720 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
7721 {
7722 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
7723
7724 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
7725 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7726 // They must be sorted by size ascending.
7727 VMA_VALIDATE(suballocItem->size >= lastSize);
7728
7729 lastSize = suballocItem->size;
7730 }
7731
7732 // Check if totals match calculacted values.
7733 VMA_VALIDATE(ValidateFreeSuballocationList());
7734 VMA_VALIDATE(calculatedOffset == GetSize());
7735 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
7736 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
7737
7738 return true;
7739}
7740
7741VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
7742{
7743 if(!m_FreeSuballocationsBySize.empty())
7744 {
7745 return m_FreeSuballocationsBySize.back()->size;
7746 }
7747 else
7748 {
7749 return 0;
7750 }
7751}
7752
7753bool VmaBlockMetadata_Generic::IsEmpty() const
7754{
7755 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
7756}
7757
7758void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
7759{
7760 outInfo.blockCount = 1;
7761
7762 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7763 outInfo.allocationCount = rangeCount - m_FreeCount;
7764 outInfo.unusedRangeCount = m_FreeCount;
7765
7766 outInfo.unusedBytes = m_SumFreeSize;
7767 outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
7768
7769 outInfo.allocationSizeMin = UINT64_MAX;
7770 outInfo.allocationSizeMax = 0;
7771 outInfo.unusedRangeSizeMin = UINT64_MAX;
7772 outInfo.unusedRangeSizeMax = 0;
7773
7774 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7775 suballocItem != m_Suballocations.cend();
7776 ++suballocItem)
7777 {
7778 const VmaSuballocation& suballoc = *suballocItem;
7779 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7780 {
7781 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
7782 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
7783 }
7784 else
7785 {
7786 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
7787 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
7788 }
7789 }
7790}
7791
7792void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
7793{
7794 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
7795
7796 inoutStats.size += GetSize();
7797 inoutStats.unusedSize += m_SumFreeSize;
7798 inoutStats.allocationCount += rangeCount - m_FreeCount;
7799 inoutStats.unusedRangeCount += m_FreeCount;
7800 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
7801}
7802
7803#if VMA_STATS_STRING_ENABLED
7804
7805void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
7806{
7807 PrintDetailedMap_Begin(json,
7808 m_SumFreeSize, // unusedBytes
7809 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
7810 m_FreeCount); // unusedRangeCount
7811
7812 size_t i = 0;
7813 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
7814 suballocItem != m_Suballocations.cend();
7815 ++suballocItem, ++i)
7816 {
7817 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7818 {
7819 PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
7820 }
7821 else
7822 {
7823 PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
7824 }
7825 }
7826
7827 PrintDetailedMap_End(json);
7828}
7829
7830#endif // #if VMA_STATS_STRING_ENABLED
7831
7832bool VmaBlockMetadata_Generic::CreateAllocationRequest(
7833 uint32_t currentFrameIndex,
7834 uint32_t frameInUseCount,
7835 VkDeviceSize bufferImageGranularity,
7836 VkDeviceSize allocSize,
7837 VkDeviceSize allocAlignment,
7838 bool upperAddress,
7839 VmaSuballocationType allocType,
7840 bool canMakeOtherLost,
7841 uint32_t strategy,
7842 VmaAllocationRequest* pAllocationRequest)
7843{
7844 VMA_ASSERT(allocSize > 0);
7845 VMA_ASSERT(!upperAddress);
7846 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7847 VMA_ASSERT(pAllocationRequest != VMA_NULL);
7848 VMA_HEAVY_ASSERT(Validate());
7849
7850 // There is not enough total free space in this block to fullfill the request: Early return.
7851 if(canMakeOtherLost == false &&
7852 m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
7853 {
7854 return false;
7855 }
7856
7857 // New algorithm, efficiently searching freeSuballocationsBySize.
7858 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7859 if(freeSuballocCount > 0)
7860 {
7861 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
7862 {
7863 // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
7864 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7865 m_FreeSuballocationsBySize.data(),
7866 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7867 allocSize + 2 * VMA_DEBUG_MARGIN,
7868 VmaSuballocationItemSizeLess());
7869 size_t index = it - m_FreeSuballocationsBySize.data();
7870 for(; index < freeSuballocCount; ++index)
7871 {
7872 if(CheckAllocation(
7873 currentFrameIndex,
7874 frameInUseCount,
7875 bufferImageGranularity,
7876 allocSize,
7877 allocAlignment,
7878 allocType,
7879 m_FreeSuballocationsBySize[index],
7880 false, // canMakeOtherLost
7881 &pAllocationRequest->offset,
7882 &pAllocationRequest->itemsToMakeLostCount,
7883 &pAllocationRequest->sumFreeSize,
7884 &pAllocationRequest->sumItemSize))
7885 {
7886 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7887 return true;
7888 }
7889 }
7890 }
7891 else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7892 {
7893 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
7894 it != m_Suballocations.end();
7895 ++it)
7896 {
7897 if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7898 currentFrameIndex,
7899 frameInUseCount,
7900 bufferImageGranularity,
7901 allocSize,
7902 allocAlignment,
7903 allocType,
7904 it,
7905 false, // canMakeOtherLost
7906 &pAllocationRequest->offset,
7907 &pAllocationRequest->itemsToMakeLostCount,
7908 &pAllocationRequest->sumFreeSize,
7909 &pAllocationRequest->sumItemSize))
7910 {
7911 pAllocationRequest->item = it;
7912 return true;
7913 }
7914 }
7915 }
7916 else // WORST_FIT, FIRST_FIT
7917 {
7918 // Search staring from biggest suballocations.
7919 for(size_t index = freeSuballocCount; index--; )
7920 {
7921 if(CheckAllocation(
7922 currentFrameIndex,
7923 frameInUseCount,
7924 bufferImageGranularity,
7925 allocSize,
7926 allocAlignment,
7927 allocType,
7928 m_FreeSuballocationsBySize[index],
7929 false, // canMakeOtherLost
7930 &pAllocationRequest->offset,
7931 &pAllocationRequest->itemsToMakeLostCount,
7932 &pAllocationRequest->sumFreeSize,
7933 &pAllocationRequest->sumItemSize))
7934 {
7935 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7936 return true;
7937 }
7938 }
7939 }
7940 }
7941
7942 if(canMakeOtherLost)
7943 {
7944 // Brute-force algorithm. TODO: Come up with something better.
7945
7946 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
7947 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
7948
7949 VmaAllocationRequest tmpAllocRequest = {};
7950 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
7951 suballocIt != m_Suballocations.end();
7952 ++suballocIt)
7953 {
7954 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
7955 suballocIt->hAllocation->CanBecomeLost())
7956 {
7957 if(CheckAllocation(
7958 currentFrameIndex,
7959 frameInUseCount,
7960 bufferImageGranularity,
7961 allocSize,
7962 allocAlignment,
7963 allocType,
7964 suballocIt,
7965 canMakeOtherLost,
7966 &tmpAllocRequest.offset,
7967 &tmpAllocRequest.itemsToMakeLostCount,
7968 &tmpAllocRequest.sumFreeSize,
7969 &tmpAllocRequest.sumItemSize))
7970 {
7971 tmpAllocRequest.item = suballocIt;
7972
7973 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
7974 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
7975 {
7976 *pAllocationRequest = tmpAllocRequest;
7977 }
7978 }
7979 }
7980 }
7981
7982 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
7983 {
7984 return true;
7985 }
7986 }
7987
7988 return false;
7989}
7990
7991bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
7992 uint32_t currentFrameIndex,
7993 uint32_t frameInUseCount,
7994 VmaAllocationRequest* pAllocationRequest)
7995{
7996 while(pAllocationRequest->itemsToMakeLostCount > 0)
7997 {
7998 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
7999 {
8000 ++pAllocationRequest->item;
8001 }
8002 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8003 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
8004 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
8005 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8006 {
8007 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
8008 --pAllocationRequest->itemsToMakeLostCount;
8009 }
8010 else
8011 {
8012 return false;
8013 }
8014 }
8015
8016 VMA_HEAVY_ASSERT(Validate());
8017 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
8018 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
8019
8020 return true;
8021}
8022
8023uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
8024{
8025 uint32_t lostAllocationCount = 0;
8026 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8027 it != m_Suballocations.end();
8028 ++it)
8029 {
8030 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
8031 it->hAllocation->CanBecomeLost() &&
8032 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
8033 {
8034 it = FreeSuballocation(it);
8035 ++lostAllocationCount;
8036 }
8037 }
8038 return lostAllocationCount;
8039}
8040
8041VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
8042{
8043 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
8044 it != m_Suballocations.end();
8045 ++it)
8046 {
8047 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
8048 {
8049 if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
8050 {
8051 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
8052 return VK_ERROR_VALIDATION_FAILED_EXT;
8053 }
8054 if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
8055 {
8056 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8057 return VK_ERROR_VALIDATION_FAILED_EXT;
8058 }
8059 }
8060 }
8061
8062 return VK_SUCCESS;
8063}
8064
8065void VmaBlockMetadata_Generic::Alloc(
8066 const VmaAllocationRequest& request,
8067 VmaSuballocationType type,
8068 VkDeviceSize allocSize,
8069 bool upperAddress,
8070 VmaAllocation hAllocation)
8071{
8072 VMA_ASSERT(!upperAddress);
8073 VMA_ASSERT(request.item != m_Suballocations.end());
8074 VmaSuballocation& suballoc = *request.item;
8075 // Given suballocation is a free block.
8076 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8077 // Given offset is inside this suballocation.
8078 VMA_ASSERT(request.offset >= suballoc.offset);
8079 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
8080 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
8081 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
8082
8083 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
8084 // it to become used.
8085 UnregisterFreeSuballocation(request.item);
8086
8087 suballoc.offset = request.offset;
8088 suballoc.size = allocSize;
8089 suballoc.type = type;
8090 suballoc.hAllocation = hAllocation;
8091
8092 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
8093 if(paddingEnd)
8094 {
8095 VmaSuballocation paddingSuballoc = {};
8096 paddingSuballoc.offset = request.offset + allocSize;
8097 paddingSuballoc.size = paddingEnd;
8098 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8099 VmaSuballocationList::iterator next = request.item;
8100 ++next;
8101 const VmaSuballocationList::iterator paddingEndItem =
8102 m_Suballocations.insert(next, paddingSuballoc);
8103 RegisterFreeSuballocation(paddingEndItem);
8104 }
8105
8106 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
8107 if(paddingBegin)
8108 {
8109 VmaSuballocation paddingSuballoc = {};
8110 paddingSuballoc.offset = request.offset - paddingBegin;
8111 paddingSuballoc.size = paddingBegin;
8112 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8113 const VmaSuballocationList::iterator paddingBeginItem =
8114 m_Suballocations.insert(request.item, paddingSuballoc);
8115 RegisterFreeSuballocation(paddingBeginItem);
8116 }
8117
8118 // Update totals.
8119 m_FreeCount = m_FreeCount - 1;
8120 if(paddingBegin > 0)
8121 {
8122 ++m_FreeCount;
8123 }
8124 if(paddingEnd > 0)
8125 {
8126 ++m_FreeCount;
8127 }
8128 m_SumFreeSize -= allocSize;
8129}
8130
8131void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
8132{
8133 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8134 suballocItem != m_Suballocations.end();
8135 ++suballocItem)
8136 {
8137 VmaSuballocation& suballoc = *suballocItem;
8138 if(suballoc.hAllocation == allocation)
8139 {
8140 FreeSuballocation(suballocItem);
8141 VMA_HEAVY_ASSERT(Validate());
8142 return;
8143 }
8144 }
8145 VMA_ASSERT(0 && "Not found!");
8146}
8147
8148void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
8149{
8150 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
8151 suballocItem != m_Suballocations.end();
8152 ++suballocItem)
8153 {
8154 VmaSuballocation& suballoc = *suballocItem;
8155 if(suballoc.offset == offset)
8156 {
8157 FreeSuballocation(suballocItem);
8158 return;
8159 }
8160 }
8161 VMA_ASSERT(0 && "Not found!");
8162}
8163
8164bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
8165{
8166 typedef VmaSuballocationList::iterator iter_type;
8167 for(iter_type suballocItem = m_Suballocations.begin();
8168 suballocItem != m_Suballocations.end();
8169 ++suballocItem)
8170 {
8171 VmaSuballocation& suballoc = *suballocItem;
8172 if(suballoc.hAllocation == alloc)
8173 {
8174 iter_type nextItem = suballocItem;
8175 ++nextItem;
8176
8177 // Should have been ensured on higher level.
8178 VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
8179
8180 // Shrinking.
8181 if(newSize < alloc->GetSize())
8182 {
8183 const VkDeviceSize sizeDiff = suballoc.size - newSize;
8184
8185 // There is next item.
8186 if(nextItem != m_Suballocations.end())
8187 {
8188 // Next item is free.
8189 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8190 {
8191 // Grow this next item backward.
8192 UnregisterFreeSuballocation(nextItem);
8193 nextItem->offset -= sizeDiff;
8194 nextItem->size += sizeDiff;
8195 RegisterFreeSuballocation(nextItem);
8196 }
8197 // Next item is not free.
8198 else
8199 {
8200 // Create free item after current one.
8201 VmaSuballocation newFreeSuballoc;
8202 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8203 newFreeSuballoc.offset = suballoc.offset + newSize;
8204 newFreeSuballoc.size = sizeDiff;
8205 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8206 iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
8207 RegisterFreeSuballocation(newFreeSuballocIt);
8208
8209 ++m_FreeCount;
8210 }
8211 }
8212 // This is the last item.
8213 else
8214 {
8215 // Create free item at the end.
8216 VmaSuballocation newFreeSuballoc;
8217 newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
8218 newFreeSuballoc.offset = suballoc.offset + newSize;
8219 newFreeSuballoc.size = sizeDiff;
8220 newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8221 m_Suballocations.push_back(newFreeSuballoc);
8222
8223 iter_type newFreeSuballocIt = m_Suballocations.end();
8224 RegisterFreeSuballocation(--newFreeSuballocIt);
8225
8226 ++m_FreeCount;
8227 }
8228
8229 suballoc.size = newSize;
8230 m_SumFreeSize += sizeDiff;
8231 }
8232 // Growing.
8233 else
8234 {
8235 const VkDeviceSize sizeDiff = newSize - suballoc.size;
8236
8237 // There is next item.
8238 if(nextItem != m_Suballocations.end())
8239 {
8240 // Next item is free.
8241 if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8242 {
8243 // There is not enough free space, including margin.
8244 if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
8245 {
8246 return false;
8247 }
8248
8249 // There is more free space than required.
8250 if(nextItem->size > sizeDiff)
8251 {
8252 // Move and shrink this next item.
8253 UnregisterFreeSuballocation(nextItem);
8254 nextItem->offset += sizeDiff;
8255 nextItem->size -= sizeDiff;
8256 RegisterFreeSuballocation(nextItem);
8257 }
8258 // There is exactly the amount of free space required.
8259 else
8260 {
8261 // Remove this next free item.
8262 UnregisterFreeSuballocation(nextItem);
8263 m_Suballocations.erase(nextItem);
8264 --m_FreeCount;
8265 }
8266 }
8267 // Next item is not free - there is no space to grow.
8268 else
8269 {
8270 return false;
8271 }
8272 }
8273 // This is the last item - there is no space to grow.
8274 else
8275 {
8276 return false;
8277 }
8278
8279 suballoc.size = newSize;
8280 m_SumFreeSize -= sizeDiff;
8281 }
8282
8283 // We cannot call Validate() here because alloc object is updated to new size outside of this call.
8284 return true;
8285 }
8286 }
8287 VMA_ASSERT(0 && "Not found!");
8288 return false;
8289}
8290
8291bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
8292{
8293 VkDeviceSize lastSize = 0;
8294 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
8295 {
8296 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
8297
8298 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
8299 VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
8300 VMA_VALIDATE(it->size >= lastSize);
8301 lastSize = it->size;
8302 }
8303 return true;
8304}
8305
8306bool VmaBlockMetadata_Generic::CheckAllocation(
8307 uint32_t currentFrameIndex,
8308 uint32_t frameInUseCount,
8309 VkDeviceSize bufferImageGranularity,
8310 VkDeviceSize allocSize,
8311 VkDeviceSize allocAlignment,
8312 VmaSuballocationType allocType,
8313 VmaSuballocationList::const_iterator suballocItem,
8314 bool canMakeOtherLost,
8315 VkDeviceSize* pOffset,
8316 size_t* itemsToMakeLostCount,
8317 VkDeviceSize* pSumFreeSize,
8318 VkDeviceSize* pSumItemSize) const
8319{
8320 VMA_ASSERT(allocSize > 0);
8321 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8322 VMA_ASSERT(suballocItem != m_Suballocations.cend());
8323 VMA_ASSERT(pOffset != VMA_NULL);
8324
8325 *itemsToMakeLostCount = 0;
8326 *pSumFreeSize = 0;
8327 *pSumItemSize = 0;
8328
8329 if(canMakeOtherLost)
8330 {
8331 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8332 {
8333 *pSumFreeSize = suballocItem->size;
8334 }
8335 else
8336 {
8337 if(suballocItem->hAllocation->CanBecomeLost() &&
8338 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8339 {
8340 ++*itemsToMakeLostCount;
8341 *pSumItemSize = suballocItem->size;
8342 }
8343 else
8344 {
8345 return false;
8346 }
8347 }
8348
8349 // Remaining size is too small for this request: Early return.
8350 if(GetSize() - suballocItem->offset < allocSize)
8351 {
8352 return false;
8353 }
8354
8355 // Start from offset equal to beginning of this suballocation.
8356 *pOffset = suballocItem->offset;
8357
8358 // Apply VMA_DEBUG_MARGIN at the beginning.
8359 if(VMA_DEBUG_MARGIN > 0)
8360 {
8361 *pOffset += VMA_DEBUG_MARGIN;
8362 }
8363
8364 // Apply alignment.
8365 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8366
8367 // Check previous suballocations for BufferImageGranularity conflicts.
8368 // Make bigger alignment if necessary.
8369 if(bufferImageGranularity > 1)
8370 {
8371 bool bufferImageGranularityConflict = false;
8372 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8373 while(prevSuballocItem != m_Suballocations.cbegin())
8374 {
8375 --prevSuballocItem;
8376 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8377 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8378 {
8379 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8380 {
8381 bufferImageGranularityConflict = true;
8382 break;
8383 }
8384 }
8385 else
8386 // Already on previous page.
8387 break;
8388 }
8389 if(bufferImageGranularityConflict)
8390 {
8391 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8392 }
8393 }
8394
8395 // Now that we have final *pOffset, check if we are past suballocItem.
8396 // If yes, return false - this function should be called for another suballocItem as starting point.
8397 if(*pOffset >= suballocItem->offset + suballocItem->size)
8398 {
8399 return false;
8400 }
8401
8402 // Calculate padding at the beginning based on current offset.
8403 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
8404
8405 // Calculate required margin at the end.
8406 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8407
8408 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
8409 // Another early return check.
8410 if(suballocItem->offset + totalSize > GetSize())
8411 {
8412 return false;
8413 }
8414
8415 // Advance lastSuballocItem until desired size is reached.
8416 // Update itemsToMakeLostCount.
8417 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
8418 if(totalSize > suballocItem->size)
8419 {
8420 VkDeviceSize remainingSize = totalSize - suballocItem->size;
8421 while(remainingSize > 0)
8422 {
8423 ++lastSuballocItem;
8424 if(lastSuballocItem == m_Suballocations.cend())
8425 {
8426 return false;
8427 }
8428 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8429 {
8430 *pSumFreeSize += lastSuballocItem->size;
8431 }
8432 else
8433 {
8434 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
8435 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
8436 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8437 {
8438 ++*itemsToMakeLostCount;
8439 *pSumItemSize += lastSuballocItem->size;
8440 }
8441 else
8442 {
8443 return false;
8444 }
8445 }
8446 remainingSize = (lastSuballocItem->size < remainingSize) ?
8447 remainingSize - lastSuballocItem->size : 0;
8448 }
8449 }
8450
8451 // Check next suballocations for BufferImageGranularity conflicts.
8452 // If conflict exists, we must mark more allocations lost or fail.
8453 if(bufferImageGranularity > 1)
8454 {
8455 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
8456 ++nextSuballocItem;
8457 while(nextSuballocItem != m_Suballocations.cend())
8458 {
8459 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8460 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8461 {
8462 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8463 {
8464 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
8465 if(nextSuballoc.hAllocation->CanBecomeLost() &&
8466 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
8467 {
8468 ++*itemsToMakeLostCount;
8469 }
8470 else
8471 {
8472 return false;
8473 }
8474 }
8475 }
8476 else
8477 {
8478 // Already on next page.
8479 break;
8480 }
8481 ++nextSuballocItem;
8482 }
8483 }
8484 }
8485 else
8486 {
8487 const VmaSuballocation& suballoc = *suballocItem;
8488 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8489
8490 *pSumFreeSize = suballoc.size;
8491
8492 // Size of this suballocation is too small for this request: Early return.
8493 if(suballoc.size < allocSize)
8494 {
8495 return false;
8496 }
8497
8498 // Start from offset equal to beginning of this suballocation.
8499 *pOffset = suballoc.offset;
8500
8501 // Apply VMA_DEBUG_MARGIN at the beginning.
8502 if(VMA_DEBUG_MARGIN > 0)
8503 {
8504 *pOffset += VMA_DEBUG_MARGIN;
8505 }
8506
8507 // Apply alignment.
8508 *pOffset = VmaAlignUp(*pOffset, allocAlignment);
8509
8510 // Check previous suballocations for BufferImageGranularity conflicts.
8511 // Make bigger alignment if necessary.
8512 if(bufferImageGranularity > 1)
8513 {
8514 bool bufferImageGranularityConflict = false;
8515 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
8516 while(prevSuballocItem != m_Suballocations.cbegin())
8517 {
8518 --prevSuballocItem;
8519 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
8520 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
8521 {
8522 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8523 {
8524 bufferImageGranularityConflict = true;
8525 break;
8526 }
8527 }
8528 else
8529 // Already on previous page.
8530 break;
8531 }
8532 if(bufferImageGranularityConflict)
8533 {
8534 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
8535 }
8536 }
8537
8538 // Calculate padding at the beginning based on current offset.
8539 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
8540
8541 // Calculate required margin at the end.
8542 const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
8543
8544 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
8545 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
8546 {
8547 return false;
8548 }
8549
8550 // Check next suballocations for BufferImageGranularity conflicts.
8551 // If conflict exists, allocation cannot be made here.
8552 if(bufferImageGranularity > 1)
8553 {
8554 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
8555 ++nextSuballocItem;
8556 while(nextSuballocItem != m_Suballocations.cend())
8557 {
8558 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
8559 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8560 {
8561 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
8562 {
8563 return false;
8564 }
8565 }
8566 else
8567 {
8568 // Already on next page.
8569 break;
8570 }
8571 ++nextSuballocItem;
8572 }
8573 }
8574 }
8575
8576 // All tests passed: Success. pOffset is already filled.
8577 return true;
8578}
8579
8580void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
8581{
8582 VMA_ASSERT(item != m_Suballocations.end());
8583 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8584
8585 VmaSuballocationList::iterator nextItem = item;
8586 ++nextItem;
8587 VMA_ASSERT(nextItem != m_Suballocations.end());
8588 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
8589
8590 item->size += nextItem->size;
8591 --m_FreeCount;
8592 m_Suballocations.erase(nextItem);
8593}
8594
8595VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
8596{
8597 // Change this suballocation to be marked as free.
8598 VmaSuballocation& suballoc = *suballocItem;
8599 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8600 suballoc.hAllocation = VK_NULL_HANDLE;
8601
8602 // Update totals.
8603 ++m_FreeCount;
8604 m_SumFreeSize += suballoc.size;
8605
8606 // Merge with previous and/or next suballocation if it's also free.
8607 bool mergeWithNext = false;
8608 bool mergeWithPrev = false;
8609
8610 VmaSuballocationList::iterator nextItem = suballocItem;
8611 ++nextItem;
8612 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
8613 {
8614 mergeWithNext = true;
8615 }
8616
8617 VmaSuballocationList::iterator prevItem = suballocItem;
8618 if(suballocItem != m_Suballocations.begin())
8619 {
8620 --prevItem;
8621 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
8622 {
8623 mergeWithPrev = true;
8624 }
8625 }
8626
8627 if(mergeWithNext)
8628 {
8629 UnregisterFreeSuballocation(nextItem);
8630 MergeFreeWithNext(suballocItem);
8631 }
8632
8633 if(mergeWithPrev)
8634 {
8635 UnregisterFreeSuballocation(prevItem);
8636 MergeFreeWithNext(prevItem);
8637 RegisterFreeSuballocation(prevItem);
8638 return prevItem;
8639 }
8640 else
8641 {
8642 RegisterFreeSuballocation(suballocItem);
8643 return suballocItem;
8644 }
8645}
8646
8647void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
8648{
8649 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8650 VMA_ASSERT(item->size > 0);
8651
8652 // You may want to enable this validation at the beginning or at the end of
8653 // this function, depending on what do you want to check.
8654 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8655
8656 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8657 {
8658 if(m_FreeSuballocationsBySize.empty())
8659 {
8660 m_FreeSuballocationsBySize.push_back(item);
8661 }
8662 else
8663 {
8664 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
8665 }
8666 }
8667
8668 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8669}
8670
8671
8672void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
8673{
8674 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
8675 VMA_ASSERT(item->size > 0);
8676
8677 // You may want to enable this validation at the beginning or at the end of
8678 // this function, depending on what do you want to check.
8679 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8680
8681 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
8682 {
8683 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
8684 m_FreeSuballocationsBySize.data(),
8685 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
8686 item,
8687 VmaSuballocationItemSizeLess());
8688 for(size_t index = it - m_FreeSuballocationsBySize.data();
8689 index < m_FreeSuballocationsBySize.size();
8690 ++index)
8691 {
8692 if(m_FreeSuballocationsBySize[index] == item)
8693 {
8694 VmaVectorRemove(m_FreeSuballocationsBySize, index);
8695 return;
8696 }
8697 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
8698 }
8699 VMA_ASSERT(0 && "Not found.");
8700 }
8701
8702 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
8703}
8704
8705bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
8706 VkDeviceSize bufferImageGranularity,
8707 VmaSuballocationType& inOutPrevSuballocType) const
8708{
8709 if(bufferImageGranularity == 1 || IsEmpty())
8710 {
8711 return false;
8712 }
8713
8714 VkDeviceSize minAlignment = VK_WHOLE_SIZE;
8715 bool typeConflictFound = false;
8716 for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
8717 it != m_Suballocations.cend();
8718 ++it)
8719 {
8720 const VmaSuballocationType suballocType = it->type;
8721 if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
8722 {
8723 minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
8724 if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
8725 {
8726 typeConflictFound = true;
8727 }
8728 inOutPrevSuballocType = suballocType;
8729 }
8730 }
8731
8732 return typeConflictFound || minAlignment >= bufferImageGranularity;
8733}
8734
8735////////////////////////////////////////////////////////////////////////////////
8736// class VmaBlockMetadata_Linear
8737
8738VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
8739 VmaBlockMetadata(hAllocator),
8740 m_SumFreeSize(0),
8741 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8742 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
8743 m_1stVectorIndex(0),
8744 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
8745 m_1stNullItemsBeginCount(0),
8746 m_1stNullItemsMiddleCount(0),
8747 m_2ndNullItemsCount(0)
8748{
8749}
8750
8751VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
8752{
8753}
8754
8755void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
8756{
8757 VmaBlockMetadata::Init(size);
8758 m_SumFreeSize = size;
8759}
8760
8761bool VmaBlockMetadata_Linear::Validate() const
8762{
8763 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8764 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8765
8766 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
8767 VMA_VALIDATE(!suballocations1st.empty() ||
8768 suballocations2nd.empty() ||
8769 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
8770
8771 if(!suballocations1st.empty())
8772 {
8773 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
8774 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
8775 // Null item at the end should be just pop_back().
8776 VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
8777 }
8778 if(!suballocations2nd.empty())
8779 {
8780 // Null item at the end should be just pop_back().
8781 VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
8782 }
8783
8784 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
8785 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
8786
8787 VkDeviceSize sumUsedSize = 0;
8788 const size_t suballoc1stCount = suballocations1st.size();
8789 VkDeviceSize offset = VMA_DEBUG_MARGIN;
8790
8791 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8792 {
8793 const size_t suballoc2ndCount = suballocations2nd.size();
8794 size_t nullItem2ndCount = 0;
8795 for(size_t i = 0; i < suballoc2ndCount; ++i)
8796 {
8797 const VmaSuballocation& suballoc = suballocations2nd[i];
8798 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8799
8800 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8801 VMA_VALIDATE(suballoc.offset >= offset);
8802
8803 if(!currFree)
8804 {
8805 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8806 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8807 sumUsedSize += suballoc.size;
8808 }
8809 else
8810 {
8811 ++nullItem2ndCount;
8812 }
8813
8814 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8815 }
8816
8817 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8818 }
8819
8820 for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
8821 {
8822 const VmaSuballocation& suballoc = suballocations1st[i];
8823 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
8824 suballoc.hAllocation == VK_NULL_HANDLE);
8825 }
8826
8827 size_t nullItem1stCount = m_1stNullItemsBeginCount;
8828
8829 for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
8830 {
8831 const VmaSuballocation& suballoc = suballocations1st[i];
8832 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8833
8834 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8835 VMA_VALIDATE(suballoc.offset >= offset);
8836 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
8837
8838 if(!currFree)
8839 {
8840 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8841 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8842 sumUsedSize += suballoc.size;
8843 }
8844 else
8845 {
8846 ++nullItem1stCount;
8847 }
8848
8849 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8850 }
8851 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
8852
8853 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8854 {
8855 const size_t suballoc2ndCount = suballocations2nd.size();
8856 size_t nullItem2ndCount = 0;
8857 for(size_t i = suballoc2ndCount; i--; )
8858 {
8859 const VmaSuballocation& suballoc = suballocations2nd[i];
8860 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
8861
8862 VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
8863 VMA_VALIDATE(suballoc.offset >= offset);
8864
8865 if(!currFree)
8866 {
8867 VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
8868 VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
8869 sumUsedSize += suballoc.size;
8870 }
8871 else
8872 {
8873 ++nullItem2ndCount;
8874 }
8875
8876 offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
8877 }
8878
8879 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
8880 }
8881
8882 VMA_VALIDATE(offset <= GetSize());
8883 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
8884
8885 return true;
8886}
8887
8888size_t VmaBlockMetadata_Linear::GetAllocationCount() const
8889{
8890 return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
8891 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
8892}
8893
8894VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
8895{
8896 const VkDeviceSize size = GetSize();
8897
8898 /*
8899 We don't consider gaps inside allocation vectors with freed allocations because
8900 they are not suitable for reuse in linear allocator. We consider only space that
8901 is available for new allocations.
8902 */
8903 if(IsEmpty())
8904 {
8905 return size;
8906 }
8907
8908 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8909
8910 switch(m_2ndVectorMode)
8911 {
8912 case SECOND_VECTOR_EMPTY:
8913 /*
8914 Available space is after end of 1st, as well as before beginning of 1st (which
8915 whould make it a ring buffer).
8916 */
8917 {
8918 const size_t suballocations1stCount = suballocations1st.size();
8919 VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
8920 const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8921 const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
8922 return VMA_MAX(
8923 firstSuballoc.offset,
8924 size - (lastSuballoc.offset + lastSuballoc.size));
8925 }
8926 break;
8927
8928 case SECOND_VECTOR_RING_BUFFER:
8929 /*
8930 Available space is only between end of 2nd and beginning of 1st.
8931 */
8932 {
8933 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8934 const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
8935 const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
8936 return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
8937 }
8938 break;
8939
8940 case SECOND_VECTOR_DOUBLE_STACK:
8941 /*
8942 Available space is only between end of 1st and top of 2nd.
8943 */
8944 {
8945 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8946 const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
8947 const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
8948 return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
8949 }
8950 break;
8951
8952 default:
8953 VMA_ASSERT(0);
8954 return 0;
8955 }
8956}
8957
8958void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
8959{
8960 const VkDeviceSize size = GetSize();
8961 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8962 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8963 const size_t suballoc1stCount = suballocations1st.size();
8964 const size_t suballoc2ndCount = suballocations2nd.size();
8965
8966 outInfo.blockCount = 1;
8967 outInfo.allocationCount = (uint32_t)GetAllocationCount();
8968 outInfo.unusedRangeCount = 0;
8969 outInfo.usedBytes = 0;
8970 outInfo.allocationSizeMin = UINT64_MAX;
8971 outInfo.allocationSizeMax = 0;
8972 outInfo.unusedRangeSizeMin = UINT64_MAX;
8973 outInfo.unusedRangeSizeMax = 0;
8974
8975 VkDeviceSize lastOffset = 0;
8976
8977 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8978 {
8979 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8980 size_t nextAlloc2ndIndex = 0;
8981 while(lastOffset < freeSpace2ndTo1stEnd)
8982 {
8983 // Find next non-null allocation or move nextAllocIndex to the end.
8984 while(nextAlloc2ndIndex < suballoc2ndCount &&
8985 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
8986 {
8987 ++nextAlloc2ndIndex;
8988 }
8989
8990 // Found non-null allocation.
8991 if(nextAlloc2ndIndex < suballoc2ndCount)
8992 {
8993 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8994
8995 // 1. Process free space before this allocation.
8996 if(lastOffset < suballoc.offset)
8997 {
8998 // There is free space from lastOffset to suballoc.offset.
8999 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9000 ++outInfo.unusedRangeCount;
9001 outInfo.unusedBytes += unusedRangeSize;
9002 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9003 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9004 }
9005
9006 // 2. Process this allocation.
9007 // There is allocation with suballoc.offset, suballoc.size.
9008 outInfo.usedBytes += suballoc.size;
9009 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9010 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9011
9012 // 3. Prepare for next iteration.
9013 lastOffset = suballoc.offset + suballoc.size;
9014 ++nextAlloc2ndIndex;
9015 }
9016 // We are at the end.
9017 else
9018 {
9019 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9020 if(lastOffset < freeSpace2ndTo1stEnd)
9021 {
9022 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9023 ++outInfo.unusedRangeCount;
9024 outInfo.unusedBytes += unusedRangeSize;
9025 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9026 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9027 }
9028
9029 // End of loop.
9030 lastOffset = freeSpace2ndTo1stEnd;
9031 }
9032 }
9033 }
9034
9035 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9036 const VkDeviceSize freeSpace1stTo2ndEnd =
9037 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9038 while(lastOffset < freeSpace1stTo2ndEnd)
9039 {
9040 // Find next non-null allocation or move nextAllocIndex to the end.
9041 while(nextAlloc1stIndex < suballoc1stCount &&
9042 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9043 {
9044 ++nextAlloc1stIndex;
9045 }
9046
9047 // Found non-null allocation.
9048 if(nextAlloc1stIndex < suballoc1stCount)
9049 {
9050 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9051
9052 // 1. Process free space before this allocation.
9053 if(lastOffset < suballoc.offset)
9054 {
9055 // There is free space from lastOffset to suballoc.offset.
9056 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9057 ++outInfo.unusedRangeCount;
9058 outInfo.unusedBytes += unusedRangeSize;
9059 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9060 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9061 }
9062
9063 // 2. Process this allocation.
9064 // There is allocation with suballoc.offset, suballoc.size.
9065 outInfo.usedBytes += suballoc.size;
9066 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9067 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9068
9069 // 3. Prepare for next iteration.
9070 lastOffset = suballoc.offset + suballoc.size;
9071 ++nextAlloc1stIndex;
9072 }
9073 // We are at the end.
9074 else
9075 {
9076 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9077 if(lastOffset < freeSpace1stTo2ndEnd)
9078 {
9079 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9080 ++outInfo.unusedRangeCount;
9081 outInfo.unusedBytes += unusedRangeSize;
9082 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9083 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9084 }
9085
9086 // End of loop.
9087 lastOffset = freeSpace1stTo2ndEnd;
9088 }
9089 }
9090
9091 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9092 {
9093 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9094 while(lastOffset < size)
9095 {
9096 // Find next non-null allocation or move nextAllocIndex to the end.
9097 while(nextAlloc2ndIndex != SIZE_MAX &&
9098 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9099 {
9100 --nextAlloc2ndIndex;
9101 }
9102
9103 // Found non-null allocation.
9104 if(nextAlloc2ndIndex != SIZE_MAX)
9105 {
9106 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9107
9108 // 1. Process free space before this allocation.
9109 if(lastOffset < suballoc.offset)
9110 {
9111 // There is free space from lastOffset to suballoc.offset.
9112 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9113 ++outInfo.unusedRangeCount;
9114 outInfo.unusedBytes += unusedRangeSize;
9115 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9116 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9117 }
9118
9119 // 2. Process this allocation.
9120 // There is allocation with suballoc.offset, suballoc.size.
9121 outInfo.usedBytes += suballoc.size;
9122 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
9123 outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
9124
9125 // 3. Prepare for next iteration.
9126 lastOffset = suballoc.offset + suballoc.size;
9127 --nextAlloc2ndIndex;
9128 }
9129 // We are at the end.
9130 else
9131 {
9132 // There is free space from lastOffset to size.
9133 if(lastOffset < size)
9134 {
9135 const VkDeviceSize unusedRangeSize = size - lastOffset;
9136 ++outInfo.unusedRangeCount;
9137 outInfo.unusedBytes += unusedRangeSize;
9138 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
9139 outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
9140 }
9141
9142 // End of loop.
9143 lastOffset = size;
9144 }
9145 }
9146 }
9147
9148 outInfo.unusedBytes = size - outInfo.usedBytes;
9149}
9150
9151void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
9152{
9153 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9154 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9155 const VkDeviceSize size = GetSize();
9156 const size_t suballoc1stCount = suballocations1st.size();
9157 const size_t suballoc2ndCount = suballocations2nd.size();
9158
9159 inoutStats.size += size;
9160
9161 VkDeviceSize lastOffset = 0;
9162
9163 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9164 {
9165 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9166 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
9167 while(lastOffset < freeSpace2ndTo1stEnd)
9168 {
9169 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9170 while(nextAlloc2ndIndex < suballoc2ndCount &&
9171 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9172 {
9173 ++nextAlloc2ndIndex;
9174 }
9175
9176 // Found non-null allocation.
9177 if(nextAlloc2ndIndex < suballoc2ndCount)
9178 {
9179 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9180
9181 // 1. Process free space before this allocation.
9182 if(lastOffset < suballoc.offset)
9183 {
9184 // There is free space from lastOffset to suballoc.offset.
9185 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9186 inoutStats.unusedSize += unusedRangeSize;
9187 ++inoutStats.unusedRangeCount;
9188 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9189 }
9190
9191 // 2. Process this allocation.
9192 // There is allocation with suballoc.offset, suballoc.size.
9193 ++inoutStats.allocationCount;
9194
9195 // 3. Prepare for next iteration.
9196 lastOffset = suballoc.offset + suballoc.size;
9197 ++nextAlloc2ndIndex;
9198 }
9199 // We are at the end.
9200 else
9201 {
9202 if(lastOffset < freeSpace2ndTo1stEnd)
9203 {
9204 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9205 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9206 inoutStats.unusedSize += unusedRangeSize;
9207 ++inoutStats.unusedRangeCount;
9208 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9209 }
9210
9211 // End of loop.
9212 lastOffset = freeSpace2ndTo1stEnd;
9213 }
9214 }
9215 }
9216
9217 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9218 const VkDeviceSize freeSpace1stTo2ndEnd =
9219 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9220 while(lastOffset < freeSpace1stTo2ndEnd)
9221 {
9222 // Find next non-null allocation or move nextAllocIndex to the end.
9223 while(nextAlloc1stIndex < suballoc1stCount &&
9224 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9225 {
9226 ++nextAlloc1stIndex;
9227 }
9228
9229 // Found non-null allocation.
9230 if(nextAlloc1stIndex < suballoc1stCount)
9231 {
9232 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9233
9234 // 1. Process free space before this allocation.
9235 if(lastOffset < suballoc.offset)
9236 {
9237 // There is free space from lastOffset to suballoc.offset.
9238 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9239 inoutStats.unusedSize += unusedRangeSize;
9240 ++inoutStats.unusedRangeCount;
9241 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9242 }
9243
9244 // 2. Process this allocation.
9245 // There is allocation with suballoc.offset, suballoc.size.
9246 ++inoutStats.allocationCount;
9247
9248 // 3. Prepare for next iteration.
9249 lastOffset = suballoc.offset + suballoc.size;
9250 ++nextAlloc1stIndex;
9251 }
9252 // We are at the end.
9253 else
9254 {
9255 if(lastOffset < freeSpace1stTo2ndEnd)
9256 {
9257 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9258 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9259 inoutStats.unusedSize += unusedRangeSize;
9260 ++inoutStats.unusedRangeCount;
9261 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9262 }
9263
9264 // End of loop.
9265 lastOffset = freeSpace1stTo2ndEnd;
9266 }
9267 }
9268
9269 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9270 {
9271 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9272 while(lastOffset < size)
9273 {
9274 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9275 while(nextAlloc2ndIndex != SIZE_MAX &&
9276 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9277 {
9278 --nextAlloc2ndIndex;
9279 }
9280
9281 // Found non-null allocation.
9282 if(nextAlloc2ndIndex != SIZE_MAX)
9283 {
9284 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9285
9286 // 1. Process free space before this allocation.
9287 if(lastOffset < suballoc.offset)
9288 {
9289 // There is free space from lastOffset to suballoc.offset.
9290 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9291 inoutStats.unusedSize += unusedRangeSize;
9292 ++inoutStats.unusedRangeCount;
9293 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9294 }
9295
9296 // 2. Process this allocation.
9297 // There is allocation with suballoc.offset, suballoc.size.
9298 ++inoutStats.allocationCount;
9299
9300 // 3. Prepare for next iteration.
9301 lastOffset = suballoc.offset + suballoc.size;
9302 --nextAlloc2ndIndex;
9303 }
9304 // We are at the end.
9305 else
9306 {
9307 if(lastOffset < size)
9308 {
9309 // There is free space from lastOffset to size.
9310 const VkDeviceSize unusedRangeSize = size - lastOffset;
9311 inoutStats.unusedSize += unusedRangeSize;
9312 ++inoutStats.unusedRangeCount;
9313 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
9314 }
9315
9316 // End of loop.
9317 lastOffset = size;
9318 }
9319 }
9320 }
9321}
9322
9323#if VMA_STATS_STRING_ENABLED
9324void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
9325{
9326 const VkDeviceSize size = GetSize();
9327 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9328 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9329 const size_t suballoc1stCount = suballocations1st.size();
9330 const size_t suballoc2ndCount = suballocations2nd.size();
9331
9332 // FIRST PASS
9333
9334 size_t unusedRangeCount = 0;
9335 VkDeviceSize usedBytes = 0;
9336
9337 VkDeviceSize lastOffset = 0;
9338
9339 size_t alloc2ndCount = 0;
9340 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9341 {
9342 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9343 size_t nextAlloc2ndIndex = 0;
9344 while(lastOffset < freeSpace2ndTo1stEnd)
9345 {
9346 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9347 while(nextAlloc2ndIndex < suballoc2ndCount &&
9348 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9349 {
9350 ++nextAlloc2ndIndex;
9351 }
9352
9353 // Found non-null allocation.
9354 if(nextAlloc2ndIndex < suballoc2ndCount)
9355 {
9356 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9357
9358 // 1. Process free space before this allocation.
9359 if(lastOffset < suballoc.offset)
9360 {
9361 // There is free space from lastOffset to suballoc.offset.
9362 ++unusedRangeCount;
9363 }
9364
9365 // 2. Process this allocation.
9366 // There is allocation with suballoc.offset, suballoc.size.
9367 ++alloc2ndCount;
9368 usedBytes += suballoc.size;
9369
9370 // 3. Prepare for next iteration.
9371 lastOffset = suballoc.offset + suballoc.size;
9372 ++nextAlloc2ndIndex;
9373 }
9374 // We are at the end.
9375 else
9376 {
9377 if(lastOffset < freeSpace2ndTo1stEnd)
9378 {
9379 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9380 ++unusedRangeCount;
9381 }
9382
9383 // End of loop.
9384 lastOffset = freeSpace2ndTo1stEnd;
9385 }
9386 }
9387 }
9388
9389 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
9390 size_t alloc1stCount = 0;
9391 const VkDeviceSize freeSpace1stTo2ndEnd =
9392 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
9393 while(lastOffset < freeSpace1stTo2ndEnd)
9394 {
9395 // Find next non-null allocation or move nextAllocIndex to the end.
9396 while(nextAlloc1stIndex < suballoc1stCount &&
9397 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9398 {
9399 ++nextAlloc1stIndex;
9400 }
9401
9402 // Found non-null allocation.
9403 if(nextAlloc1stIndex < suballoc1stCount)
9404 {
9405 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9406
9407 // 1. Process free space before this allocation.
9408 if(lastOffset < suballoc.offset)
9409 {
9410 // There is free space from lastOffset to suballoc.offset.
9411 ++unusedRangeCount;
9412 }
9413
9414 // 2. Process this allocation.
9415 // There is allocation with suballoc.offset, suballoc.size.
9416 ++alloc1stCount;
9417 usedBytes += suballoc.size;
9418
9419 // 3. Prepare for next iteration.
9420 lastOffset = suballoc.offset + suballoc.size;
9421 ++nextAlloc1stIndex;
9422 }
9423 // We are at the end.
9424 else
9425 {
9426 if(lastOffset < size)
9427 {
9428 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9429 ++unusedRangeCount;
9430 }
9431
9432 // End of loop.
9433 lastOffset = freeSpace1stTo2ndEnd;
9434 }
9435 }
9436
9437 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9438 {
9439 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9440 while(lastOffset < size)
9441 {
9442 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9443 while(nextAlloc2ndIndex != SIZE_MAX &&
9444 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9445 {
9446 --nextAlloc2ndIndex;
9447 }
9448
9449 // Found non-null allocation.
9450 if(nextAlloc2ndIndex != SIZE_MAX)
9451 {
9452 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9453
9454 // 1. Process free space before this allocation.
9455 if(lastOffset < suballoc.offset)
9456 {
9457 // There is free space from lastOffset to suballoc.offset.
9458 ++unusedRangeCount;
9459 }
9460
9461 // 2. Process this allocation.
9462 // There is allocation with suballoc.offset, suballoc.size.
9463 ++alloc2ndCount;
9464 usedBytes += suballoc.size;
9465
9466 // 3. Prepare for next iteration.
9467 lastOffset = suballoc.offset + suballoc.size;
9468 --nextAlloc2ndIndex;
9469 }
9470 // We are at the end.
9471 else
9472 {
9473 if(lastOffset < size)
9474 {
9475 // There is free space from lastOffset to size.
9476 ++unusedRangeCount;
9477 }
9478
9479 // End of loop.
9480 lastOffset = size;
9481 }
9482 }
9483 }
9484
9485 const VkDeviceSize unusedBytes = size - usedBytes;
9486 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
9487
9488 // SECOND PASS
9489 lastOffset = 0;
9490
9491 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9492 {
9493 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
9494 size_t nextAlloc2ndIndex = 0;
9495 while(lastOffset < freeSpace2ndTo1stEnd)
9496 {
9497 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9498 while(nextAlloc2ndIndex < suballoc2ndCount &&
9499 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9500 {
9501 ++nextAlloc2ndIndex;
9502 }
9503
9504 // Found non-null allocation.
9505 if(nextAlloc2ndIndex < suballoc2ndCount)
9506 {
9507 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9508
9509 // 1. Process free space before this allocation.
9510 if(lastOffset < suballoc.offset)
9511 {
9512 // There is free space from lastOffset to suballoc.offset.
9513 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9514 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9515 }
9516
9517 // 2. Process this allocation.
9518 // There is allocation with suballoc.offset, suballoc.size.
9519 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9520
9521 // 3. Prepare for next iteration.
9522 lastOffset = suballoc.offset + suballoc.size;
9523 ++nextAlloc2ndIndex;
9524 }
9525 // We are at the end.
9526 else
9527 {
9528 if(lastOffset < freeSpace2ndTo1stEnd)
9529 {
9530 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
9531 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
9532 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9533 }
9534
9535 // End of loop.
9536 lastOffset = freeSpace2ndTo1stEnd;
9537 }
9538 }
9539 }
9540
9541 nextAlloc1stIndex = m_1stNullItemsBeginCount;
9542 while(lastOffset < freeSpace1stTo2ndEnd)
9543 {
9544 // Find next non-null allocation or move nextAllocIndex to the end.
9545 while(nextAlloc1stIndex < suballoc1stCount &&
9546 suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
9547 {
9548 ++nextAlloc1stIndex;
9549 }
9550
9551 // Found non-null allocation.
9552 if(nextAlloc1stIndex < suballoc1stCount)
9553 {
9554 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
9555
9556 // 1. Process free space before this allocation.
9557 if(lastOffset < suballoc.offset)
9558 {
9559 // There is free space from lastOffset to suballoc.offset.
9560 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9561 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9562 }
9563
9564 // 2. Process this allocation.
9565 // There is allocation with suballoc.offset, suballoc.size.
9566 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9567
9568 // 3. Prepare for next iteration.
9569 lastOffset = suballoc.offset + suballoc.size;
9570 ++nextAlloc1stIndex;
9571 }
9572 // We are at the end.
9573 else
9574 {
9575 if(lastOffset < freeSpace1stTo2ndEnd)
9576 {
9577 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
9578 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
9579 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9580 }
9581
9582 // End of loop.
9583 lastOffset = freeSpace1stTo2ndEnd;
9584 }
9585 }
9586
9587 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9588 {
9589 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
9590 while(lastOffset < size)
9591 {
9592 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
9593 while(nextAlloc2ndIndex != SIZE_MAX &&
9594 suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
9595 {
9596 --nextAlloc2ndIndex;
9597 }
9598
9599 // Found non-null allocation.
9600 if(nextAlloc2ndIndex != SIZE_MAX)
9601 {
9602 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
9603
9604 // 1. Process free space before this allocation.
9605 if(lastOffset < suballoc.offset)
9606 {
9607 // There is free space from lastOffset to suballoc.offset.
9608 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
9609 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9610 }
9611
9612 // 2. Process this allocation.
9613 // There is allocation with suballoc.offset, suballoc.size.
9614 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
9615
9616 // 3. Prepare for next iteration.
9617 lastOffset = suballoc.offset + suballoc.size;
9618 --nextAlloc2ndIndex;
9619 }
9620 // We are at the end.
9621 else
9622 {
9623 if(lastOffset < size)
9624 {
9625 // There is free space from lastOffset to size.
9626 const VkDeviceSize unusedRangeSize = size - lastOffset;
9627 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
9628 }
9629
9630 // End of loop.
9631 lastOffset = size;
9632 }
9633 }
9634 }
9635
9636 PrintDetailedMap_End(json);
9637}
9638#endif // #if VMA_STATS_STRING_ENABLED
9639
9640bool VmaBlockMetadata_Linear::CreateAllocationRequest(
9641 uint32_t currentFrameIndex,
9642 uint32_t frameInUseCount,
9643 VkDeviceSize bufferImageGranularity,
9644 VkDeviceSize allocSize,
9645 VkDeviceSize allocAlignment,
9646 bool upperAddress,
9647 VmaSuballocationType allocType,
9648 bool canMakeOtherLost,
9649 uint32_t strategy,
9650 VmaAllocationRequest* pAllocationRequest)
9651{
9652 VMA_ASSERT(allocSize > 0);
9653 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
9654 VMA_ASSERT(pAllocationRequest != VMA_NULL);
9655 VMA_HEAVY_ASSERT(Validate());
9656
9657 const VkDeviceSize size = GetSize();
9658 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9659 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9660
9661 if(upperAddress)
9662 {
9663 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9664 {
9665 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9666 return false;
9667 }
9668
9669 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9670 if(allocSize > size)
9671 {
9672 return false;
9673 }
9674 VkDeviceSize resultBaseOffset = size - allocSize;
9675 if(!suballocations2nd.empty())
9676 {
9677 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9678 resultBaseOffset = lastSuballoc.offset - allocSize;
9679 if(allocSize > lastSuballoc.offset)
9680 {
9681 return false;
9682 }
9683 }
9684
9685 // Start from offset equal to end of free space.
9686 VkDeviceSize resultOffset = resultBaseOffset;
9687
9688 // Apply VMA_DEBUG_MARGIN at the end.
9689 if(VMA_DEBUG_MARGIN > 0)
9690 {
9691 if(resultOffset < VMA_DEBUG_MARGIN)
9692 {
9693 return false;
9694 }
9695 resultOffset -= VMA_DEBUG_MARGIN;
9696 }
9697
9698 // Apply alignment.
9699 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9700
9701 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9702 // Make bigger alignment if necessary.
9703 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9704 {
9705 bool bufferImageGranularityConflict = false;
9706 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9707 {
9708 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9709 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9710 {
9711 if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9712 {
9713 bufferImageGranularityConflict = true;
9714 break;
9715 }
9716 }
9717 else
9718 // Already on previous page.
9719 break;
9720 }
9721 if(bufferImageGranularityConflict)
9722 {
9723 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9724 }
9725 }
9726
9727 // There is enough free space.
9728 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9729 suballocations1st.back().offset + suballocations1st.back().size :
9730 0;
9731 if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
9732 {
9733 // Check previous suballocations for BufferImageGranularity conflicts.
9734 // If conflict exists, allocation cannot be made here.
9735 if(bufferImageGranularity > 1)
9736 {
9737 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9738 {
9739 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9740 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9741 {
9742 if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9743 {
9744 return false;
9745 }
9746 }
9747 else
9748 {
9749 // Already on next page.
9750 break;
9751 }
9752 }
9753 }
9754
9755 // All tests passed: Success.
9756 pAllocationRequest->offset = resultOffset;
9757 pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
9758 pAllocationRequest->sumItemSize = 0;
9759 // pAllocationRequest->item unused.
9760 pAllocationRequest->itemsToMakeLostCount = 0;
9761 return true;
9762 }
9763 }
9764 else // !upperAddress
9765 {
9766 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9767 {
9768 // Try to allocate at the end of 1st vector.
9769
9770 VkDeviceSize resultBaseOffset = 0;
9771 if(!suballocations1st.empty())
9772 {
9773 const VmaSuballocation& lastSuballoc = suballocations1st.back();
9774 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9775 }
9776
9777 // Start from offset equal to beginning of free space.
9778 VkDeviceSize resultOffset = resultBaseOffset;
9779
9780 // Apply VMA_DEBUG_MARGIN at the beginning.
9781 if(VMA_DEBUG_MARGIN > 0)
9782 {
9783 resultOffset += VMA_DEBUG_MARGIN;
9784 }
9785
9786 // Apply alignment.
9787 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9788
9789 // Check previous suballocations for BufferImageGranularity conflicts.
9790 // Make bigger alignment if necessary.
9791 if(bufferImageGranularity > 1 && !suballocations1st.empty())
9792 {
9793 bool bufferImageGranularityConflict = false;
9794 for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9795 {
9796 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9797 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9798 {
9799 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9800 {
9801 bufferImageGranularityConflict = true;
9802 break;
9803 }
9804 }
9805 else
9806 // Already on previous page.
9807 break;
9808 }
9809 if(bufferImageGranularityConflict)
9810 {
9811 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9812 }
9813 }
9814
9815 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
9816 suballocations2nd.back().offset : size;
9817
9818 // There is enough free space at the end after alignment.
9819 if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
9820 {
9821 // Check next suballocations for BufferImageGranularity conflicts.
9822 // If conflict exists, allocation cannot be made here.
9823 if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
9824 {
9825 for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9826 {
9827 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9828 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9829 {
9830 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9831 {
9832 return false;
9833 }
9834 }
9835 else
9836 {
9837 // Already on previous page.
9838 break;
9839 }
9840 }
9841 }
9842
9843 // All tests passed: Success.
9844 pAllocationRequest->offset = resultOffset;
9845 pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
9846 pAllocationRequest->sumItemSize = 0;
9847 // pAllocationRequest->item unused.
9848 pAllocationRequest->itemsToMakeLostCount = 0;
9849 return true;
9850 }
9851 }
9852
9853 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9854 // beginning of 1st vector as the end of free space.
9855 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9856 {
9857 VMA_ASSERT(!suballocations1st.empty());
9858
9859 VkDeviceSize resultBaseOffset = 0;
9860 if(!suballocations2nd.empty())
9861 {
9862 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9863 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
9864 }
9865
9866 // Start from offset equal to beginning of free space.
9867 VkDeviceSize resultOffset = resultBaseOffset;
9868
9869 // Apply VMA_DEBUG_MARGIN at the beginning.
9870 if(VMA_DEBUG_MARGIN > 0)
9871 {
9872 resultOffset += VMA_DEBUG_MARGIN;
9873 }
9874
9875 // Apply alignment.
9876 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9877
9878 // Check previous suballocations for BufferImageGranularity conflicts.
9879 // Make bigger alignment if necessary.
9880 if(bufferImageGranularity > 1 && !suballocations2nd.empty())
9881 {
9882 bool bufferImageGranularityConflict = false;
9883 for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9884 {
9885 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9886 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9887 {
9888 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9889 {
9890 bufferImageGranularityConflict = true;
9891 break;
9892 }
9893 }
9894 else
9895 // Already on previous page.
9896 break;
9897 }
9898 if(bufferImageGranularityConflict)
9899 {
9900 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9901 }
9902 }
9903
9904 pAllocationRequest->itemsToMakeLostCount = 0;
9905 pAllocationRequest->sumItemSize = 0;
9906 size_t index1st = m_1stNullItemsBeginCount;
9907
9908 if(canMakeOtherLost)
9909 {
9910 while(index1st < suballocations1st.size() &&
9911 resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
9912 {
9913 // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
9914 const VmaSuballocation& suballoc = suballocations1st[index1st];
9915 if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
9916 {
9917 // No problem.
9918 }
9919 else
9920 {
9921 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
9922 if(suballoc.hAllocation->CanBecomeLost() &&
9923 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9924 {
9925 ++pAllocationRequest->itemsToMakeLostCount;
9926 pAllocationRequest->sumItemSize += suballoc.size;
9927 }
9928 else
9929 {
9930 return false;
9931 }
9932 }
9933 ++index1st;
9934 }
9935
9936 // Check next suballocations for BufferImageGranularity conflicts.
9937 // If conflict exists, we must mark more allocations lost or fail.
9938 if(bufferImageGranularity > 1)
9939 {
9940 while(index1st < suballocations1st.size())
9941 {
9942 const VmaSuballocation& suballoc = suballocations1st[index1st];
9943 if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
9944 {
9945 if(suballoc.hAllocation != VK_NULL_HANDLE)
9946 {
9947 // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
9948 if(suballoc.hAllocation->CanBecomeLost() &&
9949 suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
9950 {
9951 ++pAllocationRequest->itemsToMakeLostCount;
9952 pAllocationRequest->sumItemSize += suballoc.size;
9953 }
9954 else
9955 {
9956 return false;
9957 }
9958 }
9959 }
9960 else
9961 {
9962 // Already on next page.
9963 break;
9964 }
9965 ++index1st;
9966 }
9967 }
9968 }
9969
9970 // There is enough free space at the end after alignment.
9971 if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
9972 (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
9973 {
9974 // Check next suballocations for BufferImageGranularity conflicts.
9975 // If conflict exists, allocation cannot be made here.
9976 if(bufferImageGranularity > 1)
9977 {
9978 for(size_t nextSuballocIndex = index1st;
9979 nextSuballocIndex < suballocations1st.size();
9980 nextSuballocIndex++)
9981 {
9982 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9983 if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9984 {
9985 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9986 {
9987 return false;
9988 }
9989 }
9990 else
9991 {
9992 // Already on next page.
9993 break;
9994 }
9995 }
9996 }
9997
9998 // All tests passed: Success.
9999 pAllocationRequest->offset = resultOffset;
10000 pAllocationRequest->sumFreeSize =
10001 (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
10002 - resultBaseOffset
10003 - pAllocationRequest->sumItemSize;
10004 // pAllocationRequest->item unused.
10005 return true;
10006 }
10007 }
10008 }
10009
10010 return false;
10011}
10012
10013bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
10014 uint32_t currentFrameIndex,
10015 uint32_t frameInUseCount,
10016 VmaAllocationRequest* pAllocationRequest)
10017{
10018 if(pAllocationRequest->itemsToMakeLostCount == 0)
10019 {
10020 return true;
10021 }
10022
10023 VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
10024
10025 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10026 size_t index1st = m_1stNullItemsBeginCount;
10027 size_t madeLostCount = 0;
10028 while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
10029 {
10030 VMA_ASSERT(index1st < suballocations1st.size());
10031 VmaSuballocation& suballoc = suballocations1st[index1st];
10032 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10033 {
10034 VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
10035 VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
10036 if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10037 {
10038 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10039 suballoc.hAllocation = VK_NULL_HANDLE;
10040 m_SumFreeSize += suballoc.size;
10041 ++m_1stNullItemsMiddleCount;
10042 ++madeLostCount;
10043 }
10044 else
10045 {
10046 return false;
10047 }
10048 }
10049 ++index1st;
10050 }
10051
10052 CleanupAfterFree();
10053 //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
10054
10055 return true;
10056}
10057
10058uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10059{
10060 uint32_t lostAllocationCount = 0;
10061
10062 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10063 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10064 {
10065 VmaSuballocation& suballoc = suballocations1st[i];
10066 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10067 suballoc.hAllocation->CanBecomeLost() &&
10068 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10069 {
10070 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10071 suballoc.hAllocation = VK_NULL_HANDLE;
10072 ++m_1stNullItemsMiddleCount;
10073 m_SumFreeSize += suballoc.size;
10074 ++lostAllocationCount;
10075 }
10076 }
10077
10078 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10079 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10080 {
10081 VmaSuballocation& suballoc = suballocations2nd[i];
10082 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
10083 suballoc.hAllocation->CanBecomeLost() &&
10084 suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
10085 {
10086 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10087 suballoc.hAllocation = VK_NULL_HANDLE;
10088 ++m_2ndNullItemsCount;
10089 ++lostAllocationCount;
10090 }
10091 }
10092
10093 if(lostAllocationCount)
10094 {
10095 CleanupAfterFree();
10096 }
10097
10098 return lostAllocationCount;
10099}
10100
10101VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
10102{
10103 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10104 for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
10105 {
10106 const VmaSuballocation& suballoc = suballocations1st[i];
10107 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10108 {
10109 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10110 {
10111 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10112 return VK_ERROR_VALIDATION_FAILED_EXT;
10113 }
10114 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10115 {
10116 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10117 return VK_ERROR_VALIDATION_FAILED_EXT;
10118 }
10119 }
10120 }
10121
10122 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10123 for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
10124 {
10125 const VmaSuballocation& suballoc = suballocations2nd[i];
10126 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
10127 {
10128 if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
10129 {
10130 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
10131 return VK_ERROR_VALIDATION_FAILED_EXT;
10132 }
10133 if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
10134 {
10135 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10136 return VK_ERROR_VALIDATION_FAILED_EXT;
10137 }
10138 }
10139 }
10140
10141 return VK_SUCCESS;
10142}
10143
10144void VmaBlockMetadata_Linear::Alloc(
10145 const VmaAllocationRequest& request,
10146 VmaSuballocationType type,
10147 VkDeviceSize allocSize,
10148 bool upperAddress,
10149 VmaAllocation hAllocation)
10150{
10151 const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
10152
10153 if(upperAddress)
10154 {
10155 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
10156 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
10157 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10158 suballocations2nd.push_back(newSuballoc);
10159 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
10160 }
10161 else
10162 {
10163 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10164
10165 // First allocation.
10166 if(suballocations1st.empty())
10167 {
10168 suballocations1st.push_back(newSuballoc);
10169 }
10170 else
10171 {
10172 // New allocation at the end of 1st vector.
10173 if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
10174 {
10175 // Check if it fits before the end of the block.
10176 VMA_ASSERT(request.offset + allocSize <= GetSize());
10177 suballocations1st.push_back(newSuballoc);
10178 }
10179 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
10180 else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
10181 {
10182 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10183
10184 switch(m_2ndVectorMode)
10185 {
10186 case SECOND_VECTOR_EMPTY:
10187 // First allocation from second part ring buffer.
10188 VMA_ASSERT(suballocations2nd.empty());
10189 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
10190 break;
10191 case SECOND_VECTOR_RING_BUFFER:
10192 // 2-part ring buffer is already started.
10193 VMA_ASSERT(!suballocations2nd.empty());
10194 break;
10195 case SECOND_VECTOR_DOUBLE_STACK:
10196 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
10197 break;
10198 default:
10199 VMA_ASSERT(0);
10200 }
10201
10202 suballocations2nd.push_back(newSuballoc);
10203 }
10204 else
10205 {
10206 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
10207 }
10208 }
10209 }
10210
10211 m_SumFreeSize -= newSuballoc.size;
10212}
10213
10214void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
10215{
10216 FreeAtOffset(allocation->GetOffset());
10217}
10218
10219void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
10220{
10221 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10222 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10223
10224 if(!suballocations1st.empty())
10225 {
10226 // First allocation: Mark it as next empty at the beginning.
10227 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
10228 if(firstSuballoc.offset == offset)
10229 {
10230 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
10231 firstSuballoc.hAllocation = VK_NULL_HANDLE;
10232 m_SumFreeSize += firstSuballoc.size;
10233 ++m_1stNullItemsBeginCount;
10234 CleanupAfterFree();
10235 return;
10236 }
10237 }
10238
10239 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
10240 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
10241 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
10242 {
10243 VmaSuballocation& lastSuballoc = suballocations2nd.back();
10244 if(lastSuballoc.offset == offset)
10245 {
10246 m_SumFreeSize += lastSuballoc.size;
10247 suballocations2nd.pop_back();
10248 CleanupAfterFree();
10249 return;
10250 }
10251 }
10252 // Last allocation in 1st vector.
10253 else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
10254 {
10255 VmaSuballocation& lastSuballoc = suballocations1st.back();
10256 if(lastSuballoc.offset == offset)
10257 {
10258 m_SumFreeSize += lastSuballoc.size;
10259 suballocations1st.pop_back();
10260 CleanupAfterFree();
10261 return;
10262 }
10263 }
10264
10265 // Item from the middle of 1st vector.
10266 {
10267 VmaSuballocation refSuballoc;
10268 refSuballoc.offset = offset;
10269 // Rest of members stays uninitialized intentionally for better performance.
10270 SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
10271 suballocations1st.begin() + m_1stNullItemsBeginCount,
10272 suballocations1st.end(),
10273 refSuballoc);
10274 if(it != suballocations1st.end())
10275 {
10276 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10277 it->hAllocation = VK_NULL_HANDLE;
10278 ++m_1stNullItemsMiddleCount;
10279 m_SumFreeSize += it->size;
10280 CleanupAfterFree();
10281 return;
10282 }
10283 }
10284
10285 if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
10286 {
10287 // Item from the middle of 2nd vector.
10288 VmaSuballocation refSuballoc;
10289 refSuballoc.offset = offset;
10290 // Rest of members stays uninitialized intentionally for better performance.
10291 SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
10292 VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
10293 VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
10294 if(it != suballocations2nd.end())
10295 {
10296 it->type = VMA_SUBALLOCATION_TYPE_FREE;
10297 it->hAllocation = VK_NULL_HANDLE;
10298 ++m_2ndNullItemsCount;
10299 m_SumFreeSize += it->size;
10300 CleanupAfterFree();
10301 return;
10302 }
10303 }
10304
10305 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
10306}
10307
10308bool VmaBlockMetadata_Linear::ShouldCompact1st() const
10309{
10310 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10311 const size_t suballocCount = AccessSuballocations1st().size();
10312 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
10313}
10314
10315void VmaBlockMetadata_Linear::CleanupAfterFree()
10316{
10317 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
10318 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
10319
10320 if(IsEmpty())
10321 {
10322 suballocations1st.clear();
10323 suballocations2nd.clear();
10324 m_1stNullItemsBeginCount = 0;
10325 m_1stNullItemsMiddleCount = 0;
10326 m_2ndNullItemsCount = 0;
10327 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10328 }
10329 else
10330 {
10331 const size_t suballoc1stCount = suballocations1st.size();
10332 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
10333 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
10334
10335 // Find more null items at the beginning of 1st vector.
10336 while(m_1stNullItemsBeginCount < suballoc1stCount &&
10337 suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10338 {
10339 ++m_1stNullItemsBeginCount;
10340 --m_1stNullItemsMiddleCount;
10341 }
10342
10343 // Find more null items at the end of 1st vector.
10344 while(m_1stNullItemsMiddleCount > 0 &&
10345 suballocations1st.back().hAllocation == VK_NULL_HANDLE)
10346 {
10347 --m_1stNullItemsMiddleCount;
10348 suballocations1st.pop_back();
10349 }
10350
10351 // Find more null items at the end of 2nd vector.
10352 while(m_2ndNullItemsCount > 0 &&
10353 suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
10354 {
10355 --m_2ndNullItemsCount;
10356 suballocations2nd.pop_back();
10357 }
10358
10359 if(ShouldCompact1st())
10360 {
10361 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
10362 size_t srcIndex = m_1stNullItemsBeginCount;
10363 for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
10364 {
10365 while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
10366 {
10367 ++srcIndex;
10368 }
10369 if(dstIndex != srcIndex)
10370 {
10371 suballocations1st[dstIndex] = suballocations1st[srcIndex];
10372 }
10373 ++srcIndex;
10374 }
10375 suballocations1st.resize(nonNullItemCount);
10376 m_1stNullItemsBeginCount = 0;
10377 m_1stNullItemsMiddleCount = 0;
10378 }
10379
10380 // 2nd vector became empty.
10381 if(suballocations2nd.empty())
10382 {
10383 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10384 }
10385
10386 // 1st vector became empty.
10387 if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
10388 {
10389 suballocations1st.clear();
10390 m_1stNullItemsBeginCount = 0;
10391
10392 if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
10393 {
10394 // Swap 1st with 2nd. Now 2nd is empty.
10395 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
10396 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
10397 while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
10398 suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
10399 {
10400 ++m_1stNullItemsBeginCount;
10401 --m_1stNullItemsMiddleCount;
10402 }
10403 m_2ndNullItemsCount = 0;
10404 m_1stVectorIndex ^= 1;
10405 }
10406 }
10407 }
10408
10409 VMA_HEAVY_ASSERT(Validate());
10410}
10411
10412
10413////////////////////////////////////////////////////////////////////////////////
10414// class VmaBlockMetadata_Buddy
10415
10416VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
10417 VmaBlockMetadata(hAllocator),
10418 m_Root(VMA_NULL),
10419 m_AllocationCount(0),
10420 m_FreeCount(1),
10421 m_SumFreeSize(0)
10422{
10423 memset(m_FreeList, 0, sizeof(m_FreeList));
10424}
10425
10426VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
10427{
10428 DeleteNode(m_Root);
10429}
10430
10431void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
10432{
10433 VmaBlockMetadata::Init(size);
10434
10435 m_UsableSize = VmaPrevPow2(size);
10436 m_SumFreeSize = m_UsableSize;
10437
10438 // Calculate m_LevelCount.
10439 m_LevelCount = 1;
10440 while(m_LevelCount < MAX_LEVELS &&
10441 LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
10442 {
10443 ++m_LevelCount;
10444 }
10445
10446 Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
10447 rootNode->offset = 0;
10448 rootNode->type = Node::TYPE_FREE;
10449 rootNode->parent = VMA_NULL;
10450 rootNode->buddy = VMA_NULL;
10451
10452 m_Root = rootNode;
10453 AddToFreeListFront(0, rootNode);
10454}
10455
10456bool VmaBlockMetadata_Buddy::Validate() const
10457{
10458 // Validate tree.
10459 ValidationContext ctx;
10460 if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
10461 {
10462 VMA_VALIDATE(false && "ValidateNode failed.");
10463 }
10464 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
10465 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
10466
10467 // Validate free node lists.
10468 for(uint32_t level = 0; level < m_LevelCount; ++level)
10469 {
10470 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
10471 m_FreeList[level].front->free.prev == VMA_NULL);
10472
10473 for(Node* node = m_FreeList[level].front;
10474 node != VMA_NULL;
10475 node = node->free.next)
10476 {
10477 VMA_VALIDATE(node->type == Node::TYPE_FREE);
10478
10479 if(node->free.next == VMA_NULL)
10480 {
10481 VMA_VALIDATE(m_FreeList[level].back == node);
10482 }
10483 else
10484 {
10485 VMA_VALIDATE(node->free.next->free.prev == node);
10486 }
10487 }
10488 }
10489
10490 // Validate that free lists ar higher levels are empty.
10491 for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
10492 {
10493 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
10494 }
10495
10496 return true;
10497}
10498
10499VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
10500{
10501 for(uint32_t level = 0; level < m_LevelCount; ++level)
10502 {
10503 if(m_FreeList[level].front != VMA_NULL)
10504 {
10505 return LevelToNodeSize(level);
10506 }
10507 }
10508 return 0;
10509}
10510
10511void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
10512{
10513 const VkDeviceSize unusableSize = GetUnusableSize();
10514
10515 outInfo.blockCount = 1;
10516
10517 outInfo.allocationCount = outInfo.unusedRangeCount = 0;
10518 outInfo.usedBytes = outInfo.unusedBytes = 0;
10519
10520 outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
10521 outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
10522 outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
10523
10524 CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
10525
10526 if(unusableSize > 0)
10527 {
10528 ++outInfo.unusedRangeCount;
10529 outInfo.unusedBytes += unusableSize;
10530 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
10531 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
10532 }
10533}
10534
10535void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
10536{
10537 const VkDeviceSize unusableSize = GetUnusableSize();
10538
10539 inoutStats.size += GetSize();
10540 inoutStats.unusedSize += m_SumFreeSize + unusableSize;
10541 inoutStats.allocationCount += m_AllocationCount;
10542 inoutStats.unusedRangeCount += m_FreeCount;
10543 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
10544
10545 if(unusableSize > 0)
10546 {
10547 ++inoutStats.unusedRangeCount;
10548 // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
10549 }
10550}
10551
10552#if VMA_STATS_STRING_ENABLED
10553
10554void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
10555{
10556 // TODO optimize
10557 VmaStatInfo stat;
10558 CalcAllocationStatInfo(stat);
10559
10560 PrintDetailedMap_Begin(
10561 json,
10562 stat.unusedBytes,
10563 stat.allocationCount,
10564 stat.unusedRangeCount);
10565
10566 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
10567
10568 const VkDeviceSize unusableSize = GetUnusableSize();
10569 if(unusableSize > 0)
10570 {
10571 PrintDetailedMap_UnusedRange(json,
10572 m_UsableSize, // offset
10573 unusableSize); // size
10574 }
10575
10576 PrintDetailedMap_End(json);
10577}
10578
10579#endif // #if VMA_STATS_STRING_ENABLED
10580
10581bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
10582 uint32_t currentFrameIndex,
10583 uint32_t frameInUseCount,
10584 VkDeviceSize bufferImageGranularity,
10585 VkDeviceSize allocSize,
10586 VkDeviceSize allocAlignment,
10587 bool upperAddress,
10588 VmaSuballocationType allocType,
10589 bool canMakeOtherLost,
10590 uint32_t strategy,
10591 VmaAllocationRequest* pAllocationRequest)
10592{
10593 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10594
10595 // Simple way to respect bufferImageGranularity. May be optimized some day.
10596 // Whenever it might be an OPTIMAL image...
10597 if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
10598 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
10599 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
10600 {
10601 allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
10602 allocSize = VMA_MAX(allocSize, bufferImageGranularity);
10603 }
10604
10605 if(allocSize > m_UsableSize)
10606 {
10607 return false;
10608 }
10609
10610 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10611 for(uint32_t level = targetLevel + 1; level--; )
10612 {
10613 for(Node* freeNode = m_FreeList[level].front;
10614 freeNode != VMA_NULL;
10615 freeNode = freeNode->free.next)
10616 {
10617 if(freeNode->offset % allocAlignment == 0)
10618 {
10619 pAllocationRequest->offset = freeNode->offset;
10620 pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
10621 pAllocationRequest->sumItemSize = 0;
10622 pAllocationRequest->itemsToMakeLostCount = 0;
10623 pAllocationRequest->customData = (void*)(uintptr_t)level;
10624 return true;
10625 }
10626 }
10627 }
10628
10629 return false;
10630}
10631
10632bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
10633 uint32_t currentFrameIndex,
10634 uint32_t frameInUseCount,
10635 VmaAllocationRequest* pAllocationRequest)
10636{
10637 /*
10638 Lost allocations are not supported in buddy allocator at the moment.
10639 Support might be added in the future.
10640 */
10641 return pAllocationRequest->itemsToMakeLostCount == 0;
10642}
10643
10644uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
10645{
10646 /*
10647 Lost allocations are not supported in buddy allocator at the moment.
10648 Support might be added in the future.
10649 */
10650 return 0;
10651}
10652
10653void VmaBlockMetadata_Buddy::Alloc(
10654 const VmaAllocationRequest& request,
10655 VmaSuballocationType type,
10656 VkDeviceSize allocSize,
10657 bool upperAddress,
10658 VmaAllocation hAllocation)
10659{
10660 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
10661 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
10662
10663 Node* currNode = m_FreeList[currLevel].front;
10664 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10665 while(currNode->offset != request.offset)
10666 {
10667 currNode = currNode->free.next;
10668 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
10669 }
10670
10671 // Go down, splitting free nodes.
10672 while(currLevel < targetLevel)
10673 {
10674 // currNode is already first free node at currLevel.
10675 // Remove it from list of free nodes at this currLevel.
10676 RemoveFromFreeList(currLevel, currNode);
10677
10678 const uint32_t childrenLevel = currLevel + 1;
10679
10680 // Create two free sub-nodes.
10681 Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
10682 Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
10683
10684 leftChild->offset = currNode->offset;
10685 leftChild->type = Node::TYPE_FREE;
10686 leftChild->parent = currNode;
10687 leftChild->buddy = rightChild;
10688
10689 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
10690 rightChild->type = Node::TYPE_FREE;
10691 rightChild->parent = currNode;
10692 rightChild->buddy = leftChild;
10693
10694 // Convert current currNode to split type.
10695 currNode->type = Node::TYPE_SPLIT;
10696 currNode->split.leftChild = leftChild;
10697
10698 // Add child nodes to free list. Order is important!
10699 AddToFreeListFront(childrenLevel, rightChild);
10700 AddToFreeListFront(childrenLevel, leftChild);
10701
10702 ++m_FreeCount;
10703 //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
10704 ++currLevel;
10705 currNode = m_FreeList[currLevel].front;
10706
10707 /*
10708 We can be sure that currNode, as left child of node previously split,
10709 also fullfills the alignment requirement.
10710 */
10711 }
10712
10713 // Remove from free list.
10714 VMA_ASSERT(currLevel == targetLevel &&
10715 currNode != VMA_NULL &&
10716 currNode->type == Node::TYPE_FREE);
10717 RemoveFromFreeList(currLevel, currNode);
10718
10719 // Convert to allocation node.
10720 currNode->type = Node::TYPE_ALLOCATION;
10721 currNode->allocation.alloc = hAllocation;
10722
10723 ++m_AllocationCount;
10724 --m_FreeCount;
10725 m_SumFreeSize -= allocSize;
10726}
10727
10728void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
10729{
10730 if(node->type == Node::TYPE_SPLIT)
10731 {
10732 DeleteNode(node->split.leftChild->buddy);
10733 DeleteNode(node->split.leftChild);
10734 }
10735
10736 vma_delete(GetAllocationCallbacks(), node);
10737}
10738
10739bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
10740{
10741 VMA_VALIDATE(level < m_LevelCount);
10742 VMA_VALIDATE(curr->parent == parent);
10743 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
10744 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
10745 switch(curr->type)
10746 {
10747 case Node::TYPE_FREE:
10748 // curr->free.prev, next are validated separately.
10749 ctx.calculatedSumFreeSize += levelNodeSize;
10750 ++ctx.calculatedFreeCount;
10751 break;
10752 case Node::TYPE_ALLOCATION:
10753 ++ctx.calculatedAllocationCount;
10754 ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
10755 VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
10756 break;
10757 case Node::TYPE_SPLIT:
10758 {
10759 const uint32_t childrenLevel = level + 1;
10760 const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
10761 const Node* const leftChild = curr->split.leftChild;
10762 VMA_VALIDATE(leftChild != VMA_NULL);
10763 VMA_VALIDATE(leftChild->offset == curr->offset);
10764 if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
10765 {
10766 VMA_VALIDATE(false && "ValidateNode for left child failed.");
10767 }
10768 const Node* const rightChild = leftChild->buddy;
10769 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
10770 if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
10771 {
10772 VMA_VALIDATE(false && "ValidateNode for right child failed.");
10773 }
10774 }
10775 break;
10776 default:
10777 return false;
10778 }
10779
10780 return true;
10781}
10782
10783uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
10784{
10785 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
10786 uint32_t level = 0;
10787 VkDeviceSize currLevelNodeSize = m_UsableSize;
10788 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
10789 while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
10790 {
10791 ++level;
10792 currLevelNodeSize = nextLevelNodeSize;
10793 nextLevelNodeSize = currLevelNodeSize >> 1;
10794 }
10795 return level;
10796}
10797
10798void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
10799{
10800 // Find node and level.
10801 Node* node = m_Root;
10802 VkDeviceSize nodeOffset = 0;
10803 uint32_t level = 0;
10804 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
10805 while(node->type == Node::TYPE_SPLIT)
10806 {
10807 const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
10808 if(offset < nodeOffset + nextLevelSize)
10809 {
10810 node = node->split.leftChild;
10811 }
10812 else
10813 {
10814 node = node->split.leftChild->buddy;
10815 nodeOffset += nextLevelSize;
10816 }
10817 ++level;
10818 levelNodeSize = nextLevelSize;
10819 }
10820
10821 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
10822 VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
10823
10824 ++m_FreeCount;
10825 --m_AllocationCount;
10826 m_SumFreeSize += alloc->GetSize();
10827
10828 node->type = Node::TYPE_FREE;
10829
10830 // Join free nodes if possible.
10831 while(level > 0 && node->buddy->type == Node::TYPE_FREE)
10832 {
10833 RemoveFromFreeList(level, node->buddy);
10834 Node* const parent = node->parent;
10835
10836 vma_delete(GetAllocationCallbacks(), node->buddy);
10837 vma_delete(GetAllocationCallbacks(), node);
10838 parent->type = Node::TYPE_FREE;
10839
10840 node = parent;
10841 --level;
10842 //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
10843 --m_FreeCount;
10844 }
10845
10846 AddToFreeListFront(level, node);
10847}
10848
10849void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
10850{
10851 switch(node->type)
10852 {
10853 case Node::TYPE_FREE:
10854 ++outInfo.unusedRangeCount;
10855 outInfo.unusedBytes += levelNodeSize;
10856 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
10857 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
10858 break;
10859 case Node::TYPE_ALLOCATION:
10860 {
10861 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10862 ++outInfo.allocationCount;
10863 outInfo.usedBytes += allocSize;
10864 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
10865 outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
10866
10867 const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
10868 if(unusedRangeSize > 0)
10869 {
10870 ++outInfo.unusedRangeCount;
10871 outInfo.unusedBytes += unusedRangeSize;
10872 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
10873 outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
10874 }
10875 }
10876 break;
10877 case Node::TYPE_SPLIT:
10878 {
10879 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10880 const Node* const leftChild = node->split.leftChild;
10881 CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
10882 const Node* const rightChild = leftChild->buddy;
10883 CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
10884 }
10885 break;
10886 default:
10887 VMA_ASSERT(0);
10888 }
10889}
10890
10891void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
10892{
10893 VMA_ASSERT(node->type == Node::TYPE_FREE);
10894
10895 // List is empty.
10896 Node* const frontNode = m_FreeList[level].front;
10897 if(frontNode == VMA_NULL)
10898 {
10899 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
10900 node->free.prev = node->free.next = VMA_NULL;
10901 m_FreeList[level].front = m_FreeList[level].back = node;
10902 }
10903 else
10904 {
10905 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
10906 node->free.prev = VMA_NULL;
10907 node->free.next = frontNode;
10908 frontNode->free.prev = node;
10909 m_FreeList[level].front = node;
10910 }
10911}
10912
10913void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
10914{
10915 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
10916
10917 // It is at the front.
10918 if(node->free.prev == VMA_NULL)
10919 {
10920 VMA_ASSERT(m_FreeList[level].front == node);
10921 m_FreeList[level].front = node->free.next;
10922 }
10923 else
10924 {
10925 Node* const prevFreeNode = node->free.prev;
10926 VMA_ASSERT(prevFreeNode->free.next == node);
10927 prevFreeNode->free.next = node->free.next;
10928 }
10929
10930 // It is at the back.
10931 if(node->free.next == VMA_NULL)
10932 {
10933 VMA_ASSERT(m_FreeList[level].back == node);
10934 m_FreeList[level].back = node->free.prev;
10935 }
10936 else
10937 {
10938 Node* const nextFreeNode = node->free.next;
10939 VMA_ASSERT(nextFreeNode->free.prev == node);
10940 nextFreeNode->free.prev = node->free.prev;
10941 }
10942}
10943
10944#if VMA_STATS_STRING_ENABLED
10945void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
10946{
10947 switch(node->type)
10948 {
10949 case Node::TYPE_FREE:
10950 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
10951 break;
10952 case Node::TYPE_ALLOCATION:
10953 {
10954 PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
10955 const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
10956 if(allocSize < levelNodeSize)
10957 {
10958 PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
10959 }
10960 }
10961 break;
10962 case Node::TYPE_SPLIT:
10963 {
10964 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
10965 const Node* const leftChild = node->split.leftChild;
10966 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
10967 const Node* const rightChild = leftChild->buddy;
10968 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
10969 }
10970 break;
10971 default:
10972 VMA_ASSERT(0);
10973 }
10974}
10975#endif // #if VMA_STATS_STRING_ENABLED
10976
10977
10978////////////////////////////////////////////////////////////////////////////////
10979// class VmaDeviceMemoryBlock
10980
10981VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
10982 m_pMetadata(VMA_NULL),
10983 m_MemoryTypeIndex(UINT32_MAX),
10984 m_Id(0),
10985 m_hMemory(VK_NULL_HANDLE),
10986 m_MapCount(0),
10987 m_pMappedData(VMA_NULL)
10988{
10989}
10990
10991void VmaDeviceMemoryBlock::Init(
10992 VmaAllocator hAllocator,
10993 uint32_t newMemoryTypeIndex,
10994 VkDeviceMemory newMemory,
10995 VkDeviceSize newSize,
10996 uint32_t id,
10997 uint32_t algorithm)
10998{
10999 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11000
11001 m_MemoryTypeIndex = newMemoryTypeIndex;
11002 m_Id = id;
11003 m_hMemory = newMemory;
11004
11005 switch(algorithm)
11006 {
11007 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11008 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
11009 break;
11010 case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
11011 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
11012 break;
11013 default:
11014 VMA_ASSERT(0);
11015 // Fall-through.
11016 case 0:
11017 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
11018 }
11019 m_pMetadata->Init(newSize);
11020}
11021
11022void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11023{
11024 // This is the most important assert in the entire library.
11025 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11026 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11027
11028 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11029 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11030 m_hMemory = VK_NULL_HANDLE;
11031
11032 vma_delete(allocator, m_pMetadata);
11033 m_pMetadata = VMA_NULL;
11034}
11035
11036bool VmaDeviceMemoryBlock::Validate() const
11037{
11038 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11039 (m_pMetadata->GetSize() != 0));
11040
11041 return m_pMetadata->Validate();
11042}
11043
11044VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11045{
11046 void* pData = nullptr;
11047 VkResult res = Map(hAllocator, 1, &pData);
11048 if(res != VK_SUCCESS)
11049 {
11050 return res;
11051 }
11052
11053 res = m_pMetadata->CheckCorruption(pData);
11054
11055 Unmap(hAllocator, 1);
11056
11057 return res;
11058}
11059
11060VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11061{
11062 if(count == 0)
11063 {
11064 return VK_SUCCESS;
11065 }
11066
11067 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11068 if(m_MapCount != 0)
11069 {
11070 m_MapCount += count;
11071 VMA_ASSERT(m_pMappedData != VMA_NULL);
11072 if(ppData != VMA_NULL)
11073 {
11074 *ppData = m_pMappedData;
11075 }
11076 return VK_SUCCESS;
11077 }
11078 else
11079 {
11080 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11081 hAllocator->m_hDevice,
11082 m_hMemory,
11083 0, // offset
11084 VK_WHOLE_SIZE,
11085 0, // flags
11086 &m_pMappedData);
11087 if(result == VK_SUCCESS)
11088 {
11089 if(ppData != VMA_NULL)
11090 {
11091 *ppData = m_pMappedData;
11092 }
11093 m_MapCount = count;
11094 }
11095 return result;
11096 }
11097}
11098
11099void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11100{
11101 if(count == 0)
11102 {
11103 return;
11104 }
11105
11106 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11107 if(m_MapCount >= count)
11108 {
11109 m_MapCount -= count;
11110 if(m_MapCount == 0)
11111 {
11112 m_pMappedData = VMA_NULL;
11113 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11114 }
11115 }
11116 else
11117 {
11118 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11119 }
11120}
11121
11122VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11123{
11124 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11125 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11126
11127 void* pData;
11128 VkResult res = Map(hAllocator, 1, &pData);
11129 if(res != VK_SUCCESS)
11130 {
11131 return res;
11132 }
11133
11134 VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
11135 VmaWriteMagicValue(pData, allocOffset + allocSize);
11136
11137 Unmap(hAllocator, 1);
11138
11139 return VK_SUCCESS;
11140}
11141
11142VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11143{
11144 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11145 VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
11146
11147 void* pData;
11148 VkResult res = Map(hAllocator, 1, &pData);
11149 if(res != VK_SUCCESS)
11150 {
11151 return res;
11152 }
11153
11154 if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
11155 {
11156 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
11157 }
11158 else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
11159 {
11160 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11161 }
11162
11163 Unmap(hAllocator, 1);
11164
11165 return VK_SUCCESS;
11166}
11167
11168VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11169 const VmaAllocator hAllocator,
11170 const VmaAllocation hAllocation,
11171 VkBuffer hBuffer)
11172{
11173 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11174 hAllocation->GetBlock() == this);
11175 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11176 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11177 return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
11178 hAllocator->m_hDevice,
11179 hBuffer,
11180 m_hMemory,
11181 hAllocation->GetOffset());
11182}
11183
11184VkResult VmaDeviceMemoryBlock::BindImageMemory(
11185 const VmaAllocator hAllocator,
11186 const VmaAllocation hAllocation,
11187 VkImage hImage)
11188{
11189 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11190 hAllocation->GetBlock() == this);
11191 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11192 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
11193 return hAllocator->GetVulkanFunctions().vkBindImageMemory(
11194 hAllocator->m_hDevice,
11195 hImage,
11196 m_hMemory,
11197 hAllocation->GetOffset());
11198}
11199
11200static void InitStatInfo(VmaStatInfo& outInfo)
11201{
11202 memset(&outInfo, 0, sizeof(outInfo));
11203 outInfo.allocationSizeMin = UINT64_MAX;
11204 outInfo.unusedRangeSizeMin = UINT64_MAX;
11205}
11206
11207// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
11208static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
11209{
11210 inoutInfo.blockCount += srcInfo.blockCount;
11211 inoutInfo.allocationCount += srcInfo.allocationCount;
11212 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
11213 inoutInfo.usedBytes += srcInfo.usedBytes;
11214 inoutInfo.unusedBytes += srcInfo.unusedBytes;
11215 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
11216 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
11217 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
11218 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
11219}
11220
11221static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
11222{
11223 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
11224 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
11225 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
11226 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
11227}
11228
11229VmaPool_T::VmaPool_T(
11230 VmaAllocator hAllocator,
11231 const VmaPoolCreateInfo& createInfo,
11232 VkDeviceSize preferredBlockSize) :
11233 m_BlockVector(
11234 hAllocator,
11235 createInfo.memoryTypeIndex,
11236 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
11237 createInfo.minBlockCount,
11238 createInfo.maxBlockCount,
11239 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
11240 createInfo.frameInUseCount,
11241 true, // isCustomPool
11242 createInfo.blockSize != 0, // explicitBlockSize
11243 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
11244 m_Id(0)
11245{
11246}
11247
11248VmaPool_T::~VmaPool_T()
11249{
11250}
11251
11252#if VMA_STATS_STRING_ENABLED
11253
11254#endif // #if VMA_STATS_STRING_ENABLED
11255
11256VmaBlockVector::VmaBlockVector(
11257 VmaAllocator hAllocator,
11258 uint32_t memoryTypeIndex,
11259 VkDeviceSize preferredBlockSize,
11260 size_t minBlockCount,
11261 size_t maxBlockCount,
11262 VkDeviceSize bufferImageGranularity,
11263 uint32_t frameInUseCount,
11264 bool isCustomPool,
11265 bool explicitBlockSize,
11266 uint32_t algorithm) :
11267 m_hAllocator(hAllocator),
11268 m_MemoryTypeIndex(memoryTypeIndex),
11269 m_PreferredBlockSize(preferredBlockSize),
11270 m_MinBlockCount(minBlockCount),
11271 m_MaxBlockCount(maxBlockCount),
11272 m_BufferImageGranularity(bufferImageGranularity),
11273 m_FrameInUseCount(frameInUseCount),
11274 m_IsCustomPool(isCustomPool),
11275 m_ExplicitBlockSize(explicitBlockSize),
11276 m_Algorithm(algorithm),
11277 m_HasEmptyBlock(false),
11278 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
11279 m_NextBlockId(0)
11280{
11281}
11282
11283VmaBlockVector::~VmaBlockVector()
11284{
11285 for(size_t i = m_Blocks.size(); i--; )
11286 {
11287 m_Blocks[i]->Destroy(m_hAllocator);
11288 vma_delete(m_hAllocator, m_Blocks[i]);
11289 }
11290}
11291
11292VkResult VmaBlockVector::CreateMinBlocks()
11293{
11294 for(size_t i = 0; i < m_MinBlockCount; ++i)
11295 {
11296 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
11297 if(res != VK_SUCCESS)
11298 {
11299 return res;
11300 }
11301 }
11302 return VK_SUCCESS;
11303}
11304
11305void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
11306{
11307 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
11308
11309 const size_t blockCount = m_Blocks.size();
11310
11311 pStats->size = 0;
11312 pStats->unusedSize = 0;
11313 pStats->allocationCount = 0;
11314 pStats->unusedRangeCount = 0;
11315 pStats->unusedRangeSizeMax = 0;
11316 pStats->blockCount = blockCount;
11317
11318 for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
11319 {
11320 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
11321 VMA_ASSERT(pBlock);
11322 VMA_HEAVY_ASSERT(pBlock->Validate());
11323 pBlock->m_pMetadata->AddPoolStats(*pStats);
11324 }
11325}
11326
11327bool VmaBlockVector::IsCorruptionDetectionEnabled() const
11328{
11329 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
11330 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
11331 (VMA_DEBUG_MARGIN > 0) &&
11332 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
11333}
11334
11335static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
11336
11337VkResult VmaBlockVector::Allocate(
11338 VmaPool hCurrentPool,
11339 uint32_t currentFrameIndex,
11340 VkDeviceSize size,
11341 VkDeviceSize alignment,
11342 const VmaAllocationCreateInfo& createInfo,
11343 VmaSuballocationType suballocType,
11344 size_t allocationCount,
11345 VmaAllocation* pAllocations)
11346{
11347 size_t allocIndex;
11348 VkResult res = VK_SUCCESS;
11349
11350 {
11351 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11352 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
11353 {
11354 res = AllocatePage(
11355 hCurrentPool,
11356 currentFrameIndex,
11357 size,
11358 alignment,
11359 createInfo,
11360 suballocType,
11361 pAllocations + allocIndex);
11362 if(res != VK_SUCCESS)
11363 {
11364 break;
11365 }
11366 }
11367 }
11368
11369 if(res != VK_SUCCESS)
11370 {
11371 // Free all already created allocations.
11372 while(allocIndex--)
11373 {
11374 Free(pAllocations[allocIndex]);
11375 }
11376 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
11377 }
11378
11379 return res;
11380}
11381
11382VkResult VmaBlockVector::AllocatePage(
11383 VmaPool hCurrentPool,
11384 uint32_t currentFrameIndex,
11385 VkDeviceSize size,
11386 VkDeviceSize alignment,
11387 const VmaAllocationCreateInfo& createInfo,
11388 VmaSuballocationType suballocType,
11389 VmaAllocation* pAllocation)
11390{
11391 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11392 bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
11393 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11394 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11395 const bool canCreateNewBlock =
11396 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
11397 (m_Blocks.size() < m_MaxBlockCount);
11398 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
11399
11400 // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
11401 // Which in turn is available only when maxBlockCount = 1.
11402 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
11403 {
11404 canMakeOtherLost = false;
11405 }
11406
11407 // Upper address can only be used with linear allocator and within single memory block.
11408 if(isUpperAddress &&
11409 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
11410 {
11411 return VK_ERROR_FEATURE_NOT_PRESENT;
11412 }
11413
11414 // Validate strategy.
11415 switch(strategy)
11416 {
11417 case 0:
11418 strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
11419 break;
11420 case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
11421 case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
11422 case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
11423 break;
11424 default:
11425 return VK_ERROR_FEATURE_NOT_PRESENT;
11426 }
11427
11428 // Early reject: requested allocation size is larger that maximum block size for this block vector.
11429 if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
11430 {
11431 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11432 }
11433
11434 /*
11435 Under certain condition, this whole section can be skipped for optimization, so
11436 we move on directly to trying to allocate with canMakeOtherLost. That's the case
11437 e.g. for custom pools with linear algorithm.
11438 */
11439 if(!canMakeOtherLost || canCreateNewBlock)
11440 {
11441 // 1. Search existing allocations. Try to allocate without making other allocations lost.
11442 VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
11443 allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
11444
11445 if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11446 {
11447 // Use only last block.
11448 if(!m_Blocks.empty())
11449 {
11450 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
11451 VMA_ASSERT(pCurrBlock);
11452 VkResult res = AllocateFromBlock(
11453 pCurrBlock,
11454 hCurrentPool,
11455 currentFrameIndex,
11456 size,
11457 alignment,
11458 allocFlagsCopy,
11459 createInfo.pUserData,
11460 suballocType,
11461 strategy,
11462 pAllocation);
11463 if(res == VK_SUCCESS)
11464 {
11465 VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
11466 return VK_SUCCESS;
11467 }
11468 }
11469 }
11470 else
11471 {
11472 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11473 {
11474 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11475 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11476 {
11477 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11478 VMA_ASSERT(pCurrBlock);
11479 VkResult res = AllocateFromBlock(
11480 pCurrBlock,
11481 hCurrentPool,
11482 currentFrameIndex,
11483 size,
11484 alignment,
11485 allocFlagsCopy,
11486 createInfo.pUserData,
11487 suballocType,
11488 strategy,
11489 pAllocation);
11490 if(res == VK_SUCCESS)
11491 {
11492 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11493 return VK_SUCCESS;
11494 }
11495 }
11496 }
11497 else // WORST_FIT, FIRST_FIT
11498 {
11499 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11500 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11501 {
11502 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11503 VMA_ASSERT(pCurrBlock);
11504 VkResult res = AllocateFromBlock(
11505 pCurrBlock,
11506 hCurrentPool,
11507 currentFrameIndex,
11508 size,
11509 alignment,
11510 allocFlagsCopy,
11511 createInfo.pUserData,
11512 suballocType,
11513 strategy,
11514 pAllocation);
11515 if(res == VK_SUCCESS)
11516 {
11517 VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
11518 return VK_SUCCESS;
11519 }
11520 }
11521 }
11522 }
11523
11524 // 2. Try to create new block.
11525 if(canCreateNewBlock)
11526 {
11527 // Calculate optimal size for new block.
11528 VkDeviceSize newBlockSize = m_PreferredBlockSize;
11529 uint32_t newBlockSizeShift = 0;
11530 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
11531
11532 if(!m_ExplicitBlockSize)
11533 {
11534 // Allocate 1/8, 1/4, 1/2 as first blocks.
11535 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
11536 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
11537 {
11538 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11539 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
11540 {
11541 newBlockSize = smallerNewBlockSize;
11542 ++newBlockSizeShift;
11543 }
11544 else
11545 {
11546 break;
11547 }
11548 }
11549 }
11550
11551 size_t newBlockIndex = 0;
11552 VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
11553 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
11554 if(!m_ExplicitBlockSize)
11555 {
11556 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
11557 {
11558 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
11559 if(smallerNewBlockSize >= size)
11560 {
11561 newBlockSize = smallerNewBlockSize;
11562 ++newBlockSizeShift;
11563 res = CreateBlock(newBlockSize, &newBlockIndex);
11564 }
11565 else
11566 {
11567 break;
11568 }
11569 }
11570 }
11571
11572 if(res == VK_SUCCESS)
11573 {
11574 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
11575 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
11576
11577 res = AllocateFromBlock(
11578 pBlock,
11579 hCurrentPool,
11580 currentFrameIndex,
11581 size,
11582 alignment,
11583 allocFlagsCopy,
11584 createInfo.pUserData,
11585 suballocType,
11586 strategy,
11587 pAllocation);
11588 if(res == VK_SUCCESS)
11589 {
11590 VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
11591 return VK_SUCCESS;
11592 }
11593 else
11594 {
11595 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
11596 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11597 }
11598 }
11599 }
11600 }
11601
11602 // 3. Try to allocate from existing blocks with making other allocations lost.
11603 if(canMakeOtherLost)
11604 {
11605 uint32_t tryIndex = 0;
11606 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
11607 {
11608 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
11609 VmaAllocationRequest bestRequest = {};
11610 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
11611
11612 // 1. Search existing allocations.
11613 if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
11614 {
11615 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
11616 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
11617 {
11618 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11619 VMA_ASSERT(pCurrBlock);
11620 VmaAllocationRequest currRequest = {};
11621 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11622 currentFrameIndex,
11623 m_FrameInUseCount,
11624 m_BufferImageGranularity,
11625 size,
11626 alignment,
11627 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11628 suballocType,
11629 canMakeOtherLost,
11630 strategy,
11631 &currRequest))
11632 {
11633 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11634 if(pBestRequestBlock == VMA_NULL ||
11635 currRequestCost < bestRequestCost)
11636 {
11637 pBestRequestBlock = pCurrBlock;
11638 bestRequest = currRequest;
11639 bestRequestCost = currRequestCost;
11640
11641 if(bestRequestCost == 0)
11642 {
11643 break;
11644 }
11645 }
11646 }
11647 }
11648 }
11649 else // WORST_FIT, FIRST_FIT
11650 {
11651 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
11652 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
11653 {
11654 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
11655 VMA_ASSERT(pCurrBlock);
11656 VmaAllocationRequest currRequest = {};
11657 if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
11658 currentFrameIndex,
11659 m_FrameInUseCount,
11660 m_BufferImageGranularity,
11661 size,
11662 alignment,
11663 (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
11664 suballocType,
11665 canMakeOtherLost,
11666 strategy,
11667 &currRequest))
11668 {
11669 const VkDeviceSize currRequestCost = currRequest.CalcCost();
11670 if(pBestRequestBlock == VMA_NULL ||
11671 currRequestCost < bestRequestCost ||
11672 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11673 {
11674 pBestRequestBlock = pCurrBlock;
11675 bestRequest = currRequest;
11676 bestRequestCost = currRequestCost;
11677
11678 if(bestRequestCost == 0 ||
11679 strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
11680 {
11681 break;
11682 }
11683 }
11684 }
11685 }
11686 }
11687
11688 if(pBestRequestBlock != VMA_NULL)
11689 {
11690 if(mapped)
11691 {
11692 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
11693 if(res != VK_SUCCESS)
11694 {
11695 return res;
11696 }
11697 }
11698
11699 if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
11700 currentFrameIndex,
11701 m_FrameInUseCount,
11702 &bestRequest))
11703 {
11704 // We no longer have an empty Allocation.
11705 if(pBestRequestBlock->m_pMetadata->IsEmpty())
11706 {
11707 m_HasEmptyBlock = false;
11708 }
11709 // Allocate from this pBlock.
11710 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11711 pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
11712 (*pAllocation)->InitBlockAllocation(
11713 hCurrentPool,
11714 pBestRequestBlock,
11715 bestRequest.offset,
11716 alignment,
11717 size,
11718 suballocType,
11719 mapped,
11720 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11721 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
11722 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
11723 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
11724 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11725 {
11726 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11727 }
11728 if(IsCorruptionDetectionEnabled())
11729 {
11730 VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
11731 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11732 }
11733 return VK_SUCCESS;
11734 }
11735 // else: Some allocations must have been touched while we are here. Next try.
11736 }
11737 else
11738 {
11739 // Could not find place in any of the blocks - break outer loop.
11740 break;
11741 }
11742 }
11743 /* Maximum number of tries exceeded - a very unlike event when many other
11744 threads are simultaneously touching allocations making it impossible to make
11745 lost at the same time as we try to allocate. */
11746 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
11747 {
11748 return VK_ERROR_TOO_MANY_OBJECTS;
11749 }
11750 }
11751
11752 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11753}
11754
11755void VmaBlockVector::Free(
11756 VmaAllocation hAllocation)
11757{
11758 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
11759
11760 // Scope for lock.
11761 {
11762 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
11763
11764 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
11765
11766 if(IsCorruptionDetectionEnabled())
11767 {
11768 VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
11769 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
11770 }
11771
11772 if(hAllocation->IsPersistentMap())
11773 {
11774 pBlock->Unmap(m_hAllocator, 1);
11775 }
11776
11777 pBlock->m_pMetadata->Free(hAllocation);
11778 VMA_HEAVY_ASSERT(pBlock->Validate());
11779
11780 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
11781
11782 // pBlock became empty after this deallocation.
11783 if(pBlock->m_pMetadata->IsEmpty())
11784 {
11785 // Already has empty Allocation. We don't want to have two, so delete this one.
11786 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
11787 {
11788 pBlockToDelete = pBlock;
11789 Remove(pBlock);
11790 }
11791 // We now have first empty block.
11792 else
11793 {
11794 m_HasEmptyBlock = true;
11795 }
11796 }
11797 // pBlock didn't become empty, but we have another empty block - find and free that one.
11798 // (This is optional, heuristics.)
11799 else if(m_HasEmptyBlock)
11800 {
11801 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
11802 if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
11803 {
11804 pBlockToDelete = pLastBlock;
11805 m_Blocks.pop_back();
11806 m_HasEmptyBlock = false;
11807 }
11808 }
11809
11810 IncrementallySortBlocks();
11811 }
11812
11813 // Destruction of a free Allocation. Deferred until this point, outside of mutex
11814 // lock, for performance reason.
11815 if(pBlockToDelete != VMA_NULL)
11816 {
11817 VMA_DEBUG_LOG(" Deleted empty allocation");
11818 pBlockToDelete->Destroy(m_hAllocator);
11819 vma_delete(m_hAllocator, pBlockToDelete);
11820 }
11821}
11822
11823VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
11824{
11825 VkDeviceSize result = 0;
11826 for(size_t i = m_Blocks.size(); i--; )
11827 {
11828 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
11829 if(result >= m_PreferredBlockSize)
11830 {
11831 break;
11832 }
11833 }
11834 return result;
11835}
11836
11837void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
11838{
11839 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
11840 {
11841 if(m_Blocks[blockIndex] == pBlock)
11842 {
11843 VmaVectorRemove(m_Blocks, blockIndex);
11844 return;
11845 }
11846 }
11847 VMA_ASSERT(0);
11848}
11849
11850void VmaBlockVector::IncrementallySortBlocks()
11851{
11852 if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
11853 {
11854 // Bubble sort only until first swap.
11855 for(size_t i = 1; i < m_Blocks.size(); ++i)
11856 {
11857 if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
11858 {
11859 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
11860 return;
11861 }
11862 }
11863 }
11864}
11865
11866VkResult VmaBlockVector::AllocateFromBlock(
11867 VmaDeviceMemoryBlock* pBlock,
11868 VmaPool hCurrentPool,
11869 uint32_t currentFrameIndex,
11870 VkDeviceSize size,
11871 VkDeviceSize alignment,
11872 VmaAllocationCreateFlags allocFlags,
11873 void* pUserData,
11874 VmaSuballocationType suballocType,
11875 uint32_t strategy,
11876 VmaAllocation* pAllocation)
11877{
11878 VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
11879 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
11880 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
11881 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
11882
11883 VmaAllocationRequest currRequest = {};
11884 if(pBlock->m_pMetadata->CreateAllocationRequest(
11885 currentFrameIndex,
11886 m_FrameInUseCount,
11887 m_BufferImageGranularity,
11888 size,
11889 alignment,
11890 isUpperAddress,
11891 suballocType,
11892 false, // canMakeOtherLost
11893 strategy,
11894 &currRequest))
11895 {
11896 // Allocate from pCurrBlock.
11897 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
11898
11899 if(mapped)
11900 {
11901 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
11902 if(res != VK_SUCCESS)
11903 {
11904 return res;
11905 }
11906 }
11907
11908 // We no longer have an empty Allocation.
11909 if(pBlock->m_pMetadata->IsEmpty())
11910 {
11911 m_HasEmptyBlock = false;
11912 }
11913
11914 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
11915 pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
11916 (*pAllocation)->InitBlockAllocation(
11917 hCurrentPool,
11918 pBlock,
11919 currRequest.offset,
11920 alignment,
11921 size,
11922 suballocType,
11923 mapped,
11924 (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
11925 VMA_HEAVY_ASSERT(pBlock->Validate());
11926 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
11927 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
11928 {
11929 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
11930 }
11931 if(IsCorruptionDetectionEnabled())
11932 {
11933 VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
11934 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
11935 }
11936 return VK_SUCCESS;
11937 }
11938 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11939}
11940
11941VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
11942{
11943 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
11944 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
11945 allocInfo.allocationSize = blockSize;
11946 VkDeviceMemory mem = VK_NULL_HANDLE;
11947 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
11948 if(res < 0)
11949 {
11950 return res;
11951 }
11952
11953 // New VkDeviceMemory successfully created.
11954
11955 // Create new Allocation for it.
11956 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
11957 pBlock->Init(
11958 m_hAllocator,
11959 m_MemoryTypeIndex,
11960 mem,
11961 allocInfo.allocationSize,
11962 m_NextBlockId++,
11963 m_Algorithm);
11964
11965 m_Blocks.push_back(pBlock);
11966 if(pNewBlockIndex != VMA_NULL)
11967 {
11968 *pNewBlockIndex = m_Blocks.size() - 1;
11969 }
11970
11971 return VK_SUCCESS;
11972}
11973
11974void VmaBlockVector::ApplyDefragmentationMovesCpu(
11975 class VmaBlockVectorDefragmentationContext* pDefragCtx,
11976 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
11977{
11978 const size_t blockCount = m_Blocks.size();
11979 const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
11980
11981 enum BLOCK_FLAG
11982 {
11983 BLOCK_FLAG_USED = 0x00000001,
11984 BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
11985 };
11986
11987 struct BlockInfo
11988 {
11989 uint32_t flags;
11990 void* pMappedData;
11991 };
11992 VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
11993 blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
11994 memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
11995
11996 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
11997 const size_t moveCount = moves.size();
11998 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
11999 {
12000 const VmaDefragmentationMove& move = moves[moveIndex];
12001 blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
12002 blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
12003 }
12004
12005 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12006
12007 // Go over all blocks. Get mapped pointer or map if necessary.
12008 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12009 {
12010 BlockInfo& currBlockInfo = blockInfo[blockIndex];
12011 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12012 if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
12013 {
12014 currBlockInfo.pMappedData = pBlock->GetMappedData();
12015 // It is not originally mapped - map it.
12016 if(currBlockInfo.pMappedData == VMA_NULL)
12017 {
12018 pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
12019 if(pDefragCtx->res == VK_SUCCESS)
12020 {
12021 currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
12022 }
12023 }
12024 }
12025 }
12026
12027 // Go over all moves. Do actual data transfer.
12028 if(pDefragCtx->res == VK_SUCCESS)
12029 {
12030 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12031 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12032
12033 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12034 {
12035 const VmaDefragmentationMove& move = moves[moveIndex];
12036
12037 const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
12038 const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
12039
12040 VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
12041
12042 // Invalidate source.
12043 if(isNonCoherent)
12044 {
12045 VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
12046 memRange.memory = pSrcBlock->GetDeviceMemory();
12047 memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
12048 memRange.size = VMA_MIN(
12049 VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
12050 pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
12051 (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12052 }
12053
12054 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
12055 memmove(
12056 reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
12057 reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
12058 static_cast<size_t>(move.size));
12059
12060 if(IsCorruptionDetectionEnabled())
12061 {
12062 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
12063 VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
12064 }
12065
12066 // Flush destination.
12067 if(isNonCoherent)
12068 {
12069 VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
12070 memRange.memory = pDstBlock->GetDeviceMemory();
12071 memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
12072 memRange.size = VMA_MIN(
12073 VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
12074 pDstBlock->m_pMetadata->GetSize() - memRange.offset);
12075 (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
12076 }
12077 }
12078 }
12079
12080 // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
12081 // Regardless of pCtx->res == VK_SUCCESS.
12082 for(size_t blockIndex = blockCount; blockIndex--; )
12083 {
12084 const BlockInfo& currBlockInfo = blockInfo[blockIndex];
12085 if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
12086 {
12087 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12088 pBlock->Unmap(m_hAllocator, 1);
12089 }
12090 }
12091}
12092
12093void VmaBlockVector::ApplyDefragmentationMovesGpu(
12094 class VmaBlockVectorDefragmentationContext* pDefragCtx,
12095 const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12096 VkCommandBuffer commandBuffer)
12097{
12098 const size_t blockCount = m_Blocks.size();
12099
12100 pDefragCtx->blockContexts.resize(blockCount);
12101 memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
12102
12103 // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
12104 const size_t moveCount = moves.size();
12105 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12106 {
12107 const VmaDefragmentationMove& move = moves[moveIndex];
12108 pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12109 pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
12110 }
12111
12112 VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
12113
12114 // Go over all blocks. Create and bind buffer for whole block if necessary.
12115 {
12116 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
12117 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
12118 VK_BUFFER_USAGE_TRANSFER_DST_BIT;
12119
12120 for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
12121 {
12122 VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
12123 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12124 if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
12125 {
12126 bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
12127 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
12128 m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
12129 if(pDefragCtx->res == VK_SUCCESS)
12130 {
12131 pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
12132 m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
12133 }
12134 }
12135 }
12136 }
12137
12138 // Go over all moves. Post data transfer commands to command buffer.
12139 if(pDefragCtx->res == VK_SUCCESS)
12140 {
12141 const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
12142 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
12143
12144 for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
12145 {
12146 const VmaDefragmentationMove& move = moves[moveIndex];
12147
12148 const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
12149 const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
12150
12151 VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
12152
12153 VkBufferCopy region = {
12154 move.srcOffset,
12155 move.dstOffset,
12156 move.size };
12157 (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
12158 commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
12159 }
12160 }
12161
12162 // Save buffers to defrag context for later destruction.
12163 if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
12164 {
12165 pDefragCtx->res = VK_NOT_READY;
12166 }
12167}
12168
12169void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
12170{
12171 m_HasEmptyBlock = false;
12172 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12173 {
12174 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12175 if(pBlock->m_pMetadata->IsEmpty())
12176 {
12177 if(m_Blocks.size() > m_MinBlockCount)
12178 {
12179 if(pDefragmentationStats != VMA_NULL)
12180 {
12181 ++pDefragmentationStats->deviceMemoryBlocksFreed;
12182 pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
12183 }
12184
12185 VmaVectorRemove(m_Blocks, blockIndex);
12186 pBlock->Destroy(m_hAllocator);
12187 vma_delete(m_hAllocator, pBlock);
12188 }
12189 else
12190 {
12191 m_HasEmptyBlock = true;
12192 }
12193 }
12194 }
12195}
12196
12197#if VMA_STATS_STRING_ENABLED
12198
12199void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12200{
12201 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12202
12203 json.BeginObject();
12204
12205 if(m_IsCustomPool)
12206 {
12207 json.WriteString("MemoryTypeIndex");
12208 json.WriteNumber(m_MemoryTypeIndex);
12209
12210 json.WriteString("BlockSize");
12211 json.WriteNumber(m_PreferredBlockSize);
12212
12213 json.WriteString("BlockCount");
12214 json.BeginObject(true);
12215 if(m_MinBlockCount > 0)
12216 {
12217 json.WriteString("Min");
12218 json.WriteNumber((uint64_t)m_MinBlockCount);
12219 }
12220 if(m_MaxBlockCount < SIZE_MAX)
12221 {
12222 json.WriteString("Max");
12223 json.WriteNumber((uint64_t)m_MaxBlockCount);
12224 }
12225 json.WriteString("Cur");
12226 json.WriteNumber((uint64_t)m_Blocks.size());
12227 json.EndObject();
12228
12229 if(m_FrameInUseCount > 0)
12230 {
12231 json.WriteString("FrameInUseCount");
12232 json.WriteNumber(m_FrameInUseCount);
12233 }
12234
12235 if(m_Algorithm != 0)
12236 {
12237 json.WriteString("Algorithm");
12238 json.WriteString(VmaAlgorithmToStr(m_Algorithm));
12239 }
12240 }
12241 else
12242 {
12243 json.WriteString("PreferredBlockSize");
12244 json.WriteNumber(m_PreferredBlockSize);
12245 }
12246
12247 json.WriteString("Blocks");
12248 json.BeginObject();
12249 for(size_t i = 0; i < m_Blocks.size(); ++i)
12250 {
12251 json.BeginString();
12252 json.ContinueString(m_Blocks[i]->GetId());
12253 json.EndString();
12254
12255 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12256 }
12257 json.EndObject();
12258
12259 json.EndObject();
12260}
12261
12262#endif // #if VMA_STATS_STRING_ENABLED
12263
12264void VmaBlockVector::Defragment(
12265 class VmaBlockVectorDefragmentationContext* pCtx,
12266 VmaDefragmentationStats* pStats,
12267 VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
12268 VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
12269 VkCommandBuffer commandBuffer)
12270{
12271 pCtx->res = VK_SUCCESS;
12272
12273 const VkMemoryPropertyFlags memPropFlags =
12274 m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
12275 const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12276 const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
12277
12278 const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
12279 isHostVisible;
12280 const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
12281 (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
12282
12283 // There are options to defragment this memory type.
12284 if(canDefragmentOnCpu || canDefragmentOnGpu)
12285 {
12286 bool defragmentOnGpu;
12287 // There is only one option to defragment this memory type.
12288 if(canDefragmentOnGpu != canDefragmentOnCpu)
12289 {
12290 defragmentOnGpu = canDefragmentOnGpu;
12291 }
12292 // Both options are available: Heuristics to choose the best one.
12293 else
12294 {
12295 defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
12296 m_hAllocator->IsIntegratedGpu();
12297 }
12298
12299 bool overlappingMoveSupported = !defragmentOnGpu;
12300
12301 if(m_hAllocator->m_UseMutex)
12302 {
12303 m_Mutex.LockWrite();
12304 pCtx->mutexLocked = true;
12305 }
12306
12307 pCtx->Begin(overlappingMoveSupported);
12308
12309 // Defragment.
12310
12311 const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
12312 const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
12313 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
12314 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
12315 pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
12316
12317 // Accumulate statistics.
12318 if(pStats != VMA_NULL)
12319 {
12320 const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
12321 const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
12322 pStats->bytesMoved += bytesMoved;
12323 pStats->allocationsMoved += allocationsMoved;
12324 VMA_ASSERT(bytesMoved <= maxBytesToMove);
12325 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
12326 if(defragmentOnGpu)
12327 {
12328 maxGpuBytesToMove -= bytesMoved;
12329 maxGpuAllocationsToMove -= allocationsMoved;
12330 }
12331 else
12332 {
12333 maxCpuBytesToMove -= bytesMoved;
12334 maxCpuAllocationsToMove -= allocationsMoved;
12335 }
12336 }
12337
12338 if(pCtx->res >= VK_SUCCESS)
12339 {
12340 if(defragmentOnGpu)
12341 {
12342 ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
12343 }
12344 else
12345 {
12346 ApplyDefragmentationMovesCpu(pCtx, moves);
12347 }
12348 }
12349 }
12350}
12351
12352void VmaBlockVector::DefragmentationEnd(
12353 class VmaBlockVectorDefragmentationContext* pCtx,
12354 VmaDefragmentationStats* pStats)
12355{
12356 // Destroy buffers.
12357 for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
12358 {
12359 VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
12360 if(blockCtx.hBuffer)
12361 {
12362 (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
12363 m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
12364 }
12365 }
12366
12367 if(pCtx->res >= VK_SUCCESS)
12368 {
12369 FreeEmptyBlocks(pStats);
12370 }
12371
12372 if(pCtx->mutexLocked)
12373 {
12374 VMA_ASSERT(m_hAllocator->m_UseMutex);
12375 m_Mutex.UnlockWrite();
12376 }
12377}
12378
12379size_t VmaBlockVector::CalcAllocationCount() const
12380{
12381 size_t result = 0;
12382 for(size_t i = 0; i < m_Blocks.size(); ++i)
12383 {
12384 result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
12385 }
12386 return result;
12387}
12388
12389bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
12390{
12391 if(m_BufferImageGranularity == 1)
12392 {
12393 return false;
12394 }
12395 VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
12396 for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
12397 {
12398 VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
12399 VMA_ASSERT(m_Algorithm == 0);
12400 VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
12401 if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
12402 {
12403 return true;
12404 }
12405 }
12406 return false;
12407}
12408
12409void VmaBlockVector::MakePoolAllocationsLost(
12410 uint32_t currentFrameIndex,
12411 size_t* pLostAllocationCount)
12412{
12413 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12414 size_t lostAllocationCount = 0;
12415 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12416 {
12417 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12418 VMA_ASSERT(pBlock);
12419 lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
12420 }
12421 if(pLostAllocationCount != VMA_NULL)
12422 {
12423 *pLostAllocationCount = lostAllocationCount;
12424 }
12425}
12426
12427VkResult VmaBlockVector::CheckCorruption()
12428{
12429 if(!IsCorruptionDetectionEnabled())
12430 {
12431 return VK_ERROR_FEATURE_NOT_PRESENT;
12432 }
12433
12434 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12435 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12436 {
12437 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12438 VMA_ASSERT(pBlock);
12439 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12440 if(res != VK_SUCCESS)
12441 {
12442 return res;
12443 }
12444 }
12445 return VK_SUCCESS;
12446}
12447
12448void VmaBlockVector::AddStats(VmaStats* pStats)
12449{
12450 const uint32_t memTypeIndex = m_MemoryTypeIndex;
12451 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
12452
12453 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12454
12455 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12456 {
12457 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12458 VMA_ASSERT(pBlock);
12459 VMA_HEAVY_ASSERT(pBlock->Validate());
12460 VmaStatInfo allocationStatInfo;
12461 pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
12462 VmaAddStatInfo(pStats->total, allocationStatInfo);
12463 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
12464 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
12465 }
12466}
12467
12468////////////////////////////////////////////////////////////////////////////////
12469// VmaDefragmentationAlgorithm_Generic members definition
12470
12471VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
12472 VmaAllocator hAllocator,
12473 VmaBlockVector* pBlockVector,
12474 uint32_t currentFrameIndex,
12475 bool overlappingMoveSupported) :
12476 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060012477 m_AllocationCount(0),
Tony-LunarG390319b2019-03-18 15:54:16 -060012478 m_AllAllocations(false),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060012479 m_BytesMoved(0),
12480 m_AllocationsMoved(0),
12481 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
12482{
12483 // Create block info for each block.
12484 const size_t blockCount = m_pBlockVector->m_Blocks.size();
12485 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12486 {
12487 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
12488 pBlockInfo->m_OriginalBlockIndex = blockIndex;
12489 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
12490 m_Blocks.push_back(pBlockInfo);
12491 }
12492
12493 // Sort them by m_pBlock pointer value.
12494 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
12495}
12496
12497VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
12498{
12499 for(size_t i = m_Blocks.size(); i--; )
12500 {
12501 vma_delete(m_hAllocator, m_Blocks[i]);
12502 }
12503}
12504
12505void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
12506{
12507 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
12508 if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
12509 {
12510 VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
12511 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
12512 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
12513 {
12514 AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
12515 (*it)->m_Allocations.push_back(allocInfo);
12516 }
12517 else
12518 {
12519 VMA_ASSERT(0);
12520 }
12521
12522 ++m_AllocationCount;
12523 }
12524}
12525
12526VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
12527 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12528 VkDeviceSize maxBytesToMove,
12529 uint32_t maxAllocationsToMove)
12530{
12531 if(m_Blocks.empty())
12532 {
12533 return VK_SUCCESS;
12534 }
12535
12536 // This is a choice based on research.
12537 // Option 1:
12538 uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
12539 // Option 2:
12540 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
12541 // Option 3:
12542 //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
12543
12544 size_t srcBlockMinIndex = 0;
12545 // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
12546 /*
12547 if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
12548 {
12549 const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
12550 if(blocksWithNonMovableCount > 0)
12551 {
12552 srcBlockMinIndex = blocksWithNonMovableCount - 1;
12553 }
12554 }
12555 */
12556
12557 size_t srcBlockIndex = m_Blocks.size() - 1;
12558 size_t srcAllocIndex = SIZE_MAX;
12559 for(;;)
12560 {
12561 // 1. Find next allocation to move.
12562 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
12563 // 1.2. Then start from last to first m_Allocations.
12564 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
12565 {
12566 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
12567 {
12568 // Finished: no more allocations to process.
12569 if(srcBlockIndex == srcBlockMinIndex)
12570 {
12571 return VK_SUCCESS;
12572 }
12573 else
12574 {
12575 --srcBlockIndex;
12576 srcAllocIndex = SIZE_MAX;
12577 }
12578 }
12579 else
12580 {
12581 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
12582 }
12583 }
12584
12585 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
12586 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
12587
12588 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
12589 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
12590 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
12591 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
12592
12593 // 2. Try to find new place for this allocation in preceding or current block.
12594 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
12595 {
12596 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
12597 VmaAllocationRequest dstAllocRequest;
12598 if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
12599 m_CurrentFrameIndex,
12600 m_pBlockVector->GetFrameInUseCount(),
12601 m_pBlockVector->GetBufferImageGranularity(),
12602 size,
12603 alignment,
12604 false, // upperAddress
12605 suballocType,
12606 false, // canMakeOtherLost
12607 strategy,
12608 &dstAllocRequest) &&
12609 MoveMakesSense(
12610 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
12611 {
12612 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
12613
12614 // Reached limit on number of allocations or bytes to move.
12615 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
12616 (m_BytesMoved + size > maxBytesToMove))
12617 {
12618 return VK_SUCCESS;
12619 }
12620
12621 VmaDefragmentationMove move;
12622 move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
12623 move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
12624 move.srcOffset = srcOffset;
12625 move.dstOffset = dstAllocRequest.offset;
12626 move.size = size;
12627 moves.push_back(move);
12628
12629 pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
12630 dstAllocRequest,
12631 suballocType,
12632 size,
12633 false, // upperAddress
12634 allocInfo.m_hAllocation);
12635 pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
12636
12637 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
12638
12639 if(allocInfo.m_pChanged != VMA_NULL)
12640 {
12641 *allocInfo.m_pChanged = VK_TRUE;
12642 }
12643
12644 ++m_AllocationsMoved;
12645 m_BytesMoved += size;
12646
12647 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
12648
12649 break;
12650 }
12651 }
12652
12653 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
12654
12655 if(srcAllocIndex > 0)
12656 {
12657 --srcAllocIndex;
12658 }
12659 else
12660 {
12661 if(srcBlockIndex > 0)
12662 {
12663 --srcBlockIndex;
12664 srcAllocIndex = SIZE_MAX;
12665 }
12666 else
12667 {
12668 return VK_SUCCESS;
12669 }
12670 }
12671 }
12672}
12673
12674size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
12675{
12676 size_t result = 0;
12677 for(size_t i = 0; i < m_Blocks.size(); ++i)
12678 {
12679 if(m_Blocks[i]->m_HasNonMovableAllocations)
12680 {
12681 ++result;
12682 }
12683 }
12684 return result;
12685}
12686
12687VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
12688 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12689 VkDeviceSize maxBytesToMove,
12690 uint32_t maxAllocationsToMove)
12691{
12692 if(!m_AllAllocations && m_AllocationCount == 0)
12693 {
12694 return VK_SUCCESS;
12695 }
12696
12697 const size_t blockCount = m_Blocks.size();
12698 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12699 {
12700 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
12701
12702 if(m_AllAllocations)
12703 {
12704 VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
12705 for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
12706 it != pMetadata->m_Suballocations.end();
12707 ++it)
12708 {
12709 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
12710 {
12711 AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
12712 pBlockInfo->m_Allocations.push_back(allocInfo);
12713 }
12714 }
12715 }
12716
12717 pBlockInfo->CalcHasNonMovableAllocations();
12718
12719 // This is a choice based on research.
12720 // Option 1:
12721 pBlockInfo->SortAllocationsByOffsetDescending();
12722 // Option 2:
12723 //pBlockInfo->SortAllocationsBySizeDescending();
12724 }
12725
12726 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
12727 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
12728
12729 // This is a choice based on research.
12730 const uint32_t roundCount = 2;
12731
12732 // Execute defragmentation rounds (the main part).
12733 VkResult result = VK_SUCCESS;
12734 for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
12735 {
12736 result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
12737 }
12738
12739 return result;
12740}
12741
12742bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
12743 size_t dstBlockIndex, VkDeviceSize dstOffset,
12744 size_t srcBlockIndex, VkDeviceSize srcOffset)
12745{
12746 if(dstBlockIndex < srcBlockIndex)
12747 {
12748 return true;
12749 }
12750 if(dstBlockIndex > srcBlockIndex)
12751 {
12752 return false;
12753 }
12754 if(dstOffset < srcOffset)
12755 {
12756 return true;
12757 }
12758 return false;
12759}
12760
12761////////////////////////////////////////////////////////////////////////////////
12762// VmaDefragmentationAlgorithm_Fast
12763
12764VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
12765 VmaAllocator hAllocator,
12766 VmaBlockVector* pBlockVector,
12767 uint32_t currentFrameIndex,
12768 bool overlappingMoveSupported) :
12769 VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
12770 m_OverlappingMoveSupported(overlappingMoveSupported),
12771 m_AllocationCount(0),
12772 m_AllAllocations(false),
12773 m_BytesMoved(0),
12774 m_AllocationsMoved(0),
12775 m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
12776{
12777 VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
12778
12779}
12780
12781VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
12782{
12783}
12784
12785VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
12786 VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
12787 VkDeviceSize maxBytesToMove,
12788 uint32_t maxAllocationsToMove)
12789{
12790 VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
12791
12792 const size_t blockCount = m_pBlockVector->GetBlockCount();
12793 if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
12794 {
12795 return VK_SUCCESS;
12796 }
12797
12798 PreprocessMetadata();
12799
12800 // Sort blocks in order from most destination.
12801
12802 m_BlockInfos.resize(blockCount);
12803 for(size_t i = 0; i < blockCount; ++i)
12804 {
12805 m_BlockInfos[i].origBlockIndex = i;
12806 }
12807
12808 VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
12809 return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
12810 m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
12811 });
12812
12813 // THE MAIN ALGORITHM
12814
12815 FreeSpaceDatabase freeSpaceDb;
12816
12817 size_t dstBlockInfoIndex = 0;
12818 size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12819 VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12820 VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12821 VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
12822 VkDeviceSize dstOffset = 0;
12823
12824 bool end = false;
12825 for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
12826 {
12827 const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
12828 VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
12829 VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
12830 for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
12831 !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
12832 {
12833 VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
12834 const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
12835 const VkDeviceSize srcAllocSize = srcSuballocIt->size;
12836 if(m_AllocationsMoved == maxAllocationsToMove ||
12837 m_BytesMoved + srcAllocSize > maxBytesToMove)
12838 {
12839 end = true;
12840 break;
12841 }
12842 const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
12843
12844 // Try to place it in one of free spaces from the database.
12845 size_t freeSpaceInfoIndex;
12846 VkDeviceSize dstAllocOffset;
12847 if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
12848 freeSpaceInfoIndex, dstAllocOffset))
12849 {
12850 size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
12851 VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
12852 VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
12853 VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
12854
12855 // Same block
12856 if(freeSpaceInfoIndex == srcBlockInfoIndex)
12857 {
12858 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12859
12860 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12861
12862 VmaSuballocation suballoc = *srcSuballocIt;
12863 suballoc.offset = dstAllocOffset;
12864 suballoc.hAllocation->ChangeOffset(dstAllocOffset);
12865 m_BytesMoved += srcAllocSize;
12866 ++m_AllocationsMoved;
12867
12868 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12869 ++nextSuballocIt;
12870 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12871 srcSuballocIt = nextSuballocIt;
12872
12873 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12874
12875 VmaDefragmentationMove move = {
12876 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12877 srcAllocOffset, dstAllocOffset,
12878 srcAllocSize };
12879 moves.push_back(move);
12880 }
12881 // Different block
12882 else
12883 {
12884 // MOVE OPTION 2: Move the allocation to a different block.
12885
12886 VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
12887
12888 VmaSuballocation suballoc = *srcSuballocIt;
12889 suballoc.offset = dstAllocOffset;
12890 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
12891 m_BytesMoved += srcAllocSize;
12892 ++m_AllocationsMoved;
12893
12894 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12895 ++nextSuballocIt;
12896 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12897 srcSuballocIt = nextSuballocIt;
12898
12899 InsertSuballoc(pFreeSpaceMetadata, suballoc);
12900
12901 VmaDefragmentationMove move = {
12902 srcOrigBlockIndex, freeSpaceOrigBlockIndex,
12903 srcAllocOffset, dstAllocOffset,
12904 srcAllocSize };
12905 moves.push_back(move);
12906 }
12907 }
12908 else
12909 {
12910 dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
12911
12912 // If the allocation doesn't fit before the end of dstBlock, forward to next block.
12913 while(dstBlockInfoIndex < srcBlockInfoIndex &&
12914 dstAllocOffset + srcAllocSize > dstBlockSize)
12915 {
12916 // But before that, register remaining free space at the end of dst block.
12917 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
12918
12919 ++dstBlockInfoIndex;
12920 dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
12921 pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
12922 pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
12923 dstBlockSize = pDstMetadata->GetSize();
12924 dstOffset = 0;
12925 dstAllocOffset = 0;
12926 }
12927
12928 // Same block
12929 if(dstBlockInfoIndex == srcBlockInfoIndex)
12930 {
12931 VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
12932
12933 const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
12934
12935 bool skipOver = overlap;
12936 if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
12937 {
12938 // If destination and source place overlap, skip if it would move it
12939 // by only < 1/64 of its size.
12940 skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
12941 }
12942
12943 if(skipOver)
12944 {
12945 freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
12946
12947 dstOffset = srcAllocOffset + srcAllocSize;
12948 ++srcSuballocIt;
12949 }
12950 // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
12951 else
12952 {
12953 srcSuballocIt->offset = dstAllocOffset;
12954 srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
12955 dstOffset = dstAllocOffset + srcAllocSize;
12956 m_BytesMoved += srcAllocSize;
12957 ++m_AllocationsMoved;
12958 ++srcSuballocIt;
12959 VmaDefragmentationMove move = {
12960 srcOrigBlockIndex, dstOrigBlockIndex,
12961 srcAllocOffset, dstAllocOffset,
12962 srcAllocSize };
12963 moves.push_back(move);
12964 }
12965 }
12966 // Different block
12967 else
12968 {
12969 // MOVE OPTION 2: Move the allocation to a different block.
12970
12971 VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
12972 VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
12973
12974 VmaSuballocation suballoc = *srcSuballocIt;
12975 suballoc.offset = dstAllocOffset;
12976 suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
12977 dstOffset = dstAllocOffset + srcAllocSize;
12978 m_BytesMoved += srcAllocSize;
12979 ++m_AllocationsMoved;
12980
12981 VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
12982 ++nextSuballocIt;
12983 pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
12984 srcSuballocIt = nextSuballocIt;
12985
12986 pDstMetadata->m_Suballocations.push_back(suballoc);
12987
12988 VmaDefragmentationMove move = {
12989 srcOrigBlockIndex, dstOrigBlockIndex,
12990 srcAllocOffset, dstAllocOffset,
12991 srcAllocSize };
12992 moves.push_back(move);
12993 }
12994 }
12995 }
12996 }
12997
12998 m_BlockInfos.clear();
12999
13000 PostprocessMetadata();
13001
13002 return VK_SUCCESS;
13003}
13004
13005void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
13006{
13007 const size_t blockCount = m_pBlockVector->GetBlockCount();
13008 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13009 {
13010 VmaBlockMetadata_Generic* const pMetadata =
13011 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13012 pMetadata->m_FreeCount = 0;
13013 pMetadata->m_SumFreeSize = pMetadata->GetSize();
13014 pMetadata->m_FreeSuballocationsBySize.clear();
13015 for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13016 it != pMetadata->m_Suballocations.end(); )
13017 {
13018 if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
13019 {
13020 VmaSuballocationList::iterator nextIt = it;
13021 ++nextIt;
13022 pMetadata->m_Suballocations.erase(it);
13023 it = nextIt;
13024 }
13025 else
13026 {
13027 ++it;
13028 }
13029 }
13030 }
13031}
13032
13033void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
13034{
13035 const size_t blockCount = m_pBlockVector->GetBlockCount();
13036 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
13037 {
13038 VmaBlockMetadata_Generic* const pMetadata =
13039 (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
13040 const VkDeviceSize blockSize = pMetadata->GetSize();
13041
13042 // No allocations in this block - entire area is free.
13043 if(pMetadata->m_Suballocations.empty())
13044 {
13045 pMetadata->m_FreeCount = 1;
13046 //pMetadata->m_SumFreeSize is already set to blockSize.
13047 VmaSuballocation suballoc = {
13048 0, // offset
13049 blockSize, // size
13050 VMA_NULL, // hAllocation
13051 VMA_SUBALLOCATION_TYPE_FREE };
13052 pMetadata->m_Suballocations.push_back(suballoc);
13053 pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
13054 }
13055 // There are some allocations in this block.
13056 else
13057 {
13058 VkDeviceSize offset = 0;
13059 VmaSuballocationList::iterator it;
13060 for(it = pMetadata->m_Suballocations.begin();
13061 it != pMetadata->m_Suballocations.end();
13062 ++it)
13063 {
13064 VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
13065 VMA_ASSERT(it->offset >= offset);
13066
13067 // Need to insert preceding free space.
13068 if(it->offset > offset)
13069 {
13070 ++pMetadata->m_FreeCount;
13071 const VkDeviceSize freeSize = it->offset - offset;
13072 VmaSuballocation suballoc = {
13073 offset, // offset
13074 freeSize, // size
13075 VMA_NULL, // hAllocation
13076 VMA_SUBALLOCATION_TYPE_FREE };
13077 VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13078 if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13079 {
13080 pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
13081 }
13082 }
13083
13084 pMetadata->m_SumFreeSize -= it->size;
13085 offset = it->offset + it->size;
13086 }
13087
13088 // Need to insert trailing free space.
13089 if(offset < blockSize)
13090 {
13091 ++pMetadata->m_FreeCount;
13092 const VkDeviceSize freeSize = blockSize - offset;
13093 VmaSuballocation suballoc = {
13094 offset, // offset
13095 freeSize, // size
13096 VMA_NULL, // hAllocation
13097 VMA_SUBALLOCATION_TYPE_FREE };
13098 VMA_ASSERT(it == pMetadata->m_Suballocations.end());
13099 VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
13100 if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
13101 {
13102 pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
13103 }
13104 }
13105
13106 VMA_SORT(
13107 pMetadata->m_FreeSuballocationsBySize.begin(),
13108 pMetadata->m_FreeSuballocationsBySize.end(),
13109 VmaSuballocationItemSizeLess());
13110 }
13111
13112 VMA_HEAVY_ASSERT(pMetadata->Validate());
13113 }
13114}
13115
13116void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
13117{
13118 // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
13119 VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
13120 while(it != pMetadata->m_Suballocations.end())
13121 {
13122 if(it->offset < suballoc.offset)
13123 {
13124 ++it;
13125 }
13126 }
13127 pMetadata->m_Suballocations.insert(it, suballoc);
13128}
13129
13130////////////////////////////////////////////////////////////////////////////////
13131// VmaBlockVectorDefragmentationContext
13132
13133VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
13134 VmaAllocator hAllocator,
13135 VmaPool hCustomPool,
13136 VmaBlockVector* pBlockVector,
13137 uint32_t currFrameIndex,
13138 uint32_t algorithmFlags) :
13139 res(VK_SUCCESS),
13140 mutexLocked(false),
13141 blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
13142 m_hAllocator(hAllocator),
13143 m_hCustomPool(hCustomPool),
13144 m_pBlockVector(pBlockVector),
13145 m_CurrFrameIndex(currFrameIndex),
Mike Schuchardte48dc142019-04-18 09:12:03 -070013146 //m_AlgorithmFlags(algorithmFlags),
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060013147 m_pAlgorithm(VMA_NULL),
13148 m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
13149 m_AllAllocations(false)
13150{
13151}
13152
13153VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
13154{
13155 vma_delete(m_hAllocator, m_pAlgorithm);
13156}
13157
13158void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
13159{
13160 AllocInfo info = { hAlloc, pChanged };
13161 m_Allocations.push_back(info);
13162}
13163
13164void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
13165{
13166 const bool allAllocations = m_AllAllocations ||
13167 m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
13168
13169 /********************************
13170 HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
13171 ********************************/
13172
13173 /*
13174 Fast algorithm is supported only when certain criteria are met:
13175 - VMA_DEBUG_MARGIN is 0.
13176 - All allocations in this block vector are moveable.
13177 - There is no possibility of image/buffer granularity conflict.
13178 */
13179 if(VMA_DEBUG_MARGIN == 0 &&
13180 allAllocations &&
13181 !m_pBlockVector->IsBufferImageGranularityConflictPossible())
13182 {
13183 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
13184 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13185 }
13186 else
13187 {
13188 m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
13189 m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
13190 }
13191
13192 if(allAllocations)
13193 {
13194 m_pAlgorithm->AddAll();
13195 }
13196 else
13197 {
13198 for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
13199 {
13200 m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
13201 }
13202 }
13203}
13204
13205////////////////////////////////////////////////////////////////////////////////
13206// VmaDefragmentationContext
13207
13208VmaDefragmentationContext_T::VmaDefragmentationContext_T(
13209 VmaAllocator hAllocator,
13210 uint32_t currFrameIndex,
13211 uint32_t flags,
13212 VmaDefragmentationStats* pStats) :
13213 m_hAllocator(hAllocator),
13214 m_CurrFrameIndex(currFrameIndex),
13215 m_Flags(flags),
13216 m_pStats(pStats),
13217 m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
13218{
13219 memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
13220}
13221
13222VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13223{
13224 for(size_t i = m_CustomPoolContexts.size(); i--; )
13225 {
13226 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
13227 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13228 vma_delete(m_hAllocator, pBlockVectorCtx);
13229 }
13230 for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
13231 {
13232 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
13233 if(pBlockVectorCtx)
13234 {
13235 pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
13236 vma_delete(m_hAllocator, pBlockVectorCtx);
13237 }
13238 }
13239}
13240
13241void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
13242{
13243 for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
13244 {
13245 VmaPool pool = pPools[poolIndex];
13246 VMA_ASSERT(pool);
13247 // Pools with algorithm other than default are not defragmented.
13248 if(pool->m_BlockVector.GetAlgorithm() == 0)
13249 {
13250 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13251
13252 for(size_t i = m_CustomPoolContexts.size(); i--; )
13253 {
13254 if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
13255 {
13256 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13257 break;
13258 }
13259 }
13260
13261 if(!pBlockVectorDefragCtx)
13262 {
13263 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13264 m_hAllocator,
13265 pool,
13266 &pool->m_BlockVector,
13267 m_CurrFrameIndex,
13268 m_Flags);
13269 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13270 }
13271
13272 pBlockVectorDefragCtx->AddAll();
13273 }
13274 }
13275}
13276
13277void VmaDefragmentationContext_T::AddAllocations(
13278 uint32_t allocationCount,
13279 VmaAllocation* pAllocations,
13280 VkBool32* pAllocationsChanged)
13281{
13282 // Dispatch pAllocations among defragmentators. Create them when necessary.
13283 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
13284 {
13285 const VmaAllocation hAlloc = pAllocations[allocIndex];
13286 VMA_ASSERT(hAlloc);
13287 // DedicatedAlloc cannot be defragmented.
13288 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
13289 // Lost allocation cannot be defragmented.
13290 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
13291 {
13292 VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
13293
13294 const VmaPool hAllocPool = hAlloc->GetPool();
13295 // This allocation belongs to custom pool.
13296 if(hAllocPool != VK_NULL_HANDLE)
13297 {
13298 // Pools with algorithm other than default are not defragmented.
13299 if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
13300 {
13301 for(size_t i = m_CustomPoolContexts.size(); i--; )
13302 {
13303 if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
13304 {
13305 pBlockVectorDefragCtx = m_CustomPoolContexts[i];
13306 break;
13307 }
13308 }
13309 if(!pBlockVectorDefragCtx)
13310 {
13311 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13312 m_hAllocator,
13313 hAllocPool,
13314 &hAllocPool->m_BlockVector,
13315 m_CurrFrameIndex,
13316 m_Flags);
13317 m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
13318 }
13319 }
13320 }
13321 // This allocation belongs to default pool.
13322 else
13323 {
13324 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
13325 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
13326 if(!pBlockVectorDefragCtx)
13327 {
13328 pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
13329 m_hAllocator,
13330 VMA_NULL, // hCustomPool
13331 m_hAllocator->m_pBlockVectors[memTypeIndex],
13332 m_CurrFrameIndex,
13333 m_Flags);
13334 m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
13335 }
13336 }
13337
13338 if(pBlockVectorDefragCtx)
13339 {
13340 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
13341 &pAllocationsChanged[allocIndex] : VMA_NULL;
13342 pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
13343 }
13344 }
13345 }
13346}
13347
13348VkResult VmaDefragmentationContext_T::Defragment(
13349 VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
13350 VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
13351 VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
13352{
13353 if(pStats)
13354 {
13355 memset(pStats, 0, sizeof(VmaDefragmentationStats));
13356 }
13357
13358 if(commandBuffer == VK_NULL_HANDLE)
13359 {
13360 maxGpuBytesToMove = 0;
13361 maxGpuAllocationsToMove = 0;
13362 }
13363
13364 VkResult res = VK_SUCCESS;
13365
13366 // Process default pools.
13367 for(uint32_t memTypeIndex = 0;
13368 memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
13369 ++memTypeIndex)
13370 {
13371 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
13372 if(pBlockVectorCtx)
13373 {
13374 VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
13375 pBlockVectorCtx->GetBlockVector()->Defragment(
13376 pBlockVectorCtx,
13377 pStats,
13378 maxCpuBytesToMove, maxCpuAllocationsToMove,
13379 maxGpuBytesToMove, maxGpuAllocationsToMove,
13380 commandBuffer);
13381 if(pBlockVectorCtx->res != VK_SUCCESS)
13382 {
13383 res = pBlockVectorCtx->res;
13384 }
13385 }
13386 }
13387
13388 // Process custom pools.
13389 for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
13390 customCtxIndex < customCtxCount && res >= VK_SUCCESS;
13391 ++customCtxIndex)
13392 {
13393 VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
13394 VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
13395 pBlockVectorCtx->GetBlockVector()->Defragment(
13396 pBlockVectorCtx,
13397 pStats,
13398 maxCpuBytesToMove, maxCpuAllocationsToMove,
13399 maxGpuBytesToMove, maxGpuAllocationsToMove,
13400 commandBuffer);
13401 if(pBlockVectorCtx->res != VK_SUCCESS)
13402 {
13403 res = pBlockVectorCtx->res;
13404 }
13405 }
13406
13407 return res;
13408}
13409
13410////////////////////////////////////////////////////////////////////////////////
13411// VmaRecorder
13412
13413#if VMA_RECORDING_ENABLED
13414
13415VmaRecorder::VmaRecorder() :
13416 m_UseMutex(true),
13417 m_Flags(0),
13418 m_File(VMA_NULL),
13419 m_Freq(INT64_MAX),
13420 m_StartCounter(INT64_MAX)
13421{
13422}
13423
13424VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
13425{
13426 m_UseMutex = useMutex;
13427 m_Flags = settings.flags;
13428
13429 QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
13430 QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
13431
13432 // Open file for writing.
13433 errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
13434 if(err != 0)
13435 {
13436 return VK_ERROR_INITIALIZATION_FAILED;
13437 }
13438
13439 // Write header.
13440 fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
13441 fprintf(m_File, "%s\n", "1,5");
13442
13443 return VK_SUCCESS;
13444}
13445
13446VmaRecorder::~VmaRecorder()
13447{
13448 if(m_File != VMA_NULL)
13449 {
13450 fclose(m_File);
13451 }
13452}
13453
13454void VmaRecorder::RecordCreateAllocator(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,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
13461 Flush();
13462}
13463
13464void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
13465{
13466 CallParams callParams;
13467 GetBasicParams(callParams);
13468
13469 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13470 fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
13471 Flush();
13472}
13473
13474void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
13475{
13476 CallParams callParams;
13477 GetBasicParams(callParams);
13478
13479 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13480 fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
13481 createInfo.memoryTypeIndex,
13482 createInfo.flags,
13483 createInfo.blockSize,
13484 (uint64_t)createInfo.minBlockCount,
13485 (uint64_t)createInfo.maxBlockCount,
13486 createInfo.frameInUseCount,
13487 pool);
13488 Flush();
13489}
13490
13491void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
13492{
13493 CallParams callParams;
13494 GetBasicParams(callParams);
13495
13496 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13497 fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
13498 pool);
13499 Flush();
13500}
13501
13502void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
13503 const VkMemoryRequirements& vkMemReq,
13504 const VmaAllocationCreateInfo& createInfo,
13505 VmaAllocation allocation)
13506{
13507 CallParams callParams;
13508 GetBasicParams(callParams);
13509
13510 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13511 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13512 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13513 vkMemReq.size,
13514 vkMemReq.alignment,
13515 vkMemReq.memoryTypeBits,
13516 createInfo.flags,
13517 createInfo.usage,
13518 createInfo.requiredFlags,
13519 createInfo.preferredFlags,
13520 createInfo.memoryTypeBits,
13521 createInfo.pool,
13522 allocation,
13523 userDataStr.GetString());
13524 Flush();
13525}
13526
13527void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
13528 const VkMemoryRequirements& vkMemReq,
13529 const VmaAllocationCreateInfo& createInfo,
13530 uint64_t allocationCount,
13531 const VmaAllocation* pAllocations)
13532{
13533 CallParams callParams;
13534 GetBasicParams(callParams);
13535
13536 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13537 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13538 fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
13539 vkMemReq.size,
13540 vkMemReq.alignment,
13541 vkMemReq.memoryTypeBits,
13542 createInfo.flags,
13543 createInfo.usage,
13544 createInfo.requiredFlags,
13545 createInfo.preferredFlags,
13546 createInfo.memoryTypeBits,
13547 createInfo.pool);
13548 PrintPointerList(allocationCount, pAllocations);
13549 fprintf(m_File, ",%s\n", userDataStr.GetString());
13550 Flush();
13551}
13552
13553void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
13554 const VkMemoryRequirements& vkMemReq,
13555 bool requiresDedicatedAllocation,
13556 bool prefersDedicatedAllocation,
13557 const VmaAllocationCreateInfo& createInfo,
13558 VmaAllocation allocation)
13559{
13560 CallParams callParams;
13561 GetBasicParams(callParams);
13562
13563 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13564 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13565 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,
13566 vkMemReq.size,
13567 vkMemReq.alignment,
13568 vkMemReq.memoryTypeBits,
13569 requiresDedicatedAllocation ? 1 : 0,
13570 prefersDedicatedAllocation ? 1 : 0,
13571 createInfo.flags,
13572 createInfo.usage,
13573 createInfo.requiredFlags,
13574 createInfo.preferredFlags,
13575 createInfo.memoryTypeBits,
13576 createInfo.pool,
13577 allocation,
13578 userDataStr.GetString());
13579 Flush();
13580}
13581
13582void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
13583 const VkMemoryRequirements& vkMemReq,
13584 bool requiresDedicatedAllocation,
13585 bool prefersDedicatedAllocation,
13586 const VmaAllocationCreateInfo& createInfo,
13587 VmaAllocation allocation)
13588{
13589 CallParams callParams;
13590 GetBasicParams(callParams);
13591
13592 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13593 UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
13594 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,
13595 vkMemReq.size,
13596 vkMemReq.alignment,
13597 vkMemReq.memoryTypeBits,
13598 requiresDedicatedAllocation ? 1 : 0,
13599 prefersDedicatedAllocation ? 1 : 0,
13600 createInfo.flags,
13601 createInfo.usage,
13602 createInfo.requiredFlags,
13603 createInfo.preferredFlags,
13604 createInfo.memoryTypeBits,
13605 createInfo.pool,
13606 allocation,
13607 userDataStr.GetString());
13608 Flush();
13609}
13610
13611void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
13612 VmaAllocation allocation)
13613{
13614 CallParams callParams;
13615 GetBasicParams(callParams);
13616
13617 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13618 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13619 allocation);
13620 Flush();
13621}
13622
13623void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
13624 uint64_t allocationCount,
13625 const VmaAllocation* pAllocations)
13626{
13627 CallParams callParams;
13628 GetBasicParams(callParams);
13629
13630 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13631 fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
13632 PrintPointerList(allocationCount, pAllocations);
13633 fprintf(m_File, "\n");
13634 Flush();
13635}
13636
13637void VmaRecorder::RecordResizeAllocation(
13638 uint32_t frameIndex,
13639 VmaAllocation allocation,
13640 VkDeviceSize newSize)
13641{
13642 CallParams callParams;
13643 GetBasicParams(callParams);
13644
13645 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13646 fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
13647 allocation, newSize);
13648 Flush();
13649}
13650
13651void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
13652 VmaAllocation allocation,
13653 const void* pUserData)
13654{
13655 CallParams callParams;
13656 GetBasicParams(callParams);
13657
13658 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13659 UserDataString userDataStr(
13660 allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
13661 pUserData);
13662 fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
13663 allocation,
13664 userDataStr.GetString());
13665 Flush();
13666}
13667
13668void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
13669 VmaAllocation allocation)
13670{
13671 CallParams callParams;
13672 GetBasicParams(callParams);
13673
13674 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13675 fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13676 allocation);
13677 Flush();
13678}
13679
13680void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
13681 VmaAllocation allocation)
13682{
13683 CallParams callParams;
13684 GetBasicParams(callParams);
13685
13686 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13687 fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13688 allocation);
13689 Flush();
13690}
13691
13692void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
13693 VmaAllocation allocation)
13694{
13695 CallParams callParams;
13696 GetBasicParams(callParams);
13697
13698 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13699 fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
13700 allocation);
13701 Flush();
13702}
13703
13704void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
13705 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13706{
13707 CallParams callParams;
13708 GetBasicParams(callParams);
13709
13710 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13711 fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13712 allocation,
13713 offset,
13714 size);
13715 Flush();
13716}
13717
13718void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
13719 VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
13720{
13721 CallParams callParams;
13722 GetBasicParams(callParams);
13723
13724 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13725 fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
13726 allocation,
13727 offset,
13728 size);
13729 Flush();
13730}
13731
13732void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
13733 const VkBufferCreateInfo& bufCreateInfo,
13734 const VmaAllocationCreateInfo& allocCreateInfo,
13735 VmaAllocation allocation)
13736{
13737 CallParams callParams;
13738 GetBasicParams(callParams);
13739
13740 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13741 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13742 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,
13743 bufCreateInfo.flags,
13744 bufCreateInfo.size,
13745 bufCreateInfo.usage,
13746 bufCreateInfo.sharingMode,
13747 allocCreateInfo.flags,
13748 allocCreateInfo.usage,
13749 allocCreateInfo.requiredFlags,
13750 allocCreateInfo.preferredFlags,
13751 allocCreateInfo.memoryTypeBits,
13752 allocCreateInfo.pool,
13753 allocation,
13754 userDataStr.GetString());
13755 Flush();
13756}
13757
13758void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
13759 const VkImageCreateInfo& imageCreateInfo,
13760 const VmaAllocationCreateInfo& allocCreateInfo,
13761 VmaAllocation allocation)
13762{
13763 CallParams callParams;
13764 GetBasicParams(callParams);
13765
13766 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13767 UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
13768 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,
13769 imageCreateInfo.flags,
13770 imageCreateInfo.imageType,
13771 imageCreateInfo.format,
13772 imageCreateInfo.extent.width,
13773 imageCreateInfo.extent.height,
13774 imageCreateInfo.extent.depth,
13775 imageCreateInfo.mipLevels,
13776 imageCreateInfo.arrayLayers,
13777 imageCreateInfo.samples,
13778 imageCreateInfo.tiling,
13779 imageCreateInfo.usage,
13780 imageCreateInfo.sharingMode,
13781 imageCreateInfo.initialLayout,
13782 allocCreateInfo.flags,
13783 allocCreateInfo.usage,
13784 allocCreateInfo.requiredFlags,
13785 allocCreateInfo.preferredFlags,
13786 allocCreateInfo.memoryTypeBits,
13787 allocCreateInfo.pool,
13788 allocation,
13789 userDataStr.GetString());
13790 Flush();
13791}
13792
13793void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
13794 VmaAllocation allocation)
13795{
13796 CallParams callParams;
13797 GetBasicParams(callParams);
13798
13799 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13800 fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
13801 allocation);
13802 Flush();
13803}
13804
13805void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
13806 VmaAllocation allocation)
13807{
13808 CallParams callParams;
13809 GetBasicParams(callParams);
13810
13811 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13812 fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
13813 allocation);
13814 Flush();
13815}
13816
13817void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
13818 VmaAllocation allocation)
13819{
13820 CallParams callParams;
13821 GetBasicParams(callParams);
13822
13823 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13824 fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
13825 allocation);
13826 Flush();
13827}
13828
13829void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
13830 VmaAllocation allocation)
13831{
13832 CallParams callParams;
13833 GetBasicParams(callParams);
13834
13835 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13836 fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
13837 allocation);
13838 Flush();
13839}
13840
13841void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
13842 VmaPool pool)
13843{
13844 CallParams callParams;
13845 GetBasicParams(callParams);
13846
13847 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13848 fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
13849 pool);
13850 Flush();
13851}
13852
13853void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
13854 const VmaDefragmentationInfo2& info,
13855 VmaDefragmentationContext ctx)
13856{
13857 CallParams callParams;
13858 GetBasicParams(callParams);
13859
13860 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13861 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
13862 info.flags);
13863 PrintPointerList(info.allocationCount, info.pAllocations);
13864 fprintf(m_File, ",");
13865 PrintPointerList(info.poolCount, info.pPools);
13866 fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
13867 info.maxCpuBytesToMove,
13868 info.maxCpuAllocationsToMove,
13869 info.maxGpuBytesToMove,
13870 info.maxGpuAllocationsToMove,
13871 info.commandBuffer,
13872 ctx);
13873 Flush();
13874}
13875
13876void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
13877 VmaDefragmentationContext ctx)
13878{
13879 CallParams callParams;
13880 GetBasicParams(callParams);
13881
13882 VmaMutexLock lock(m_FileMutex, m_UseMutex);
13883 fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
13884 ctx);
13885 Flush();
13886}
13887
13888VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
13889{
13890 if(pUserData != VMA_NULL)
13891 {
13892 if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
13893 {
13894 m_Str = (const char*)pUserData;
13895 }
13896 else
13897 {
13898 sprintf_s(m_PtrStr, "%p", pUserData);
13899 m_Str = m_PtrStr;
13900 }
13901 }
13902 else
13903 {
13904 m_Str = "";
13905 }
13906}
13907
13908void VmaRecorder::WriteConfiguration(
13909 const VkPhysicalDeviceProperties& devProps,
13910 const VkPhysicalDeviceMemoryProperties& memProps,
13911 bool dedicatedAllocationExtensionEnabled)
13912{
13913 fprintf(m_File, "Config,Begin\n");
13914
13915 fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
13916 fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
13917 fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
13918 fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
13919 fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
13920 fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
13921
13922 fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
13923 fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
13924 fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
13925
13926 fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
13927 for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
13928 {
13929 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
13930 fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
13931 }
13932 fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
13933 for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
13934 {
13935 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
13936 fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
13937 }
13938
13939 fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
13940
13941 fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
13942 fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
13943 fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
13944 fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
13945 fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
13946 fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
13947 fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
13948 fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
13949 fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
13950
13951 fprintf(m_File, "Config,End\n");
13952}
13953
13954void VmaRecorder::GetBasicParams(CallParams& outParams)
13955{
13956 outParams.threadId = GetCurrentThreadId();
13957
13958 LARGE_INTEGER counter;
13959 QueryPerformanceCounter(&counter);
13960 outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
13961}
13962
13963void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
13964{
13965 if(count)
13966 {
13967 fprintf(m_File, "%p", pItems[0]);
13968 for(uint64_t i = 1; i < count; ++i)
13969 {
13970 fprintf(m_File, " %p", pItems[i]);
13971 }
13972 }
13973}
13974
13975void VmaRecorder::Flush()
13976{
13977 if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
13978 {
13979 fflush(m_File);
13980 }
13981}
13982
13983#endif // #if VMA_RECORDING_ENABLED
13984
13985////////////////////////////////////////////////////////////////////////////////
13986// VmaAllocator_T
13987
13988VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
13989 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
13990 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
13991 m_hDevice(pCreateInfo->device),
13992 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
13993 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
13994 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
13995 m_PreferredLargeHeapBlockSize(0),
13996 m_PhysicalDevice(pCreateInfo->physicalDevice),
13997 m_CurrentFrameIndex(0),
13998 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
13999 m_NextPoolId(0)
14000#if VMA_RECORDING_ENABLED
14001 ,m_pRecorder(VMA_NULL)
14002#endif
14003{
14004 if(VMA_DEBUG_DETECT_CORRUPTION)
14005 {
14006 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14007 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14008 }
14009
14010 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
14011
14012#if !(VMA_DEDICATED_ALLOCATION)
14013 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14014 {
14015 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14016 }
14017#endif
14018
14019 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14020 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14021 memset(&m_MemProps, 0, sizeof(m_MemProps));
14022
14023 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14024 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
14025
14026 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14027 {
14028 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
14029 }
14030
14031 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14032 {
14033 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14034 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14035 }
14036
14037 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14038
14039 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14040 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14041
14042 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
14043 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14044 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14045 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14046
14047 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14048 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14049
14050 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14051 {
14052 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14053 {
14054 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14055 if(limit != VK_WHOLE_SIZE)
14056 {
14057 m_HeapSizeLimit[heapIndex] = limit;
14058 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14059 {
14060 m_MemProps.memoryHeaps[heapIndex].size = limit;
14061 }
14062 }
14063 }
14064 }
14065
14066 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14067 {
14068 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14069
14070 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14071 this,
14072 memTypeIndex,
14073 preferredBlockSize,
14074 0,
14075 SIZE_MAX,
14076 GetBufferImageGranularity(),
14077 pCreateInfo->frameInUseCount,
14078 false, // isCustomPool
14079 false, // explicitBlockSize
14080 false); // linearAlgorithm
14081 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14082 // becase minBlockCount is 0.
14083 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
14084
14085 }
14086}
14087
14088VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14089{
14090 VkResult res = VK_SUCCESS;
14091
14092 if(pCreateInfo->pRecordSettings != VMA_NULL &&
14093 !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
14094 {
14095#if VMA_RECORDING_ENABLED
14096 m_pRecorder = vma_new(this, VmaRecorder)();
14097 res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
14098 if(res != VK_SUCCESS)
14099 {
14100 return res;
14101 }
14102 m_pRecorder->WriteConfiguration(
14103 m_PhysicalDeviceProperties,
14104 m_MemProps,
14105 m_UseKhrDedicatedAllocation);
14106 m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
14107#else
14108 VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
14109 return VK_ERROR_FEATURE_NOT_PRESENT;
14110#endif
14111 }
14112
14113 return res;
14114}
14115
14116VmaAllocator_T::~VmaAllocator_T()
14117{
14118#if VMA_RECORDING_ENABLED
14119 if(m_pRecorder != VMA_NULL)
14120 {
14121 m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
14122 vma_delete(this, m_pRecorder);
14123 }
14124#endif
14125
14126 VMA_ASSERT(m_Pools.empty());
14127
14128 for(size_t i = GetMemoryTypeCount(); i--; )
14129 {
14130 vma_delete(this, m_pDedicatedAllocations[i]);
14131 vma_delete(this, m_pBlockVectors[i]);
14132 }
14133}
14134
14135void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14136{
14137#if VMA_STATIC_VULKAN_FUNCTIONS == 1
14138 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
14139 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
14140 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
14141 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
14142 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
14143 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
14144 m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
14145 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
14146 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
14147 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
14148 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
14149 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
14150 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
14151 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
14152 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
14153 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
14154 m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
14155#if VMA_DEDICATED_ALLOCATION
14156 if(m_UseKhrDedicatedAllocation)
14157 {
14158 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
14159 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
14160 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
14161 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
14162 }
14163#endif // #if VMA_DEDICATED_ALLOCATION
14164#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14165
14166#define VMA_COPY_IF_NOT_NULL(funcName) \
14167 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14168
14169 if(pVulkanFunctions != VMA_NULL)
14170 {
14171 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14172 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14173 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14174 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14175 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14176 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14177 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14178 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14179 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14180 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14181 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14182 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14183 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14184 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14185 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14186 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14187 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14188#if VMA_DEDICATED_ALLOCATION
14189 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14190 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14191#endif
14192 }
14193
14194#undef VMA_COPY_IF_NOT_NULL
14195
14196 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
14197 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
14198 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14199 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14200 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14201 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14202 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14203 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14204 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14205 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14206 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14207 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14208 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14209 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14210 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14211 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14212 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14213 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14214 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14215#if VMA_DEDICATED_ALLOCATION
14216 if(m_UseKhrDedicatedAllocation)
14217 {
14218 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14219 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14220 }
14221#endif
14222}
14223
14224VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14225{
14226 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14227 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14228 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14229 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
14230}
14231
14232VkResult VmaAllocator_T::AllocateMemoryOfType(
14233 VkDeviceSize size,
14234 VkDeviceSize alignment,
14235 bool dedicatedAllocation,
14236 VkBuffer dedicatedBuffer,
14237 VkImage dedicatedImage,
14238 const VmaAllocationCreateInfo& createInfo,
14239 uint32_t memTypeIndex,
14240 VmaSuballocationType suballocType,
14241 size_t allocationCount,
14242 VmaAllocation* pAllocations)
14243{
14244 VMA_ASSERT(pAllocations != VMA_NULL);
14245 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
14246
14247 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14248
14249 // If memory type is not HOST_VISIBLE, disable MAPPED.
14250 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14251 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14252 {
14253 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14254 }
14255
14256 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
14257 VMA_ASSERT(blockVector);
14258
14259 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
14260 bool preferDedicatedMemory =
14261 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
14262 dedicatedAllocation ||
14263 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14264 size > preferredBlockSize / 2;
14265
14266 if(preferDedicatedMemory &&
14267 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14268 finalCreateInfo.pool == VK_NULL_HANDLE)
14269 {
14270 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14271 }
14272
14273 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14274 {
14275 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14276 {
14277 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14278 }
14279 else
14280 {
14281 return AllocateDedicatedMemory(
14282 size,
14283 suballocType,
14284 memTypeIndex,
14285 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14286 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14287 finalCreateInfo.pUserData,
14288 dedicatedBuffer,
14289 dedicatedImage,
14290 allocationCount,
14291 pAllocations);
14292 }
14293 }
14294 else
14295 {
14296 VkResult res = blockVector->Allocate(
14297 VK_NULL_HANDLE, // hCurrentPool
14298 m_CurrentFrameIndex.load(),
14299 size,
14300 alignment,
14301 finalCreateInfo,
14302 suballocType,
14303 allocationCount,
14304 pAllocations);
14305 if(res == VK_SUCCESS)
14306 {
14307 return res;
14308 }
14309
14310 // 5. Try dedicated memory.
14311 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14312 {
14313 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14314 }
14315 else
14316 {
14317 res = AllocateDedicatedMemory(
14318 size,
14319 suballocType,
14320 memTypeIndex,
14321 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14322 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14323 finalCreateInfo.pUserData,
14324 dedicatedBuffer,
14325 dedicatedImage,
14326 allocationCount,
14327 pAllocations);
14328 if(res == VK_SUCCESS)
14329 {
14330 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14331 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14332 return VK_SUCCESS;
14333 }
14334 else
14335 {
14336 // Everything failed: Return error code.
14337 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14338 return res;
14339 }
14340 }
14341 }
14342}
14343
14344VkResult VmaAllocator_T::AllocateDedicatedMemory(
14345 VkDeviceSize size,
14346 VmaSuballocationType suballocType,
14347 uint32_t memTypeIndex,
14348 bool map,
14349 bool isUserDataString,
14350 void* pUserData,
14351 VkBuffer dedicatedBuffer,
14352 VkImage dedicatedImage,
14353 size_t allocationCount,
14354 VmaAllocation* pAllocations)
14355{
14356 VMA_ASSERT(allocationCount > 0 && pAllocations);
14357
14358 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14359 allocInfo.memoryTypeIndex = memTypeIndex;
14360 allocInfo.allocationSize = size;
14361
14362#if VMA_DEDICATED_ALLOCATION
14363 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14364 if(m_UseKhrDedicatedAllocation)
14365 {
14366 if(dedicatedBuffer != VK_NULL_HANDLE)
14367 {
14368 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14369 dedicatedAllocInfo.buffer = dedicatedBuffer;
14370 allocInfo.pNext = &dedicatedAllocInfo;
14371 }
14372 else if(dedicatedImage != VK_NULL_HANDLE)
14373 {
14374 dedicatedAllocInfo.image = dedicatedImage;
14375 allocInfo.pNext = &dedicatedAllocInfo;
14376 }
14377 }
14378#endif // #if VMA_DEDICATED_ALLOCATION
14379
14380 size_t allocIndex;
Tony-LunarG390319b2019-03-18 15:54:16 -060014381 VkResult res = VK_SUCCESS;
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060014382 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14383 {
14384 res = AllocateDedicatedMemoryPage(
14385 size,
14386 suballocType,
14387 memTypeIndex,
14388 allocInfo,
14389 map,
14390 isUserDataString,
14391 pUserData,
14392 pAllocations + allocIndex);
14393 if(res != VK_SUCCESS)
14394 {
14395 break;
14396 }
14397 }
14398
14399 if(res == VK_SUCCESS)
14400 {
14401 // Register them in m_pDedicatedAllocations.
14402 {
14403 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14404 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
14405 VMA_ASSERT(pDedicatedAllocations);
14406 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14407 {
14408 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
14409 }
14410 }
14411
14412 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14413 }
14414 else
14415 {
14416 // Free all already created allocations.
14417 while(allocIndex--)
14418 {
14419 VmaAllocation currAlloc = pAllocations[allocIndex];
14420 VkDeviceMemory hMemory = currAlloc->GetMemory();
14421
14422 /*
14423 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14424 before vkFreeMemory.
14425
14426 if(currAlloc->GetMappedData() != VMA_NULL)
14427 {
14428 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14429 }
14430 */
14431
14432 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14433
14434 currAlloc->SetUserData(this, VMA_NULL);
14435 vma_delete(this, currAlloc);
14436 }
14437
14438 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14439 }
14440
14441 return res;
14442}
14443
14444VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14445 VkDeviceSize size,
14446 VmaSuballocationType suballocType,
14447 uint32_t memTypeIndex,
14448 const VkMemoryAllocateInfo& allocInfo,
14449 bool map,
14450 bool isUserDataString,
14451 void* pUserData,
14452 VmaAllocation* pAllocation)
14453{
14454 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14455 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14456 if(res < 0)
14457 {
14458 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14459 return res;
14460 }
14461
14462 void* pMappedData = VMA_NULL;
14463 if(map)
14464 {
14465 res = (*m_VulkanFunctions.vkMapMemory)(
14466 m_hDevice,
14467 hMemory,
14468 0,
14469 VK_WHOLE_SIZE,
14470 0,
14471 &pMappedData);
14472 if(res < 0)
14473 {
14474 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14475 FreeVulkanMemory(memTypeIndex, size, hMemory);
14476 return res;
14477 }
14478 }
14479
14480 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
14481 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
14482 (*pAllocation)->SetUserData(this, pUserData);
14483 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14484 {
14485 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14486 }
14487
14488 return VK_SUCCESS;
14489}
14490
14491void VmaAllocator_T::GetBufferMemoryRequirements(
14492 VkBuffer hBuffer,
14493 VkMemoryRequirements& memReq,
14494 bool& requiresDedicatedAllocation,
14495 bool& prefersDedicatedAllocation) const
14496{
14497#if VMA_DEDICATED_ALLOCATION
14498 if(m_UseKhrDedicatedAllocation)
14499 {
14500 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14501 memReqInfo.buffer = hBuffer;
14502
14503 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14504
14505 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14506 memReq2.pNext = &memDedicatedReq;
14507
14508 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14509
14510 memReq = memReq2.memoryRequirements;
14511 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14512 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14513 }
14514 else
14515#endif // #if VMA_DEDICATED_ALLOCATION
14516 {
14517 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14518 requiresDedicatedAllocation = false;
14519 prefersDedicatedAllocation = false;
14520 }
14521}
14522
14523void VmaAllocator_T::GetImageMemoryRequirements(
14524 VkImage hImage,
14525 VkMemoryRequirements& memReq,
14526 bool& requiresDedicatedAllocation,
14527 bool& prefersDedicatedAllocation) const
14528{
14529#if VMA_DEDICATED_ALLOCATION
14530 if(m_UseKhrDedicatedAllocation)
14531 {
14532 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14533 memReqInfo.image = hImage;
14534
14535 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14536
14537 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14538 memReq2.pNext = &memDedicatedReq;
14539
14540 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14541
14542 memReq = memReq2.memoryRequirements;
14543 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14544 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14545 }
14546 else
14547#endif // #if VMA_DEDICATED_ALLOCATION
14548 {
14549 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14550 requiresDedicatedAllocation = false;
14551 prefersDedicatedAllocation = false;
14552 }
14553}
14554
14555VkResult VmaAllocator_T::AllocateMemory(
14556 const VkMemoryRequirements& vkMemReq,
14557 bool requiresDedicatedAllocation,
14558 bool prefersDedicatedAllocation,
14559 VkBuffer dedicatedBuffer,
14560 VkImage dedicatedImage,
14561 const VmaAllocationCreateInfo& createInfo,
14562 VmaSuballocationType suballocType,
14563 size_t allocationCount,
14564 VmaAllocation* pAllocations)
14565{
14566 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14567
14568 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
14569
14570 if(vkMemReq.size == 0)
14571 {
14572 return VK_ERROR_VALIDATION_FAILED_EXT;
14573 }
14574 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14575 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14576 {
14577 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14578 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14579 }
14580 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14581 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
14582 {
14583 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
14584 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14585 }
14586 if(requiresDedicatedAllocation)
14587 {
14588 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14589 {
14590 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
14591 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14592 }
14593 if(createInfo.pool != VK_NULL_HANDLE)
14594 {
14595 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
14596 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14597 }
14598 }
14599 if((createInfo.pool != VK_NULL_HANDLE) &&
14600 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
14601 {
14602 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
14603 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14604 }
14605
14606 if(createInfo.pool != VK_NULL_HANDLE)
14607 {
14608 const VkDeviceSize alignmentForPool = VMA_MAX(
14609 vkMemReq.alignment,
14610 GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
14611 return createInfo.pool->m_BlockVector.Allocate(
14612 createInfo.pool,
14613 m_CurrentFrameIndex.load(),
14614 vkMemReq.size,
14615 alignmentForPool,
14616 createInfo,
14617 suballocType,
14618 allocationCount,
14619 pAllocations);
14620 }
14621 else
14622 {
14623 // Bit mask of memory Vulkan types acceptable for this allocation.
14624 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
14625 uint32_t memTypeIndex = UINT32_MAX;
14626 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14627 if(res == VK_SUCCESS)
14628 {
14629 VkDeviceSize alignmentForMemType = VMA_MAX(
14630 vkMemReq.alignment,
14631 GetMemoryTypeMinAlignment(memTypeIndex));
14632
14633 res = AllocateMemoryOfType(
14634 vkMemReq.size,
14635 alignmentForMemType,
14636 requiresDedicatedAllocation || prefersDedicatedAllocation,
14637 dedicatedBuffer,
14638 dedicatedImage,
14639 createInfo,
14640 memTypeIndex,
14641 suballocType,
14642 allocationCount,
14643 pAllocations);
14644 // Succeeded on first try.
14645 if(res == VK_SUCCESS)
14646 {
14647 return res;
14648 }
14649 // Allocation from this memory type failed. Try other compatible memory types.
14650 else
14651 {
14652 for(;;)
14653 {
14654 // Remove old memTypeIndex from list of possibilities.
14655 memoryTypeBits &= ~(1u << memTypeIndex);
14656 // Find alternative memTypeIndex.
14657 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
14658 if(res == VK_SUCCESS)
14659 {
14660 alignmentForMemType = VMA_MAX(
14661 vkMemReq.alignment,
14662 GetMemoryTypeMinAlignment(memTypeIndex));
14663
14664 res = AllocateMemoryOfType(
14665 vkMemReq.size,
14666 alignmentForMemType,
14667 requiresDedicatedAllocation || prefersDedicatedAllocation,
14668 dedicatedBuffer,
14669 dedicatedImage,
14670 createInfo,
14671 memTypeIndex,
14672 suballocType,
14673 allocationCount,
14674 pAllocations);
14675 // Allocation from this alternative memory type succeeded.
14676 if(res == VK_SUCCESS)
14677 {
14678 return res;
14679 }
14680 // else: Allocation from this memory type failed. Try next one - next loop iteration.
14681 }
14682 // No other matching memory type index could be found.
14683 else
14684 {
14685 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
14686 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14687 }
14688 }
14689 }
14690 }
14691 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
14692 else
14693 return res;
14694 }
14695}
14696
14697void VmaAllocator_T::FreeMemory(
14698 size_t allocationCount,
14699 const VmaAllocation* pAllocations)
14700{
14701 VMA_ASSERT(pAllocations);
14702
14703 for(size_t allocIndex = allocationCount; allocIndex--; )
14704 {
14705 VmaAllocation allocation = pAllocations[allocIndex];
14706
14707 if(allocation != VK_NULL_HANDLE)
14708 {
14709 if(TouchAllocation(allocation))
14710 {
14711 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14712 {
14713 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
14714 }
14715
14716 switch(allocation->GetType())
14717 {
14718 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14719 {
14720 VmaBlockVector* pBlockVector = VMA_NULL;
14721 VmaPool hPool = allocation->GetPool();
14722 if(hPool != VK_NULL_HANDLE)
14723 {
14724 pBlockVector = &hPool->m_BlockVector;
14725 }
14726 else
14727 {
14728 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
14729 pBlockVector = m_pBlockVectors[memTypeIndex];
14730 }
14731 pBlockVector->Free(allocation);
14732 }
14733 break;
14734 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14735 FreeDedicatedMemory(allocation);
14736 break;
14737 default:
14738 VMA_ASSERT(0);
14739 }
14740 }
14741
14742 allocation->SetUserData(this, VMA_NULL);
14743 vma_delete(this, allocation);
14744 }
14745 }
14746}
14747
14748VkResult VmaAllocator_T::ResizeAllocation(
14749 const VmaAllocation alloc,
14750 VkDeviceSize newSize)
14751{
14752 if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
14753 {
14754 return VK_ERROR_VALIDATION_FAILED_EXT;
14755 }
14756 if(newSize == alloc->GetSize())
14757 {
14758 return VK_SUCCESS;
14759 }
14760
14761 switch(alloc->GetType())
14762 {
14763 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
14764 return VK_ERROR_FEATURE_NOT_PRESENT;
14765 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
14766 if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
14767 {
14768 alloc->ChangeSize(newSize);
14769 VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
14770 return VK_SUCCESS;
14771 }
14772 else
14773 {
14774 return VK_ERROR_OUT_OF_POOL_MEMORY;
14775 }
14776 default:
14777 VMA_ASSERT(0);
14778 return VK_ERROR_VALIDATION_FAILED_EXT;
14779 }
14780}
14781
14782void VmaAllocator_T::CalculateStats(VmaStats* pStats)
14783{
14784 // Initialize.
14785 InitStatInfo(pStats->total);
14786 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
14787 InitStatInfo(pStats->memoryType[i]);
14788 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
14789 InitStatInfo(pStats->memoryHeap[i]);
14790
14791 // Process default pools.
14792 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14793 {
14794 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
14795 VMA_ASSERT(pBlockVector);
14796 pBlockVector->AddStats(pStats);
14797 }
14798
14799 // Process custom pools.
14800 {
14801 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
14802 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
14803 {
14804 m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
14805 }
14806 }
14807
14808 // Process dedicated allocations.
14809 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14810 {
14811 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14812 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
14813 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
14814 VMA_ASSERT(pDedicatedAllocVector);
14815 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
14816 {
14817 VmaStatInfo allocationStatInfo;
14818 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
14819 VmaAddStatInfo(pStats->total, allocationStatInfo);
14820 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
14821 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
14822 }
14823 }
14824
14825 // Postprocess.
14826 VmaPostprocessCalcStatInfo(pStats->total);
14827 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
14828 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
14829 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
14830 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
14831}
14832
14833static const uint32_t VMA_VENDOR_ID_AMD = 4098;
14834
14835VkResult VmaAllocator_T::DefragmentationBegin(
14836 const VmaDefragmentationInfo2& info,
14837 VmaDefragmentationStats* pStats,
14838 VmaDefragmentationContext* pContext)
14839{
14840 if(info.pAllocationsChanged != VMA_NULL)
14841 {
14842 memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
14843 }
14844
14845 *pContext = vma_new(this, VmaDefragmentationContext_T)(
14846 this, m_CurrentFrameIndex.load(), info.flags, pStats);
14847
14848 (*pContext)->AddPools(info.poolCount, info.pPools);
14849 (*pContext)->AddAllocations(
14850 info.allocationCount, info.pAllocations, info.pAllocationsChanged);
14851
14852 VkResult res = (*pContext)->Defragment(
14853 info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
14854 info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
14855 info.commandBuffer, pStats);
14856
14857 if(res != VK_NOT_READY)
14858 {
14859 vma_delete(this, *pContext);
14860 *pContext = VMA_NULL;
14861 }
14862
14863 return res;
14864}
14865
14866VkResult VmaAllocator_T::DefragmentationEnd(
14867 VmaDefragmentationContext context)
14868{
14869 vma_delete(this, context);
14870 return VK_SUCCESS;
14871}
14872
14873void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
14874{
14875 if(hAllocation->CanBecomeLost())
14876 {
14877 /*
14878 Warning: This is a carefully designed algorithm.
14879 Do not modify unless you really know what you're doing :)
14880 */
14881 const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14882 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14883 for(;;)
14884 {
14885 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14886 {
14887 pAllocationInfo->memoryType = UINT32_MAX;
14888 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
14889 pAllocationInfo->offset = 0;
14890 pAllocationInfo->size = hAllocation->GetSize();
14891 pAllocationInfo->pMappedData = VMA_NULL;
14892 pAllocationInfo->pUserData = hAllocation->GetUserData();
14893 return;
14894 }
14895 else if(localLastUseFrameIndex == localCurrFrameIndex)
14896 {
14897 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14898 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14899 pAllocationInfo->offset = hAllocation->GetOffset();
14900 pAllocationInfo->size = hAllocation->GetSize();
14901 pAllocationInfo->pMappedData = VMA_NULL;
14902 pAllocationInfo->pUserData = hAllocation->GetUserData();
14903 return;
14904 }
14905 else // Last use time earlier than current time.
14906 {
14907 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14908 {
14909 localLastUseFrameIndex = localCurrFrameIndex;
14910 }
14911 }
14912 }
14913 }
14914 else
14915 {
14916#if VMA_STATS_STRING_ENABLED
14917 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14918 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14919 for(;;)
14920 {
14921 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14922 if(localLastUseFrameIndex == localCurrFrameIndex)
14923 {
14924 break;
14925 }
14926 else // Last use time earlier than current time.
14927 {
14928 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14929 {
14930 localLastUseFrameIndex = localCurrFrameIndex;
14931 }
14932 }
14933 }
14934#endif
14935
14936 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
14937 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
14938 pAllocationInfo->offset = hAllocation->GetOffset();
14939 pAllocationInfo->size = hAllocation->GetSize();
14940 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
14941 pAllocationInfo->pUserData = hAllocation->GetUserData();
14942 }
14943}
14944
14945bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
14946{
14947 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
14948 if(hAllocation->CanBecomeLost())
14949 {
14950 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14951 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14952 for(;;)
14953 {
14954 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
14955 {
14956 return false;
14957 }
14958 else if(localLastUseFrameIndex == localCurrFrameIndex)
14959 {
14960 return true;
14961 }
14962 else // Last use time earlier than current time.
14963 {
14964 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14965 {
14966 localLastUseFrameIndex = localCurrFrameIndex;
14967 }
14968 }
14969 }
14970 }
14971 else
14972 {
14973#if VMA_STATS_STRING_ENABLED
14974 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
14975 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
14976 for(;;)
14977 {
14978 VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
14979 if(localLastUseFrameIndex == localCurrFrameIndex)
14980 {
14981 break;
14982 }
14983 else // Last use time earlier than current time.
14984 {
14985 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
14986 {
14987 localLastUseFrameIndex = localCurrFrameIndex;
14988 }
14989 }
14990 }
14991#endif
14992
14993 return true;
14994 }
14995}
14996
14997VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
14998{
14999 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15000
15001 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15002
15003 if(newCreateInfo.maxBlockCount == 0)
15004 {
15005 newCreateInfo.maxBlockCount = SIZE_MAX;
15006 }
15007 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15008 {
15009 return VK_ERROR_INITIALIZATION_FAILED;
15010 }
15011
15012 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15013
15014 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15015
15016 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15017 if(res != VK_SUCCESS)
15018 {
15019 vma_delete(this, *pPool);
15020 *pPool = VMA_NULL;
15021 return res;
15022 }
15023
15024 // Add to m_Pools.
15025 {
15026 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15027 (*pPool)->SetId(m_NextPoolId++);
15028 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
15029 }
15030
15031 return VK_SUCCESS;
15032}
15033
15034void VmaAllocator_T::DestroyPool(VmaPool pool)
15035{
15036 // Remove from m_Pools.
15037 {
15038 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15039 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
15040 VMA_ASSERT(success && "Pool not found in Allocator.");
15041 }
15042
15043 vma_delete(this, pool);
15044}
15045
15046void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
15047{
15048 pool->m_BlockVector.GetPoolStats(pPoolStats);
15049}
15050
15051void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15052{
15053 m_CurrentFrameIndex.store(frameIndex);
15054}
15055
15056void VmaAllocator_T::MakePoolAllocationsLost(
15057 VmaPool hPool,
15058 size_t* pLostAllocationCount)
15059{
15060 hPool->m_BlockVector.MakePoolAllocationsLost(
15061 m_CurrentFrameIndex.load(),
15062 pLostAllocationCount);
15063}
15064
15065VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15066{
15067 return hPool->m_BlockVector.CheckCorruption();
15068}
15069
15070VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15071{
15072 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15073
15074 // Process default pools.
15075 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15076 {
15077 if(((1u << memTypeIndex) & memoryTypeBits) != 0)
15078 {
15079 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15080 VMA_ASSERT(pBlockVector);
15081 VkResult localRes = pBlockVector->CheckCorruption();
15082 switch(localRes)
15083 {
15084 case VK_ERROR_FEATURE_NOT_PRESENT:
15085 break;
15086 case VK_SUCCESS:
15087 finalRes = VK_SUCCESS;
15088 break;
15089 default:
15090 return localRes;
15091 }
15092 }
15093 }
15094
15095 // Process custom pools.
15096 {
15097 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15098 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
15099 {
15100 if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15101 {
15102 VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
15103 switch(localRes)
15104 {
15105 case VK_ERROR_FEATURE_NOT_PRESENT:
15106 break;
15107 case VK_SUCCESS:
15108 finalRes = VK_SUCCESS;
15109 break;
15110 default:
15111 return localRes;
15112 }
15113 }
15114 }
15115 }
15116
15117 return finalRes;
15118}
15119
15120void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
15121{
15122 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
15123 (*pAllocation)->InitLost();
15124}
15125
15126VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15127{
15128 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15129
15130 VkResult res;
15131 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15132 {
15133 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15134 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
15135 {
15136 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15137 if(res == VK_SUCCESS)
15138 {
15139 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
15140 }
15141 }
15142 else
15143 {
15144 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
15145 }
15146 }
15147 else
15148 {
15149 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15150 }
15151
15152 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15153 {
15154 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
15155 }
15156
15157 return res;
15158}
15159
15160void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15161{
15162 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15163 {
15164 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
15165 }
15166
15167 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15168
15169 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15170 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
15171 {
15172 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
15173 m_HeapSizeLimit[heapIndex] += size;
15174 }
15175}
15176
15177VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15178{
15179 if(hAllocation->CanBecomeLost())
15180 {
15181 return VK_ERROR_MEMORY_MAP_FAILED;
15182 }
15183
15184 switch(hAllocation->GetType())
15185 {
15186 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15187 {
15188 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15189 char *pBytes = VMA_NULL;
15190 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15191 if(res == VK_SUCCESS)
15192 {
15193 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15194 hAllocation->BlockAllocMap();
15195 }
15196 return res;
15197 }
15198 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15199 return hAllocation->DedicatedAllocMap(this, ppData);
15200 default:
15201 VMA_ASSERT(0);
15202 return VK_ERROR_MEMORY_MAP_FAILED;
15203 }
15204}
15205
15206void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15207{
15208 switch(hAllocation->GetType())
15209 {
15210 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15211 {
15212 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15213 hAllocation->BlockAllocUnmap();
15214 pBlock->Unmap(this, 1);
15215 }
15216 break;
15217 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15218 hAllocation->DedicatedAllocUnmap(this);
15219 break;
15220 default:
15221 VMA_ASSERT(0);
15222 }
15223}
15224
15225VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
15226{
15227 VkResult res = VK_SUCCESS;
15228 switch(hAllocation->GetType())
15229 {
15230 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15231 res = GetVulkanFunctions().vkBindBufferMemory(
15232 m_hDevice,
15233 hBuffer,
15234 hAllocation->GetMemory(),
15235 0); //memoryOffset
15236 break;
15237 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15238 {
15239 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15240 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
15241 res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
15242 break;
15243 }
15244 default:
15245 VMA_ASSERT(0);
15246 }
15247 return res;
15248}
15249
15250VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
15251{
15252 VkResult res = VK_SUCCESS;
15253 switch(hAllocation->GetType())
15254 {
15255 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15256 res = GetVulkanFunctions().vkBindImageMemory(
15257 m_hDevice,
15258 hImage,
15259 hAllocation->GetMemory(),
15260 0); //memoryOffset
15261 break;
15262 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15263 {
15264 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15265 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
15266 res = pBlock->BindImageMemory(this, hAllocation, hImage);
15267 break;
15268 }
15269 default:
15270 VMA_ASSERT(0);
15271 }
15272 return res;
15273}
15274
15275void VmaAllocator_T::FlushOrInvalidateAllocation(
15276 VmaAllocation hAllocation,
15277 VkDeviceSize offset, VkDeviceSize size,
15278 VMA_CACHE_OPERATION op)
15279{
15280 const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
15281 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15282 {
15283 const VkDeviceSize allocationSize = hAllocation->GetSize();
15284 VMA_ASSERT(offset <= allocationSize);
15285
15286 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15287
15288 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
15289 memRange.memory = hAllocation->GetMemory();
15290
15291 switch(hAllocation->GetType())
15292 {
15293 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15294 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15295 if(size == VK_WHOLE_SIZE)
15296 {
15297 memRange.size = allocationSize - memRange.offset;
15298 }
15299 else
15300 {
15301 VMA_ASSERT(offset + size <= allocationSize);
15302 memRange.size = VMA_MIN(
15303 VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
15304 allocationSize - memRange.offset);
15305 }
15306 break;
15307
15308 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15309 {
15310 // 1. Still within this allocation.
15311 memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15312 if(size == VK_WHOLE_SIZE)
15313 {
15314 size = allocationSize - offset;
15315 }
15316 else
15317 {
15318 VMA_ASSERT(offset + size <= allocationSize);
15319 }
15320 memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
15321
15322 // 2. Adjust to whole block.
15323 const VkDeviceSize allocationOffset = hAllocation->GetOffset();
15324 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15325 const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
15326 memRange.offset += allocationOffset;
15327 memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
15328
15329 break;
15330 }
15331
15332 default:
15333 VMA_ASSERT(0);
15334 }
15335
15336 switch(op)
15337 {
15338 case VMA_CACHE_FLUSH:
15339 (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15340 break;
15341 case VMA_CACHE_INVALIDATE:
15342 (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15343 break;
15344 default:
15345 VMA_ASSERT(0);
15346 }
15347 }
15348 // else: Just ignore this call.
15349}
15350
15351void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
15352{
15353 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15354
15355 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15356 {
15357 VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15358 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
15359 VMA_ASSERT(pDedicatedAllocations);
15360 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
15361 VMA_ASSERT(success);
15362 }
15363
15364 VkDeviceMemory hMemory = allocation->GetMemory();
15365
15366 /*
15367 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15368 before vkFreeMemory.
15369
15370 if(allocation->GetMappedData() != VMA_NULL)
15371 {
15372 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15373 }
15374 */
15375
15376 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15377
15378 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15379}
15380
15381void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15382{
15383 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15384 !hAllocation->CanBecomeLost() &&
15385 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15386 {
15387 void* pData = VMA_NULL;
15388 VkResult res = Map(hAllocation, &pData);
15389 if(res == VK_SUCCESS)
15390 {
15391 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15392 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15393 Unmap(hAllocation);
15394 }
15395 else
15396 {
15397 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15398 }
15399 }
15400}
15401
15402#if VMA_STATS_STRING_ENABLED
15403
15404void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15405{
15406 bool dedicatedAllocationsStarted = false;
15407 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15408 {
15409 VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
15410 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
15411 VMA_ASSERT(pDedicatedAllocVector);
15412 if(pDedicatedAllocVector->empty() == false)
15413 {
15414 if(dedicatedAllocationsStarted == false)
15415 {
15416 dedicatedAllocationsStarted = true;
15417 json.WriteString("DedicatedAllocations");
15418 json.BeginObject();
15419 }
15420
15421 json.BeginString("Type ");
15422 json.ContinueString(memTypeIndex);
15423 json.EndString();
15424
15425 json.BeginArray();
15426
15427 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
15428 {
15429 json.BeginObject(true);
15430 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
15431 hAlloc->PrintParameters(json);
15432 json.EndObject();
15433 }
15434
15435 json.EndArray();
15436 }
15437 }
15438 if(dedicatedAllocationsStarted)
15439 {
15440 json.EndObject();
15441 }
15442
15443 {
15444 bool allocationsStarted = false;
15445 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15446 {
15447 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
15448 {
15449 if(allocationsStarted == false)
15450 {
15451 allocationsStarted = true;
15452 json.WriteString("DefaultPools");
15453 json.BeginObject();
15454 }
15455
15456 json.BeginString("Type ");
15457 json.ContinueString(memTypeIndex);
15458 json.EndString();
15459
15460 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
15461 }
15462 }
15463 if(allocationsStarted)
15464 {
15465 json.EndObject();
15466 }
15467 }
15468
15469 // Custom pools
15470 {
15471 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15472 const size_t poolCount = m_Pools.size();
15473 if(poolCount > 0)
15474 {
15475 json.WriteString("Pools");
15476 json.BeginObject();
15477 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
15478 {
15479 json.BeginString();
15480 json.ContinueString(m_Pools[poolIndex]->GetId());
15481 json.EndString();
15482
15483 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
15484 }
15485 json.EndObject();
15486 }
15487 }
15488}
15489
15490#endif // #if VMA_STATS_STRING_ENABLED
15491
15492////////////////////////////////////////////////////////////////////////////////
15493// Public interface
15494
15495VkResult vmaCreateAllocator(
15496 const VmaAllocatorCreateInfo* pCreateInfo,
15497 VmaAllocator* pAllocator)
15498{
15499 VMA_ASSERT(pCreateInfo && pAllocator);
15500 VMA_DEBUG_LOG("vmaCreateAllocator");
15501 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
15502 return (*pAllocator)->Init(pCreateInfo);
15503}
15504
15505void vmaDestroyAllocator(
15506 VmaAllocator allocator)
15507{
15508 if(allocator != VK_NULL_HANDLE)
15509 {
15510 VMA_DEBUG_LOG("vmaDestroyAllocator");
15511 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
15512 vma_delete(&allocationCallbacks, allocator);
15513 }
15514}
15515
15516void vmaGetPhysicalDeviceProperties(
15517 VmaAllocator allocator,
15518 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
15519{
15520 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
15521 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
15522}
15523
15524void vmaGetMemoryProperties(
15525 VmaAllocator allocator,
15526 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
15527{
15528 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
15529 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
15530}
15531
15532void vmaGetMemoryTypeProperties(
15533 VmaAllocator allocator,
15534 uint32_t memoryTypeIndex,
15535 VkMemoryPropertyFlags* pFlags)
15536{
15537 VMA_ASSERT(allocator && pFlags);
15538 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
15539 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
15540}
15541
15542void vmaSetCurrentFrameIndex(
15543 VmaAllocator allocator,
15544 uint32_t frameIndex)
15545{
15546 VMA_ASSERT(allocator);
15547 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
15548
15549 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15550
15551 allocator->SetCurrentFrameIndex(frameIndex);
15552}
15553
15554void vmaCalculateStats(
15555 VmaAllocator allocator,
15556 VmaStats* pStats)
15557{
15558 VMA_ASSERT(allocator && pStats);
15559 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15560 allocator->CalculateStats(pStats);
15561}
15562
15563#if VMA_STATS_STRING_ENABLED
15564
15565void vmaBuildStatsString(
15566 VmaAllocator allocator,
15567 char** ppStatsString,
15568 VkBool32 detailedMap)
15569{
15570 VMA_ASSERT(allocator && ppStatsString);
15571 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15572
15573 VmaStringBuilder sb(allocator);
15574 {
15575 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
15576 json.BeginObject();
15577
15578 VmaStats stats;
15579 allocator->CalculateStats(&stats);
15580
15581 json.WriteString("Total");
15582 VmaPrintStatInfo(json, stats.total);
15583
15584 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
15585 {
15586 json.BeginString("Heap ");
15587 json.ContinueString(heapIndex);
15588 json.EndString();
15589 json.BeginObject();
15590
15591 json.WriteString("Size");
15592 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
15593
15594 json.WriteString("Flags");
15595 json.BeginArray(true);
15596 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
15597 {
15598 json.WriteString("DEVICE_LOCAL");
15599 }
15600 json.EndArray();
15601
15602 if(stats.memoryHeap[heapIndex].blockCount > 0)
15603 {
15604 json.WriteString("Stats");
15605 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
15606 }
15607
15608 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
15609 {
15610 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
15611 {
15612 json.BeginString("Type ");
15613 json.ContinueString(typeIndex);
15614 json.EndString();
15615
15616 json.BeginObject();
15617
15618 json.WriteString("Flags");
15619 json.BeginArray(true);
15620 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
15621 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
15622 {
15623 json.WriteString("DEVICE_LOCAL");
15624 }
15625 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15626 {
15627 json.WriteString("HOST_VISIBLE");
15628 }
15629 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
15630 {
15631 json.WriteString("HOST_COHERENT");
15632 }
15633 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
15634 {
15635 json.WriteString("HOST_CACHED");
15636 }
15637 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
15638 {
15639 json.WriteString("LAZILY_ALLOCATED");
15640 }
15641 json.EndArray();
15642
15643 if(stats.memoryType[typeIndex].blockCount > 0)
15644 {
15645 json.WriteString("Stats");
15646 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
15647 }
15648
15649 json.EndObject();
15650 }
15651 }
15652
15653 json.EndObject();
15654 }
15655 if(detailedMap == VK_TRUE)
15656 {
15657 allocator->PrintDetailedMap(json);
15658 }
15659
15660 json.EndObject();
15661 }
15662
15663 const size_t len = sb.GetLength();
15664 char* const pChars = vma_new_array(allocator, char, len + 1);
15665 if(len > 0)
15666 {
15667 memcpy(pChars, sb.GetData(), len);
15668 }
15669 pChars[len] = '\0';
15670 *ppStatsString = pChars;
15671}
15672
15673void vmaFreeStatsString(
15674 VmaAllocator allocator,
15675 char* pStatsString)
15676{
15677 if(pStatsString != VMA_NULL)
15678 {
15679 VMA_ASSERT(allocator);
15680 size_t len = strlen(pStatsString);
15681 vma_delete_array(allocator, pStatsString, len + 1);
15682 }
15683}
15684
15685#endif // #if VMA_STATS_STRING_ENABLED
15686
15687/*
15688This function is not protected by any mutex because it just reads immutable data.
15689*/
15690VkResult vmaFindMemoryTypeIndex(
15691 VmaAllocator allocator,
15692 uint32_t memoryTypeBits,
15693 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15694 uint32_t* pMemoryTypeIndex)
15695{
15696 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15697 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15698 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15699
15700 if(pAllocationCreateInfo->memoryTypeBits != 0)
15701 {
15702 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
15703 }
15704
15705 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
15706 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
15707
15708 const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
15709 if(mapped)
15710 {
15711 preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15712 }
15713
15714 // Convert usage to requiredFlags and preferredFlags.
15715 switch(pAllocationCreateInfo->usage)
15716 {
15717 case VMA_MEMORY_USAGE_UNKNOWN:
15718 break;
15719 case VMA_MEMORY_USAGE_GPU_ONLY:
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_CPU_ONLY:
15726 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
15727 break;
15728 case VMA_MEMORY_USAGE_CPU_TO_GPU:
15729 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15730 if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
15731 {
15732 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
15733 }
15734 break;
15735 case VMA_MEMORY_USAGE_GPU_TO_CPU:
15736 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
15737 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
15738 break;
15739 default:
15740 break;
15741 }
15742
15743 *pMemoryTypeIndex = UINT32_MAX;
15744 uint32_t minCost = UINT32_MAX;
15745 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
15746 memTypeIndex < allocator->GetMemoryTypeCount();
15747 ++memTypeIndex, memTypeBit <<= 1)
15748 {
15749 // This memory type is acceptable according to memoryTypeBits bitmask.
15750 if((memTypeBit & memoryTypeBits) != 0)
15751 {
15752 const VkMemoryPropertyFlags currFlags =
15753 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
15754 // This memory type contains requiredFlags.
15755 if((requiredFlags & ~currFlags) == 0)
15756 {
15757 // Calculate cost as number of bits from preferredFlags not present in this memory type.
15758 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
15759 // Remember memory type with lowest cost.
15760 if(currCost < minCost)
15761 {
15762 *pMemoryTypeIndex = memTypeIndex;
15763 if(currCost == 0)
15764 {
15765 return VK_SUCCESS;
15766 }
15767 minCost = currCost;
15768 }
15769 }
15770 }
15771 }
15772 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
15773}
15774
15775VkResult vmaFindMemoryTypeIndexForBufferInfo(
15776 VmaAllocator allocator,
15777 const VkBufferCreateInfo* pBufferCreateInfo,
15778 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15779 uint32_t* pMemoryTypeIndex)
15780{
15781 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15782 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
15783 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15784 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15785
15786 const VkDevice hDev = allocator->m_hDevice;
15787 VkBuffer hBuffer = VK_NULL_HANDLE;
15788 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
15789 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
15790 if(res == VK_SUCCESS)
15791 {
15792 VkMemoryRequirements memReq = {};
15793 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
15794 hDev, hBuffer, &memReq);
15795
15796 res = vmaFindMemoryTypeIndex(
15797 allocator,
15798 memReq.memoryTypeBits,
15799 pAllocationCreateInfo,
15800 pMemoryTypeIndex);
15801
15802 allocator->GetVulkanFunctions().vkDestroyBuffer(
15803 hDev, hBuffer, allocator->GetAllocationCallbacks());
15804 }
15805 return res;
15806}
15807
15808VkResult vmaFindMemoryTypeIndexForImageInfo(
15809 VmaAllocator allocator,
15810 const VkImageCreateInfo* pImageCreateInfo,
15811 const VmaAllocationCreateInfo* pAllocationCreateInfo,
15812 uint32_t* pMemoryTypeIndex)
15813{
15814 VMA_ASSERT(allocator != VK_NULL_HANDLE);
15815 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
15816 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
15817 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
15818
15819 const VkDevice hDev = allocator->m_hDevice;
15820 VkImage hImage = VK_NULL_HANDLE;
15821 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
15822 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
15823 if(res == VK_SUCCESS)
15824 {
15825 VkMemoryRequirements memReq = {};
15826 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
15827 hDev, hImage, &memReq);
15828
15829 res = vmaFindMemoryTypeIndex(
15830 allocator,
15831 memReq.memoryTypeBits,
15832 pAllocationCreateInfo,
15833 pMemoryTypeIndex);
15834
15835 allocator->GetVulkanFunctions().vkDestroyImage(
15836 hDev, hImage, allocator->GetAllocationCallbacks());
15837 }
15838 return res;
15839}
15840
15841VkResult vmaCreatePool(
15842 VmaAllocator allocator,
15843 const VmaPoolCreateInfo* pCreateInfo,
15844 VmaPool* pPool)
15845{
15846 VMA_ASSERT(allocator && pCreateInfo && pPool);
15847
15848 VMA_DEBUG_LOG("vmaCreatePool");
15849
15850 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15851
15852 VkResult res = allocator->CreatePool(pCreateInfo, pPool);
15853
15854#if VMA_RECORDING_ENABLED
15855 if(allocator->GetRecorder() != VMA_NULL)
15856 {
15857 allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
15858 }
15859#endif
15860
15861 return res;
15862}
15863
15864void vmaDestroyPool(
15865 VmaAllocator allocator,
15866 VmaPool pool)
15867{
15868 VMA_ASSERT(allocator);
15869
15870 if(pool == VK_NULL_HANDLE)
15871 {
15872 return;
15873 }
15874
15875 VMA_DEBUG_LOG("vmaDestroyPool");
15876
15877 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15878
15879#if VMA_RECORDING_ENABLED
15880 if(allocator->GetRecorder() != VMA_NULL)
15881 {
15882 allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
15883 }
15884#endif
15885
15886 allocator->DestroyPool(pool);
15887}
15888
15889void vmaGetPoolStats(
15890 VmaAllocator allocator,
15891 VmaPool pool,
15892 VmaPoolStats* pPoolStats)
15893{
15894 VMA_ASSERT(allocator && pool && pPoolStats);
15895
15896 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15897
15898 allocator->GetPoolStats(pool, pPoolStats);
15899}
15900
15901void vmaMakePoolAllocationsLost(
15902 VmaAllocator allocator,
15903 VmaPool pool,
15904 size_t* pLostAllocationCount)
15905{
15906 VMA_ASSERT(allocator && pool);
15907
15908 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15909
15910#if VMA_RECORDING_ENABLED
15911 if(allocator->GetRecorder() != VMA_NULL)
15912 {
15913 allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
15914 }
15915#endif
15916
15917 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
15918}
15919
15920VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
15921{
15922 VMA_ASSERT(allocator && pool);
15923
15924 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15925
15926 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
15927
15928 return allocator->CheckPoolCorruption(pool);
15929}
15930
15931VkResult vmaAllocateMemory(
15932 VmaAllocator allocator,
15933 const VkMemoryRequirements* pVkMemoryRequirements,
15934 const VmaAllocationCreateInfo* pCreateInfo,
15935 VmaAllocation* pAllocation,
15936 VmaAllocationInfo* pAllocationInfo)
15937{
15938 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
15939
15940 VMA_DEBUG_LOG("vmaAllocateMemory");
15941
15942 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15943
15944 VkResult result = allocator->AllocateMemory(
15945 *pVkMemoryRequirements,
15946 false, // requiresDedicatedAllocation
15947 false, // prefersDedicatedAllocation
15948 VK_NULL_HANDLE, // dedicatedBuffer
15949 VK_NULL_HANDLE, // dedicatedImage
15950 *pCreateInfo,
15951 VMA_SUBALLOCATION_TYPE_UNKNOWN,
15952 1, // allocationCount
15953 pAllocation);
15954
15955#if VMA_RECORDING_ENABLED
15956 if(allocator->GetRecorder() != VMA_NULL)
15957 {
15958 allocator->GetRecorder()->RecordAllocateMemory(
15959 allocator->GetCurrentFrameIndex(),
15960 *pVkMemoryRequirements,
15961 *pCreateInfo,
15962 *pAllocation);
15963 }
15964#endif
15965
15966 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
15967 {
15968 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
15969 }
15970
15971 return result;
15972}
15973
15974VkResult vmaAllocateMemoryPages(
15975 VmaAllocator allocator,
15976 const VkMemoryRequirements* pVkMemoryRequirements,
15977 const VmaAllocationCreateInfo* pCreateInfo,
15978 size_t allocationCount,
15979 VmaAllocation* pAllocations,
15980 VmaAllocationInfo* pAllocationInfo)
15981{
15982 if(allocationCount == 0)
15983 {
15984 return VK_SUCCESS;
15985 }
15986
15987 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
15988
15989 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
15990
15991 VMA_DEBUG_GLOBAL_MUTEX_LOCK
15992
15993 VkResult result = allocator->AllocateMemory(
15994 *pVkMemoryRequirements,
15995 false, // requiresDedicatedAllocation
15996 false, // prefersDedicatedAllocation
15997 VK_NULL_HANDLE, // dedicatedBuffer
15998 VK_NULL_HANDLE, // dedicatedImage
15999 *pCreateInfo,
16000 VMA_SUBALLOCATION_TYPE_UNKNOWN,
16001 allocationCount,
16002 pAllocations);
16003
16004#if VMA_RECORDING_ENABLED
16005 if(allocator->GetRecorder() != VMA_NULL)
16006 {
16007 allocator->GetRecorder()->RecordAllocateMemoryPages(
16008 allocator->GetCurrentFrameIndex(),
16009 *pVkMemoryRequirements,
16010 *pCreateInfo,
16011 (uint64_t)allocationCount,
16012 pAllocations);
16013 }
16014#endif
16015
16016 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16017 {
16018 for(size_t i = 0; i < allocationCount; ++i)
16019 {
16020 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16021 }
16022 }
16023
16024 return result;
16025}
16026
16027VkResult vmaAllocateMemoryForBuffer(
16028 VmaAllocator allocator,
16029 VkBuffer buffer,
16030 const VmaAllocationCreateInfo* pCreateInfo,
16031 VmaAllocation* pAllocation,
16032 VmaAllocationInfo* pAllocationInfo)
16033{
16034 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16035
16036 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16037
16038 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16039
16040 VkMemoryRequirements vkMemReq = {};
16041 bool requiresDedicatedAllocation = false;
16042 bool prefersDedicatedAllocation = false;
16043 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16044 requiresDedicatedAllocation,
16045 prefersDedicatedAllocation);
16046
16047 VkResult result = allocator->AllocateMemory(
16048 vkMemReq,
16049 requiresDedicatedAllocation,
16050 prefersDedicatedAllocation,
16051 buffer, // dedicatedBuffer
16052 VK_NULL_HANDLE, // dedicatedImage
16053 *pCreateInfo,
16054 VMA_SUBALLOCATION_TYPE_BUFFER,
16055 1, // allocationCount
16056 pAllocation);
16057
16058#if VMA_RECORDING_ENABLED
16059 if(allocator->GetRecorder() != VMA_NULL)
16060 {
16061 allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
16062 allocator->GetCurrentFrameIndex(),
16063 vkMemReq,
16064 requiresDedicatedAllocation,
16065 prefersDedicatedAllocation,
16066 *pCreateInfo,
16067 *pAllocation);
16068 }
16069#endif
16070
16071 if(pAllocationInfo && result == VK_SUCCESS)
16072 {
16073 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16074 }
16075
16076 return result;
16077}
16078
16079VkResult vmaAllocateMemoryForImage(
16080 VmaAllocator allocator,
16081 VkImage image,
16082 const VmaAllocationCreateInfo* pCreateInfo,
16083 VmaAllocation* pAllocation,
16084 VmaAllocationInfo* pAllocationInfo)
16085{
16086 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16087
16088 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16089
16090 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16091
16092 VkMemoryRequirements vkMemReq = {};
16093 bool requiresDedicatedAllocation = false;
16094 bool prefersDedicatedAllocation = false;
16095 allocator->GetImageMemoryRequirements(image, vkMemReq,
16096 requiresDedicatedAllocation, prefersDedicatedAllocation);
16097
16098 VkResult result = allocator->AllocateMemory(
16099 vkMemReq,
16100 requiresDedicatedAllocation,
16101 prefersDedicatedAllocation,
16102 VK_NULL_HANDLE, // dedicatedBuffer
16103 image, // dedicatedImage
16104 *pCreateInfo,
16105 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16106 1, // allocationCount
16107 pAllocation);
16108
16109#if VMA_RECORDING_ENABLED
16110 if(allocator->GetRecorder() != VMA_NULL)
16111 {
16112 allocator->GetRecorder()->RecordAllocateMemoryForImage(
16113 allocator->GetCurrentFrameIndex(),
16114 vkMemReq,
16115 requiresDedicatedAllocation,
16116 prefersDedicatedAllocation,
16117 *pCreateInfo,
16118 *pAllocation);
16119 }
16120#endif
16121
16122 if(pAllocationInfo && result == VK_SUCCESS)
16123 {
16124 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16125 }
16126
16127 return result;
16128}
16129
16130void vmaFreeMemory(
16131 VmaAllocator allocator,
16132 VmaAllocation allocation)
16133{
16134 VMA_ASSERT(allocator);
16135
16136 if(allocation == VK_NULL_HANDLE)
16137 {
16138 return;
16139 }
16140
16141 VMA_DEBUG_LOG("vmaFreeMemory");
16142
16143 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16144
16145#if VMA_RECORDING_ENABLED
16146 if(allocator->GetRecorder() != VMA_NULL)
16147 {
16148 allocator->GetRecorder()->RecordFreeMemory(
16149 allocator->GetCurrentFrameIndex(),
16150 allocation);
16151 }
16152#endif
16153
16154 allocator->FreeMemory(
16155 1, // allocationCount
16156 &allocation);
16157}
16158
16159void vmaFreeMemoryPages(
16160 VmaAllocator allocator,
16161 size_t allocationCount,
16162 VmaAllocation* pAllocations)
16163{
16164 if(allocationCount == 0)
16165 {
16166 return;
16167 }
16168
16169 VMA_ASSERT(allocator);
16170
16171 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16172
16173 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16174
16175#if VMA_RECORDING_ENABLED
16176 if(allocator->GetRecorder() != VMA_NULL)
16177 {
16178 allocator->GetRecorder()->RecordFreeMemoryPages(
16179 allocator->GetCurrentFrameIndex(),
16180 (uint64_t)allocationCount,
16181 pAllocations);
16182 }
16183#endif
16184
16185 allocator->FreeMemory(allocationCount, pAllocations);
16186}
16187
16188VkResult vmaResizeAllocation(
16189 VmaAllocator allocator,
16190 VmaAllocation allocation,
16191 VkDeviceSize newSize)
16192{
16193 VMA_ASSERT(allocator && allocation);
16194
16195 VMA_DEBUG_LOG("vmaResizeAllocation");
16196
16197 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16198
16199#if VMA_RECORDING_ENABLED
16200 if(allocator->GetRecorder() != VMA_NULL)
16201 {
16202 allocator->GetRecorder()->RecordResizeAllocation(
16203 allocator->GetCurrentFrameIndex(),
16204 allocation,
16205 newSize);
16206 }
16207#endif
16208
16209 return allocator->ResizeAllocation(allocation, newSize);
16210}
16211
16212void vmaGetAllocationInfo(
16213 VmaAllocator allocator,
16214 VmaAllocation allocation,
16215 VmaAllocationInfo* pAllocationInfo)
16216{
16217 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16218
16219 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16220
16221#if VMA_RECORDING_ENABLED
16222 if(allocator->GetRecorder() != VMA_NULL)
16223 {
16224 allocator->GetRecorder()->RecordGetAllocationInfo(
16225 allocator->GetCurrentFrameIndex(),
16226 allocation);
16227 }
16228#endif
16229
16230 allocator->GetAllocationInfo(allocation, pAllocationInfo);
16231}
16232
16233VkBool32 vmaTouchAllocation(
16234 VmaAllocator allocator,
16235 VmaAllocation allocation)
16236{
16237 VMA_ASSERT(allocator && allocation);
16238
16239 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16240
16241#if VMA_RECORDING_ENABLED
16242 if(allocator->GetRecorder() != VMA_NULL)
16243 {
16244 allocator->GetRecorder()->RecordTouchAllocation(
16245 allocator->GetCurrentFrameIndex(),
16246 allocation);
16247 }
16248#endif
16249
16250 return allocator->TouchAllocation(allocation);
16251}
16252
16253void vmaSetAllocationUserData(
16254 VmaAllocator allocator,
16255 VmaAllocation allocation,
16256 void* pUserData)
16257{
16258 VMA_ASSERT(allocator && allocation);
16259
16260 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16261
16262 allocation->SetUserData(allocator, pUserData);
16263
16264#if VMA_RECORDING_ENABLED
16265 if(allocator->GetRecorder() != VMA_NULL)
16266 {
16267 allocator->GetRecorder()->RecordSetAllocationUserData(
16268 allocator->GetCurrentFrameIndex(),
16269 allocation,
16270 pUserData);
16271 }
16272#endif
16273}
16274
16275void vmaCreateLostAllocation(
16276 VmaAllocator allocator,
16277 VmaAllocation* pAllocation)
16278{
16279 VMA_ASSERT(allocator && pAllocation);
16280
16281 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
16282
16283 allocator->CreateLostAllocation(pAllocation);
16284
16285#if VMA_RECORDING_ENABLED
16286 if(allocator->GetRecorder() != VMA_NULL)
16287 {
16288 allocator->GetRecorder()->RecordCreateLostAllocation(
16289 allocator->GetCurrentFrameIndex(),
16290 *pAllocation);
16291 }
16292#endif
16293}
16294
16295VkResult vmaMapMemory(
16296 VmaAllocator allocator,
16297 VmaAllocation allocation,
16298 void** ppData)
16299{
16300 VMA_ASSERT(allocator && allocation && ppData);
16301
16302 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16303
16304 VkResult res = allocator->Map(allocation, ppData);
16305
16306#if VMA_RECORDING_ENABLED
16307 if(allocator->GetRecorder() != VMA_NULL)
16308 {
16309 allocator->GetRecorder()->RecordMapMemory(
16310 allocator->GetCurrentFrameIndex(),
16311 allocation);
16312 }
16313#endif
16314
16315 return res;
16316}
16317
16318void vmaUnmapMemory(
16319 VmaAllocator allocator,
16320 VmaAllocation allocation)
16321{
16322 VMA_ASSERT(allocator && allocation);
16323
16324 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16325
16326#if VMA_RECORDING_ENABLED
16327 if(allocator->GetRecorder() != VMA_NULL)
16328 {
16329 allocator->GetRecorder()->RecordUnmapMemory(
16330 allocator->GetCurrentFrameIndex(),
16331 allocation);
16332 }
16333#endif
16334
16335 allocator->Unmap(allocation);
16336}
16337
16338void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16339{
16340 VMA_ASSERT(allocator && allocation);
16341
16342 VMA_DEBUG_LOG("vmaFlushAllocation");
16343
16344 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16345
16346 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16347
16348#if VMA_RECORDING_ENABLED
16349 if(allocator->GetRecorder() != VMA_NULL)
16350 {
16351 allocator->GetRecorder()->RecordFlushAllocation(
16352 allocator->GetCurrentFrameIndex(),
16353 allocation, offset, size);
16354 }
16355#endif
16356}
16357
16358void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
16359{
16360 VMA_ASSERT(allocator && allocation);
16361
16362 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16363
16364 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16365
16366 allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16367
16368#if VMA_RECORDING_ENABLED
16369 if(allocator->GetRecorder() != VMA_NULL)
16370 {
16371 allocator->GetRecorder()->RecordInvalidateAllocation(
16372 allocator->GetCurrentFrameIndex(),
16373 allocation, offset, size);
16374 }
16375#endif
16376}
16377
16378VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
16379{
16380 VMA_ASSERT(allocator);
16381
16382 VMA_DEBUG_LOG("vmaCheckCorruption");
16383
16384 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16385
16386 return allocator->CheckCorruption(memoryTypeBits);
16387}
16388
16389VkResult vmaDefragment(
16390 VmaAllocator allocator,
16391 VmaAllocation* pAllocations,
16392 size_t allocationCount,
16393 VkBool32* pAllocationsChanged,
16394 const VmaDefragmentationInfo *pDefragmentationInfo,
16395 VmaDefragmentationStats* pDefragmentationStats)
16396{
16397 // Deprecated interface, reimplemented using new one.
16398
16399 VmaDefragmentationInfo2 info2 = {};
16400 info2.allocationCount = (uint32_t)allocationCount;
16401 info2.pAllocations = pAllocations;
16402 info2.pAllocationsChanged = pAllocationsChanged;
16403 if(pDefragmentationInfo != VMA_NULL)
16404 {
16405 info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
16406 info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
16407 }
16408 else
16409 {
16410 info2.maxCpuAllocationsToMove = UINT32_MAX;
16411 info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
16412 }
16413 // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
16414
16415 VmaDefragmentationContext ctx;
16416 VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
16417 if(res == VK_NOT_READY)
16418 {
16419 res = vmaDefragmentationEnd( allocator, ctx);
16420 }
16421 return res;
16422}
16423
16424VkResult vmaDefragmentationBegin(
16425 VmaAllocator allocator,
16426 const VmaDefragmentationInfo2* pInfo,
16427 VmaDefragmentationStats* pStats,
16428 VmaDefragmentationContext *pContext)
16429{
16430 VMA_ASSERT(allocator && pInfo && pContext);
16431
16432 // Degenerate case: Nothing to defragment.
16433 if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
16434 {
16435 return VK_SUCCESS;
16436 }
16437
16438 VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
16439 VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
16440 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
16441 VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
16442
16443 VMA_DEBUG_LOG("vmaDefragmentationBegin");
16444
16445 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16446
16447 VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
16448
16449#if VMA_RECORDING_ENABLED
16450 if(allocator->GetRecorder() != VMA_NULL)
16451 {
16452 allocator->GetRecorder()->RecordDefragmentationBegin(
16453 allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
16454 }
16455#endif
16456
16457 return res;
16458}
16459
16460VkResult vmaDefragmentationEnd(
16461 VmaAllocator allocator,
16462 VmaDefragmentationContext context)
16463{
16464 VMA_ASSERT(allocator);
16465
16466 VMA_DEBUG_LOG("vmaDefragmentationEnd");
16467
16468 if(context != VK_NULL_HANDLE)
16469 {
16470 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16471
16472#if VMA_RECORDING_ENABLED
16473 if(allocator->GetRecorder() != VMA_NULL)
16474 {
16475 allocator->GetRecorder()->RecordDefragmentationEnd(
16476 allocator->GetCurrentFrameIndex(), context);
16477 }
16478#endif
16479
16480 return allocator->DefragmentationEnd(context);
16481 }
16482 else
16483 {
16484 return VK_SUCCESS;
16485 }
16486}
16487
16488VkResult vmaBindBufferMemory(
16489 VmaAllocator allocator,
16490 VmaAllocation allocation,
16491 VkBuffer buffer)
16492{
16493 VMA_ASSERT(allocator && allocation && buffer);
16494
16495 VMA_DEBUG_LOG("vmaBindBufferMemory");
16496
16497 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16498
16499 return allocator->BindBufferMemory(allocation, buffer);
16500}
16501
16502VkResult vmaBindImageMemory(
16503 VmaAllocator allocator,
16504 VmaAllocation allocation,
16505 VkImage image)
16506{
16507 VMA_ASSERT(allocator && allocation && image);
16508
16509 VMA_DEBUG_LOG("vmaBindImageMemory");
16510
16511 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16512
16513 return allocator->BindImageMemory(allocation, image);
16514}
16515
16516VkResult vmaCreateBuffer(
16517 VmaAllocator allocator,
16518 const VkBufferCreateInfo* pBufferCreateInfo,
16519 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16520 VkBuffer* pBuffer,
16521 VmaAllocation* pAllocation,
16522 VmaAllocationInfo* pAllocationInfo)
16523{
16524 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
16525
16526 if(pBufferCreateInfo->size == 0)
16527 {
16528 return VK_ERROR_VALIDATION_FAILED_EXT;
16529 }
16530
16531 VMA_DEBUG_LOG("vmaCreateBuffer");
16532
16533 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16534
16535 *pBuffer = VK_NULL_HANDLE;
16536 *pAllocation = VK_NULL_HANDLE;
16537
16538 // 1. Create VkBuffer.
16539 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
16540 allocator->m_hDevice,
16541 pBufferCreateInfo,
16542 allocator->GetAllocationCallbacks(),
16543 pBuffer);
16544 if(res >= 0)
16545 {
16546 // 2. vkGetBufferMemoryRequirements.
16547 VkMemoryRequirements vkMemReq = {};
16548 bool requiresDedicatedAllocation = false;
16549 bool prefersDedicatedAllocation = false;
16550 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
16551 requiresDedicatedAllocation, prefersDedicatedAllocation);
16552
16553 // Make sure alignment requirements for specific buffer usages reported
16554 // in Physical Device Properties are included in alignment reported by memory requirements.
16555 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
16556 {
16557 VMA_ASSERT(vkMemReq.alignment %
16558 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
16559 }
16560 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
16561 {
16562 VMA_ASSERT(vkMemReq.alignment %
16563 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
16564 }
16565 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
16566 {
16567 VMA_ASSERT(vkMemReq.alignment %
16568 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
16569 }
16570
16571 // 3. Allocate memory using allocator.
16572 res = allocator->AllocateMemory(
16573 vkMemReq,
16574 requiresDedicatedAllocation,
16575 prefersDedicatedAllocation,
16576 *pBuffer, // dedicatedBuffer
16577 VK_NULL_HANDLE, // dedicatedImage
16578 *pAllocationCreateInfo,
16579 VMA_SUBALLOCATION_TYPE_BUFFER,
16580 1, // allocationCount
16581 pAllocation);
16582
16583#if VMA_RECORDING_ENABLED
16584 if(allocator->GetRecorder() != VMA_NULL)
16585 {
16586 allocator->GetRecorder()->RecordCreateBuffer(
16587 allocator->GetCurrentFrameIndex(),
16588 *pBufferCreateInfo,
16589 *pAllocationCreateInfo,
16590 *pAllocation);
16591 }
16592#endif
16593
16594 if(res >= 0)
16595 {
16596 // 3. Bind buffer with memory.
16597 res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
16598 if(res >= 0)
16599 {
16600 // All steps succeeded.
16601 #if VMA_STATS_STRING_ENABLED
16602 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
16603 #endif
16604 if(pAllocationInfo != VMA_NULL)
16605 {
16606 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16607 }
16608
16609 return VK_SUCCESS;
16610 }
16611 allocator->FreeMemory(
16612 1, // allocationCount
16613 pAllocation);
16614 *pAllocation = VK_NULL_HANDLE;
16615 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16616 *pBuffer = VK_NULL_HANDLE;
16617 return res;
16618 }
16619 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
16620 *pBuffer = VK_NULL_HANDLE;
16621 return res;
16622 }
16623 return res;
16624}
16625
16626void vmaDestroyBuffer(
16627 VmaAllocator allocator,
16628 VkBuffer buffer,
16629 VmaAllocation allocation)
16630{
16631 VMA_ASSERT(allocator);
16632
16633 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16634 {
16635 return;
16636 }
16637
16638 VMA_DEBUG_LOG("vmaDestroyBuffer");
16639
16640 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16641
16642#if VMA_RECORDING_ENABLED
16643 if(allocator->GetRecorder() != VMA_NULL)
16644 {
16645 allocator->GetRecorder()->RecordDestroyBuffer(
16646 allocator->GetCurrentFrameIndex(),
16647 allocation);
16648 }
16649#endif
16650
16651 if(buffer != VK_NULL_HANDLE)
16652 {
16653 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
16654 }
16655
16656 if(allocation != VK_NULL_HANDLE)
16657 {
16658 allocator->FreeMemory(
16659 1, // allocationCount
16660 &allocation);
16661 }
16662}
16663
16664VkResult vmaCreateImage(
16665 VmaAllocator allocator,
16666 const VkImageCreateInfo* pImageCreateInfo,
16667 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16668 VkImage* pImage,
16669 VmaAllocation* pAllocation,
16670 VmaAllocationInfo* pAllocationInfo)
16671{
16672 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
16673
16674 if(pImageCreateInfo->extent.width == 0 ||
16675 pImageCreateInfo->extent.height == 0 ||
16676 pImageCreateInfo->extent.depth == 0 ||
16677 pImageCreateInfo->mipLevels == 0 ||
16678 pImageCreateInfo->arrayLayers == 0)
16679 {
16680 return VK_ERROR_VALIDATION_FAILED_EXT;
16681 }
16682
16683 VMA_DEBUG_LOG("vmaCreateImage");
16684
16685 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16686
16687 *pImage = VK_NULL_HANDLE;
16688 *pAllocation = VK_NULL_HANDLE;
16689
16690 // 1. Create VkImage.
16691 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
16692 allocator->m_hDevice,
16693 pImageCreateInfo,
16694 allocator->GetAllocationCallbacks(),
16695 pImage);
16696 if(res >= 0)
16697 {
16698 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
16699 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
16700 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
16701
16702 // 2. Allocate memory using allocator.
16703 VkMemoryRequirements vkMemReq = {};
16704 bool requiresDedicatedAllocation = false;
16705 bool prefersDedicatedAllocation = false;
16706 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
16707 requiresDedicatedAllocation, prefersDedicatedAllocation);
16708
16709 res = allocator->AllocateMemory(
16710 vkMemReq,
16711 requiresDedicatedAllocation,
16712 prefersDedicatedAllocation,
16713 VK_NULL_HANDLE, // dedicatedBuffer
16714 *pImage, // dedicatedImage
16715 *pAllocationCreateInfo,
16716 suballocType,
16717 1, // allocationCount
16718 pAllocation);
16719
16720#if VMA_RECORDING_ENABLED
16721 if(allocator->GetRecorder() != VMA_NULL)
16722 {
16723 allocator->GetRecorder()->RecordCreateImage(
16724 allocator->GetCurrentFrameIndex(),
16725 *pImageCreateInfo,
16726 *pAllocationCreateInfo,
16727 *pAllocation);
16728 }
16729#endif
16730
16731 if(res >= 0)
16732 {
16733 // 3. Bind image with memory.
16734 res = allocator->BindImageMemory(*pAllocation, *pImage);
16735 if(res >= 0)
16736 {
16737 // All steps succeeded.
16738 #if VMA_STATS_STRING_ENABLED
16739 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
16740 #endif
16741 if(pAllocationInfo != VMA_NULL)
16742 {
16743 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16744 }
16745
16746 return VK_SUCCESS;
16747 }
16748 allocator->FreeMemory(
16749 1, // allocationCount
16750 pAllocation);
16751 *pAllocation = VK_NULL_HANDLE;
16752 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16753 *pImage = VK_NULL_HANDLE;
16754 return res;
16755 }
16756 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
16757 *pImage = VK_NULL_HANDLE;
16758 return res;
16759 }
16760 return res;
16761}
16762
16763void vmaDestroyImage(
16764 VmaAllocator allocator,
16765 VkImage image,
16766 VmaAllocation allocation)
16767{
16768 VMA_ASSERT(allocator);
16769
16770 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
16771 {
16772 return;
16773 }
16774
16775 VMA_DEBUG_LOG("vmaDestroyImage");
16776
16777 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16778
16779#if VMA_RECORDING_ENABLED
16780 if(allocator->GetRecorder() != VMA_NULL)
16781 {
16782 allocator->GetRecorder()->RecordDestroyImage(
16783 allocator->GetCurrentFrameIndex(),
16784 allocation);
16785 }
16786#endif
16787
16788 if(image != VK_NULL_HANDLE)
16789 {
16790 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
16791 }
16792 if(allocation != VK_NULL_HANDLE)
16793 {
16794 allocator->FreeMemory(
16795 1, // allocationCount
16796 &allocation);
16797 }
16798}
Tony-LunarG390319b2019-03-18 15:54:16 -060016799#if defined(__GNUC__)
16800#pragma GCC diagnostic pop
Jeremy Hayes33838bf2019-06-05 14:01:20 -060016801#if defined(__clang__)
16802#pragma clang diagnostic pop
16803#endif
Tony-LunarG390319b2019-03-18 15:54:16 -060016804#endif
Tony-LunarG7b7e4e62019-03-18 15:01:55 -060016805#endif // #ifdef VMA_IMPLEMENTATION
Tony-LunarG0e564722019-03-19 16:09:14 -060016806// clang-format on