1 //
2 // Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
25
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29
30 /** \mainpage Vulkan Memory Allocator
31
32 <b>Version 2.0.0</b> (2018-03-19)
33
34 Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
35 License: MIT
36
37 Documentation of all members: vk_mem_alloc.h
38
39 \section main_table_of_contents Table of contents
40
41 - <b>User guide</b>
42 - \subpage quick_start
43 - [Project setup](@ref quick_start_project_setup)
44 - [Initialization](@ref quick_start_initialization)
45 - [Resource allocation](@ref quick_start_resource_allocation)
46 - \subpage choosing_memory_type
47 - [Usage](@ref choosing_memory_type_usage)
48 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
49 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
50 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
51 - \subpage memory_mapping
52 - [Mapping functions](@ref memory_mapping_mapping_functions)
53 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
54 - [Cache control](@ref memory_mapping_cache_control)
55 - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
56 - \subpage custom_memory_pools
57 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58 - \subpage defragmentation
59 - \subpage lost_allocations
60 - \subpage statistics
61 - [Numeric statistics](@ref statistics_numeric_statistics)
62 - [JSON dump](@ref statistics_json_dump)
63 - \subpage allocation_annotation
64 - [Allocation user data](@ref allocation_user_data)
65 - [Allocation names](@ref allocation_names)
66 - \subpage usage_patterns
67 - [Simple patterns](@ref usage_patterns_simple)
68 - [Advanced patterns](@ref usage_patterns_advanced)
69 - \subpage configuration
70 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
71 - [Custom host memory allocator](@ref custom_memory_allocator)
72 - [Device memory allocation callbacks](@ref allocation_callbacks)
73 - [Device heap memory limit](@ref heap_memory_limit)
74 - \subpage vk_khr_dedicated_allocation
75 - \subpage general_considerations
76 - [Thread safety](@ref general_considerations_thread_safety)
77 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
78 - [Features not supported](@ref general_considerations_features_not_supported)
79
80 \section main_see_also See also
81
82 - [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
83 - [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
84
85
86
87
88 \page quick_start Quick start
89
90 \section quick_start_project_setup Project setup
91
92 Vulkan Memory Allocator comes in form of a single header file.
93 You don't need to build it as a separate library project.
94 You can add this file directly to your project and submit it to code repository next to your other source files.
95
96 "Single header" doesn't mean that everything is contained in C/C++ declarations,
97 like it tends to be in case of inline functions or C++ templates.
98 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
99 If you don't do it properly, you will get linker errors.
100
101 To do it properly:
102
103 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
104 This includes declarations of all members of the library.
105 -# In exacly one CPP file define following macro before this include.
106 It enables also internal definitions.
107
108 \code
109 #define VMA_IMPLEMENTATION
110 #include "vk_mem_alloc.h"
111 \endcode
112
113 It may be a good idea to create dedicated CPP file just for this purpose.
114
115 \section quick_start_initialization Initialization
116
117 At program startup:
118
119 -# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
120 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
121 calling vmaCreateAllocator().
122
123 \code
124 VmaAllocatorCreateInfo allocatorInfo = {};
125 allocatorInfo.physicalDevice = physicalDevice;
126 allocatorInfo.device = device;
127
128 VmaAllocator allocator;
129 vmaCreateAllocator(&allocatorInfo, &allocator);
130 \endcode
131
132 \section quick_start_resource_allocation Resource allocation
133
134 When you want to create a buffer or image:
135
136 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
137 -# Fill VmaAllocationCreateInfo structure.
138 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
139 already allocated and bound to it.
140
141 \code
142 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
143 bufferInfo.size = 65536;
144 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
145
146 VmaAllocationCreateInfo allocInfo = {};
147 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
148
149 VkBuffer buffer;
150 VmaAllocation allocation;
151 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
152 \endcode
153
154 Don't forget to destroy your objects when no longer needed:
155
156 \code
157 vmaDestroyBuffer(allocator, buffer, allocation);
158 vmaDestroyAllocator(allocator);
159 \endcode
160
161
162 \page choosing_memory_type Choosing memory type
163
164 Physical devices in Vulkan support various combinations of memory heaps and
165 types. Help with choosing correct and optimal memory type for your specific
166 resource is one of the key features of this library. You can use it by filling
167 appropriate members of VmaAllocationCreateInfo structure, as described below.
168 You can also combine multiple methods.
169
170 -# If you just want to find memory type index that meets your requirements, you
171 can use function vmaFindMemoryTypeIndex().
172 -# If you want to allocate a region of device memory without association with any
173 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
174 this function is not recommended and usually not needed.
175 -# If you already have a buffer or an image created, you want to allocate memory
176 for it and then you will bind it yourself, you can use function
177 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
178 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
179 -# If you want to create a buffer or an image, allocate memory for it and bind
180 them together, all in one call, you can use function vmaCreateBuffer(),
181 vmaCreateImage(). This is the recommended way to use this library.
182
183 When using 3. or 4., the library internally queries Vulkan for memory types
184 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
185 and uses only one of these types.
186
187 If no memory type can be found that meets all the requirements, these functions
188 return `VK_ERROR_FEATURE_NOT_PRESENT`.
189
190 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
191 It means no requirements are specified for memory type.
192 It is valid, although not very useful.
193
194 \section choosing_memory_type_usage Usage
195
196 The easiest way to specify memory requirements is to fill member
197 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
198 It defines high level, common usage types.
199 For more details, see description of this enum.
200
201 For example, if you want to create a uniform buffer that will be filled using
202 transfer only once or infrequently and used for rendering every frame, you can
203 do it using following code:
204
205 \code
206 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
207 bufferInfo.size = 65536;
208 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
209
210 VmaAllocationCreateInfo allocInfo = {};
211 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
212
213 VkBuffer buffer;
214 VmaAllocation allocation;
215 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
216 \endcode
217
218 \section choosing_memory_type_required_preferred_flags Required and preferred flags
219
220 You can specify more detailed requirements by filling members
221 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
222 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
223 if you want to create a buffer that will be persistently mapped on host (so it
224 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
225 use following code:
226
227 \code
228 VmaAllocationCreateInfo allocInfo = {};
229 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
230 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
231 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
232
233 VkBuffer buffer;
234 VmaAllocation allocation;
235 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
236 \endcode
237
238 A memory type is chosen that has all the required flags and as many preferred
239 flags set as possible.
240
241 If you use VmaAllocationCreateInfo::usage, it is just internally converted to
242 a set of required and preferred flags.
243
244 \section choosing_memory_type_explicit_memory_types Explicit memory types
245
246 If you inspected memory types available on the physical device and you have
247 a preference for memory types that you want to use, you can fill member
248 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
249 means that a memory type with that index is allowed to be used for the
250 allocation. Special value 0, just like `UINT32_MAX`, means there are no
251 restrictions to memory type index.
252
253 Please note that this member is NOT just a memory type index.
254 Still you can use it to choose just one, specific memory type.
255 For example, if you already determined that your buffer should be created in
256 memory type 2, use following code:
257
258 \code
259 uint32_t memoryTypeIndex = 2;
260
261 VmaAllocationCreateInfo allocInfo = {};
262 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
263
264 VkBuffer buffer;
265 VmaAllocation allocation;
266 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
267 \endcode
268
269 \section choosing_memory_type_custom_memory_pools Custom memory pools
270
271 If you allocate from custom memory pool, all the ways of specifying memory
272 requirements described above are not applicable and the aforementioned members
273 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
274 explicitly when creating the pool and then used to make all the allocations from
275 that pool. For further details, see \ref custom_memory_pools.
276
277
278 \page memory_mapping Memory mapping
279
280 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
281 to be able to read from it or write to it in CPU code.
282 Mapping is possible only of memory allocated from a memory type that has
283 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
284 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
285 You can use them directly with memory allocated by this library,
286 but it is not recommended because of following issue:
287 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
288 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
289 Because of this, Vulkan Memory Allocator provides following facilities:
290
291 \section memory_mapping_mapping_functions Mapping functions
292
293 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
294 They are safer and more convenient to use than standard Vulkan functions.
295 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
296 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
297 They way it's implemented is that the library always maps entire memory block, not just region of the allocation.
298 For further details, see description of vmaMapMemory() function.
299 Example:
300
301 \code
302 // Having these objects initialized:
303
304 struct ConstantBuffer
305 {
306 ...
307 };
308 ConstantBuffer constantBufferData;
309
310 VmaAllocator allocator;
311 VmaBuffer constantBuffer;
312 VmaAllocation constantBufferAllocation;
313
314 // You can map and fill your buffer using following code:
315
316 void* mappedData;
317 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
318 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
319 vmaUnmapMemory(allocator, constantBufferAllocation);
320 \endcode
321
322 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
323
324 Kepping your memory persistently mapped is generally OK in Vulkan.
325 You don't need to unmap it before using its data on the GPU.
326 The library provides a special feature designed for that:
327 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
328 VmaAllocationCreateInfo::flags stay mapped all the time,
329 so you can just access CPU pointer to it any time
330 without a need to call any "map" or "unmap" function.
331 Example:
332
333 \code
334 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
335 bufCreateInfo.size = sizeof(ConstantBuffer);
336 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
337
338 VmaAllocationCreateInfo allocCreateInfo = {};
339 allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
340 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
341
342 VkBuffer buf;
343 VmaAllocation alloc;
344 VmaAllocationInfo allocInfo;
345 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
346
347 // Buffer is already mapped. You can access its memory.
348 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
349 \endcode
350
351 There are some exceptions though, when you should consider mapping memory only for a short period of time:
352
353 - When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
354 device is discrete AMD GPU,
355 and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
356 (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
357 then whenever a memory block allocated from this memory type stays mapped
358 for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
359 block is migrated by WDDM to system RAM, which degrades performance. It doesn't
360 matter if that particular memory block is actually used by the command buffer
361 being submitted.
362 - Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
363
364 \section memory_mapping_cache_control Cache control
365
366 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
367 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
368 you need to manually invalidate cache before reading of mapped pointer
369 using function `vkvkInvalidateMappedMemoryRanges()`
370 and flush cache after writing to mapped pointer
371 using function `vkFlushMappedMemoryRanges()`.
372 Example:
373
374 \code
375 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
376
377 VkMemoryPropertyFlags memFlags;
378 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
379 if((memFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
380 {
381 VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
382 memRange.memory = allocInfo.deviceMemory;
383 memRange.offset = allocInfo.offset;
384 memRange.size = allocInfo.size;
385 vkFlushMappedMemoryRanges(device, 1, &memRange);
386 }
387 \endcode
388
389 Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be host coherent.
390
391 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
392 currently provide `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag on all memory types that are
393 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, so on this platform you may not need to bother.
394
395 \section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
396
397 It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
398 despite it wasn't explicitly requested.
399 For example, application may work on integrated graphics with unified memory (like Intel) or
400 allocation from video memory might have failed, so the library chose system memory as fallback.
401
402 You can detect this case and map such allocation to access its memory on CPU directly,
403 instead of launching a transfer operation.
404 In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
405 and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
406
407 \code
408 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
409 bufCreateInfo.size = sizeof(ConstantBuffer);
410 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
411
412 VmaAllocationCreateInfo allocCreateInfo = {};
413 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
414
415 VkBuffer buf;
416 VmaAllocation alloc;
417 VmaAllocationInfo allocInfo;
418 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
419
420 VkMemoryPropertyFlags memFlags;
421 vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
422 if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
423 {
424 // Allocation ended up in mappable memory. You can map it and access it directly.
425 void* mappedData;
426 vmaMapMemory(allocator, alloc, &mappedData);
427 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
428 vmaUnmapMemory(allocator, alloc);
429 }
430 else
431 {
432 // Allocation ended up in non-mappable memory.
433 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
434 }
435 \endcode
436
437 You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
438 that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
439 If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
440 If not, the flag is just ignored.
441 Example:
442
443 \code
444 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
445 bufCreateInfo.size = sizeof(ConstantBuffer);
446 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
447
448 VmaAllocationCreateInfo allocCreateInfo = {};
449 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
450 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
451
452 VkBuffer buf;
453 VmaAllocation alloc;
454 VmaAllocationInfo allocInfo;
455 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
456
457 if(allocInfo.pUserData != nullptr)
458 {
459 // Allocation ended up in mappable memory.
460 // It's persistently mapped. You can access it directly.
461 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
462 }
463 else
464 {
465 // Allocation ended up in non-mappable memory.
466 // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
467 }
468 \endcode
469
470
471 \page custom_memory_pools Custom memory pools
472
473 A memory pool contains a number of `VkDeviceMemory` blocks.
474 The library automatically creates and manages default pool for each memory type available on the device.
475 Default memory pool automatically grows in size.
476 Size of allocated blocks is also variable and managed automatically.
477
478 You can create custom pool and allocate memory out of it.
479 It can be useful if you want to:
480
481 - Keep certain kind of allocations separate from others.
482 - Enforce particular, fixed size of Vulkan memory blocks.
483 - Limit maximum amount of Vulkan memory allocated for that pool.
484 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
485
486 To use custom memory pools:
487
488 -# Fill VmaPoolCreateInfo structure.
489 -# Call vmaCreatePool() to obtain #VmaPool handle.
490 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
491 You don't need to specify any other parameters of this structure, like usage.
492
493 Example:
494
495 \code
496 // Create a pool that can have at most 2 blocks, 128 MiB each.
497 VmaPoolCreateInfo poolCreateInfo = {};
498 poolCreateInfo.memoryTypeIndex = ...
499 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
500 poolCreateInfo.maxBlockCount = 2;
501
502 VmaPool pool;
503 vmaCreatePool(allocator, &poolCreateInfo, &pool);
504
505 // Allocate a buffer out of it.
506 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
507 bufCreateInfo.size = 1024;
508 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
509
510 VmaAllocationCreateInfo allocCreateInfo = {};
511 allocCreateInfo.pool = pool;
512
513 VkBuffer buf;
514 VmaAllocation alloc;
515 VmaAllocationInfo allocInfo;
516 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
517 \endcode
518
519 You have to free all allocations made from this pool before destroying it.
520
521 \code
522 vmaDestroyBuffer(allocator, buf, alloc);
523 vmaDestroyPool(allocator, pool);
524 \endcode
525
526 \section custom_memory_pools_MemTypeIndex Choosing memory type index
527
528 When creating a pool, you must explicitly specify memory type index.
529 To find the one suitable for your buffers or images, you can use helper functions
530 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
531 You need to provide structures with example parameters of buffers or images
532 that you are going to create in that pool.
533
534 \code
535 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
536 exampleBufCreateInfo.size = 1024; // Whatever.
537 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
538
539 VmaAllocationCreateInfo allocCreateInfo = {};
540 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
541
542 uint32_t memTypeIndex;
543 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
544
545 VmaPoolCreateInfo poolCreateInfo = {};
546 poolCreateInfo.memoryTypeIndex = memTypeIndex;
547 // ...
548 \endcode
549
550 When creating buffers/images allocated in that pool, provide following parameters:
551
552 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
553 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
554 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
555 or the other way around.
556 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
557 Other members are ignored anyway.
558
559
560 \page defragmentation Defragmentation
561
562 Interleaved allocations and deallocations of many objects of varying size can
563 cause fragmentation, which can lead to a situation where the library is unable
564 to find a continuous range of free memory for a new allocation despite there is
565 enough free space, just scattered across many small free ranges between existing
566 allocations.
567
568 To mitigate this problem, you can use vmaDefragment(). Given set of allocations,
569 this function can move them to compact used memory, ensure more continuous free
570 space and possibly also free some `VkDeviceMemory`. It can work only on
571 allocations made from memory type that is `HOST_VISIBLE`. Allocations are
572 modified to point to the new `VkDeviceMemory` and offset. Data in this memory is
573 also `memmove`-ed to the new place. However, if you have images or buffers bound
574 to these allocations (and you certainly do), you need to destroy, recreate, and
575 bind them to the new place in memory.
576
577 For further details and example code, see documentation of function
578 vmaDefragment().
579
580 \page lost_allocations Lost allocations
581
582 If your game oversubscribes video memory, if may work OK in previous-generation
583 graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
584 paged to system RAM. In Vulkan you can't do it because when you run out of
585 memory, an allocation just fails. If you have more data (e.g. textures) that can
586 fit into VRAM and you don't need it all at once, you may want to upload them to
587 GPU on demand and "push out" ones that are not used for a long time to make room
588 for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
589 cache. Vulkan Memory Allocator can help you with that by supporting a concept of
590 "lost allocations".
591
592 To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
593 flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
594 such allocation in every new frame, you need to query it if it's not lost.
595 To check it, call vmaTouchAllocation().
596 If the allocation is lost, you should not use it or buffer/image bound to it.
597 You mustn't forget to destroy this allocation and this buffer/image.
598 vmaGetAllocationInfo() can also be used for checking status of the allocation.
599 Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
600
601 To create an allocation that can make some other allocations lost to make room
602 for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
603 usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
604 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
605
606 Warning! Current implementation uses quite naive, brute force algorithm,
607 which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
608 flag quite slow. A new, more optimal algorithm and data structure to speed this
609 up is planned for the future.
610
611 <b>Q: When interleaving creation of new allocations with usage of existing ones,
612 how do you make sure that an allocation won't become lost while it's used in the
613 current frame?</b>
614
615 It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
616 status/parameters and checks whether it's not lost, but when it's not, it also
617 atomically marks it as used in the current frame, which makes it impossible to
618 become lost in that frame. It uses lockless algorithm, so it works fast and
619 doesn't involve locking any internal mutex.
620
621 <b>Q: What if my allocation may still be in use by the GPU when it's rendering a
622 previous frame while I already submit new frame on the CPU?</b>
623
624 You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
625 become lost for a number of additional frames back from the current one by
626 specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
627 memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
628
629 <b>Q: How do you inform the library when new frame starts?</b>
630
631 You need to call function vmaSetCurrentFrameIndex().
632
633 Example code:
634
635 \code
636 struct MyBuffer
637 {
638 VkBuffer m_Buf = nullptr;
639 VmaAllocation m_Alloc = nullptr;
640
641 // Called when the buffer is really needed in the current frame.
642 void EnsureBuffer();
643 };
644
645 void MyBuffer::EnsureBuffer()
646 {
647 // Buffer has been created.
648 if(m_Buf != VK_NULL_HANDLE)
649 {
650 // Check if its allocation is not lost + mark it as used in current frame.
651 if(vmaTouchAllocation(allocator, m_Alloc))
652 {
653 // It's all OK - safe to use m_Buf.
654 return;
655 }
656 }
657
658 // Buffer not yet exists or lost - destroy and recreate it.
659
660 vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
661
662 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
663 bufCreateInfo.size = 1024;
664 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
665
666 VmaAllocationCreateInfo allocCreateInfo = {};
667 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
668 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
669 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
670
671 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
672 }
673 \endcode
674
675 When using lost allocations, you may see some Vulkan validation layer warnings
676 about overlapping regions of memory bound to different kinds of buffers and
677 images. This is still valid as long as you implement proper handling of lost
678 allocations (like in the example above) and don't use them.
679
680 You can create an allocation that is already in lost state from the beginning using function
681 vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
682
683 You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
684 in a specified custom pool to lost state.
685 Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
686 cannot become lost.
687
688
689 \page statistics Statistics
690
691 This library contains functions that return information about its internal state,
692 especially the amount of memory allocated from Vulkan.
693 Please keep in mind that these functions need to traverse all internal data structures
694 to gather these information, so they may be quite time-consuming.
695 Don't call them too often.
696
697 \section statistics_numeric_statistics Numeric statistics
698
699 You can query for overall statistics of the allocator using function vmaCalculateStats().
700 Information are returned using structure #VmaStats.
701 It contains #VmaStatInfo - number of allocated blocks, number of allocations
702 (occupied ranges in these blocks), number of unused (free) ranges in these blocks,
703 number of bytes used and unused (but still allocated from Vulkan) and other information.
704 They are summed across memory heaps, memory types and total for whole allocator.
705
706 You can query for statistics of a custom pool using function vmaGetPoolStats().
707 Information are returned using structure #VmaPoolStats.
708
709 You can query for information about specific allocation using function vmaGetAllocationInfo().
710 It fill structure #VmaAllocationInfo.
711
712 \section statistics_json_dump JSON dump
713
714 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
715 The result is guaranteed to be correct JSON.
716 It uses ANSI encoding.
717 Any strings provided by user (see [Allocation names](@ref allocation_names))
718 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
719 this JSON string can be treated as using this encoding.
720 It must be freed using function vmaFreeStatsString().
721
722 The format of this JSON string is not part of official documentation of the library,
723 but it will not change in backward-incompatible way without increasing library major version number
724 and appropriate mention in changelog.
725
726 The JSON string contains all the data that can be obtained using vmaCalculateStats().
727 It can also contain detailed map of allocated memory blocks and their regions -
728 free and occupied by allocations.
729 This allows e.g. to visualize the memory or assess fragmentation.
730
731
732 \page allocation_annotation Allocation names and user data
733
734 \section allocation_user_data Allocation user data
735
736 You can annotate allocations with your own information, e.g. for debugging purposes.
737 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
738 an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
739 some handle, index, key, ordinal number or any other value that would associate
740 the allocation with your custom metadata.
741
742 \code
743 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
744 // Fill bufferInfo...
745
746 MyBufferMetadata* pMetadata = CreateBufferMetadata();
747
748 VmaAllocationCreateInfo allocCreateInfo = {};
749 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
750 allocCreateInfo.pUserData = pMetadata;
751
752 VkBuffer buffer;
753 VmaAllocation allocation;
754 vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
755 \endcode
756
757 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
758
759 \code
760 VmaAllocationInfo allocInfo;
761 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
762 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
763 \endcode
764
765 It can also be changed using function vmaSetAllocationUserData().
766
767 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
768 vmaBuildStatsString(), in hexadecimal form.
769
770 \section allocation_names Allocation names
771
772 There is alternative mode available where `pUserData` pointer is used to point to
773 a null-terminated string, giving a name to the allocation. To use this mode,
774 set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
775 Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
776 vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
777 The library creates internal copy of the string, so the pointer you pass doesn't need
778 to be valid for whole lifetime of the allocation. You can free it after the call.
779
780 \code
781 VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
782 // Fill imageInfo...
783
784 std::string imageName = "Texture: ";
785 imageName += fileName;
786
787 VmaAllocationCreateInfo allocCreateInfo = {};
788 allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
789 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
790 allocCreateInfo.pUserData = imageName.c_str();
791
792 VkImage image;
793 VmaAllocation allocation;
794 vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
795 \endcode
796
797 The value of `pUserData` pointer of the allocation will be different than the one
798 you passed when setting allocation's name - pointing to a buffer managed
799 internally that holds copy of the string.
800
801 \code
802 VmaAllocationInfo allocInfo;
803 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
804 const char* imageName = (const char*)allocInfo.pUserData;
805 printf("Image name: %s\n", imageName);
806 \endcode
807
808 That string is also printed in JSON report created by vmaBuildStatsString().
809
810
811 \page usage_patterns Recommended usage patterns
812
813 \section usage_patterns_simple Simple patterns
814
815 \subsection usage_patterns_simple_render_targets Render targets
816
817 <b>When:</b>
818 Any resources that you frequently write and read on GPU,
819 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
820 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
821
822 <b>What to do:</b>
823 Create them in video memory that is fastest to access from GPU using
824 #VMA_MEMORY_USAGE_GPU_ONLY.
825
826 Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
827 and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
828 especially if they are large or if you plan to destroy and recreate them e.g. when
829 display resolution changes.
830 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
831
832 \subsection usage_patterns_simple_immutable_resources Immutable resources
833
834 <b>When:</b>
835 Any resources that you fill on CPU only once (aka "immutable") or infrequently
836 and then read frequently on GPU,
837 e.g. textures, vertex and index buffers, constant buffers that don't change often.
838
839 <b>What to do:</b>
840 Create them in video memory that is fastest to access from GPU using
841 #VMA_MEMORY_USAGE_GPU_ONLY.
842
843 To initialize content of such resource, create a CPU-side (aka "staging") copy of it
844 in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
845 and submit a transfer from it to the GPU resource.
846 You can keep the staging copy if you need it for another upload transfer in the future.
847 If you don't, you can destroy it or reuse this buffer for uploading different resource
848 after the transfer finishes.
849
850 Prefer to create just buffers in system memory rather than images, even for uploading textures.
851 Use `vkCmdCopyBufferToImage()`.
852 Dont use images with `VK_IMAGE_TILING_LINEAR`.
853
854 \subsection usage_patterns_dynamic_resources Dynamic resources
855
856 <b>When:</b>
857 Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
858 written on CPU, read on GPU.
859
860 <b>What to do:</b>
861 Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
862 You can map it and write to it directly on CPU, as well as read from it on GPU.
863
864 This is a more complex situation. Different solutions are possible,
865 and the best one depends on specific GPU type, but you can use this simple approach for the start.
866 Prefer to write to such resource sequentially (e.g. using `memcpy`).
867 Don't perform random access or any reads from it, as it may be very slow.
868
869 \subsection usage_patterns_readback Readback
870
871 <b>When:</b>
872 Resources that contain data written by GPU that you want to read back on CPU,
873 e.g. results of some computations.
874
875 <b>What to do:</b>
876 Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
877 You can write to them directly on GPU, as well as map and read them on CPU.
878
879 \section usage_patterns_advanced Advanced patterns
880
881 \subsection usage_patterns_integrated_graphics Detecting integrated graphics
882
883 You can support integrated graphics (like Intel HD Graphics, AMD APU) better
884 by detecting it in Vulkan.
885 To do it, call `vkGetPhysicalDeviceProperties()`, inspect
886 `VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
887 When you find it, you can assume that memory is unified and all memory types are equally fast
888 to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
889
890 You can then sum up sizes of all available memory heaps and treat them as useful for
891 your GPU resources, instead of only `DEVICE_LOCAL` ones.
892 You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
893 directly instead of submitting explicit transfer (see below).
894
895 \subsection usage_patterns_direct_vs_transfer Direct access versus transfer
896
897 For resources that you frequently write on CPU and read on GPU, many solutions are possible:
898
899 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
900 second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
901 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
902 read it directly on GPU.
903 -# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
904 read it directly on GPU.
905
906 Which solution is the most efficient depends on your resource and especially on the GPU.
907 It is best to measure it and then make the decision.
908 Some general recommendations:
909
910 - On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
911 related to using a second copy.
912 - For small resources (e.g. constant buffers) use (2).
913 Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
914 Even if the resource ends up in system memory, its data may be cached on GPU after first
915 fetch over PCIe bus.
916 - For larger resources (e.g. textures), decide between (1) and (2).
917 You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
918 both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
919
920 Similarly, for resources that you frequently write on GPU and read on CPU, multiple
921 solutions are possible:
922
923 -# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
924 second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
925 -# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
926 map it and read it on CPU.
927
928 You should take some measurements to decide which option is faster in case of your specific
929 resource.
930
931 If you don't want to specialize your code for specific types of GPUs, yon can still make
932 an simple optimization for cases when your resource ends up in mappable memory to use it
933 directly in this case instead of creating CPU-side staging copy.
934 For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
935
936
937 \page configuration Configuration
938
939 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
940 before each include of this file or change directly in this file to provide
941 your own implementation of basic facilities like assert, `min()` and `max()` functions,
942 mutex, atomic etc.
943 The library uses its own implementation of containers by default, but you can switch to using
944 STL containers instead.
945
946 \section config_Vulkan_functions Pointers to Vulkan functions
947
948 The library uses Vulkan functions straight from the `vulkan.h` header by default.
949 If you want to provide your own pointers to these functions, e.g. fetched using
950 `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
951
952 -# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
953 -# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
954
955 \section custom_memory_allocator Custom host memory allocator
956
957 If you use custom allocator for CPU memory rather than default operator `new`
958 and `delete` from C++, you can make this library using your allocator as well
959 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
960 functions will be passed to Vulkan, as well as used by the library itself to
961 make any CPU-side allocations.
962
963 \section allocation_callbacks Device memory allocation callbacks
964
965 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
966 You can setup callbacks to be informed about these calls, e.g. for the purpose
967 of gathering some statistics. To do it, fill optional member
968 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
969
970 \section heap_memory_limit Device heap memory limit
971
972 If you want to test how your program behaves with limited amount of Vulkan device
973 memory available without switching your graphics card to one that really has
974 smaller VRAM, you can use a feature of this library intended for this purpose.
975 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
976
977
978
979 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
980
981 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
982 performance on some GPUs. It augments Vulkan API with possibility to query
983 driver whether it prefers particular buffer or image to have its own, dedicated
984 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
985 to do some internal optimizations.
986
987 The extension is supported by this library. It will be used automatically when
988 enabled. To enable it:
989
990 1 . When creating Vulkan device, check if following 2 device extensions are
991 supported (call `vkEnumerateDeviceExtensionProperties()`).
992 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
993
994 - VK_KHR_get_memory_requirements2
995 - VK_KHR_dedicated_allocation
996
997 If you enabled these extensions:
998
999 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
1000 your #VmaAllocator`to inform the library that you enabled required extensions
1001 and you want the library to use them.
1002
1003 \code
1004 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
1005
1006 vmaCreateAllocator(&allocatorInfo, &allocator);
1007 \endcode
1008
1009 That's all. The extension will be automatically used whenever you create a
1010 buffer using vmaCreateBuffer() or image using vmaCreateImage().
1011
1012 When using the extension together with Vulkan Validation Layer, you will receive
1013 warnings like this:
1014
1015 vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
1016
1017 It is OK, you should just ignore it. It happens because you use function
1018 `vkGetBufferMemoryRequirements2KHR()` instead of standard
1019 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
1020 unaware of it.
1021
1022 To learn more about this extension, see:
1023
1024 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
1025 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
1026
1027
1028
1029 \page general_considerations General considerations
1030
1031 \section general_considerations_thread_safety Thread safety
1032
1033 - The library has no global state, so separate #VmaAllocator objects can be used
1034 independently.
1035 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
1036 - By default, all calls to functions that take #VmaAllocator as first parameter
1037 are safe to call from multiple threads simultaneously because they are
1038 synchronized internally when needed.
1039 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
1040 flag, calls to functions that take such #VmaAllocator object must be
1041 synchronized externally.
1042 - Access to a #VmaAllocation object must be externally synchronized. For example,
1043 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
1044 threads at the same time if you pass the same #VmaAllocation object to these
1045 functions.
1046
1047 \section general_considerations_allocation_algorithm Allocation algorithm
1048
1049 The library uses following algorithm for allocation, in order:
1050
1051 -# Try to find free range of memory in existing blocks.
1052 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
1053 -# If failed, try to create such block with size/2, size/4, size/8.
1054 -# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
1055 specified, try to find space in existing blocks, possilby making some other
1056 allocations lost.
1057 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
1058 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1059 -# If failed, choose other memory type that meets the requirements specified in
1060 VmaAllocationCreateInfo and go to point 1.
1061 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1062
1063 \section general_considerations_features_not_supported Features not supported
1064
1065 Features deliberately excluded from the scope of this library:
1066
1067 - Data transfer - issuing commands that transfer data between buffers or images, any usage of
1068 `VkCommandList` or `VkCommandQueue` and related synchronization is responsibility of the user.
1069 - Support for any programming languages other than C/C++.
1070 Bindings to other languages are welcomed as external projects.
1071
1072 */
1073
1074 // For skia we don't include vulkan.h here. Before including this header we always include
1075 // GrVkDefines which has a user defined header. Additionally we don't require vulkan/vulkan.h to be
1076 // on the include path.
1077 // #include <vulkan/vulkan.h>
1078
1079 /** \struct VmaAllocator
1080 \brief Represents main object of this library initialized.
1081
1082 Fill structure VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
1083 Call function vmaDestroyAllocator() to destroy it.
1084
1085 It is recommended to create just one object of this type per `VkDevice` object,
1086 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
1087 */
1088 VK_DEFINE_HANDLE(VmaAllocator)
1089
1090 /// Callback function called after successful vkAllocateMemory.
1091 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
1092 VmaAllocator allocator,
1093 uint32_t memoryType,
1094 VkDeviceMemory memory,
1095 VkDeviceSize size);
1096 /// Callback function called before vkFreeMemory.
1097 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
1098 VmaAllocator allocator,
1099 uint32_t memoryType,
1100 VkDeviceMemory memory,
1101 VkDeviceSize size);
1102
1103 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
1104
1105 Provided for informative purpose, e.g. to gather statistics about number of
1106 allocations or total amount of memory allocated in Vulkan.
1107
1108 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
1109 */
1110 typedef struct VmaDeviceMemoryCallbacks {
1111 /// Optional, can be null.
1112 PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
1113 /// Optional, can be null.
1114 PFN_vmaFreeDeviceMemoryFunction pfnFree;
1115 } VmaDeviceMemoryCallbacks;
1116
1117 /// Flags for created #VmaAllocator.
1118 typedef enum VmaAllocatorCreateFlagBits {
1119 /** \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.
1120
1121 Using this flag may increase performance because internal mutexes are not used.
1122 */
1123 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
1124 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
1125
1126 Using this extenion will automatically allocate dedicated blocks of memory for
1127 some buffers and images instead of suballocating place for them out of bigger
1128 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
1129 flag) when it is recommended by the driver. It may improve performance on some
1130 GPUs.
1131
1132 You may set this flag only if you found out that following device extensions are
1133 supported, you enabled them while creating Vulkan device passed as
1134 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
1135 library:
1136
1137 - VK_KHR_get_memory_requirements2
1138 - VK_KHR_dedicated_allocation
1139
1140 When this flag is set, you can experience following warnings reported by Vulkan
1141 validation layer. You can ignore them.
1142
1143 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
1144 */
1145 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
1146
1147 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1148 } VmaAllocatorCreateFlagBits;
1149 typedef VkFlags VmaAllocatorCreateFlags;
1150
1151 /** \brief Pointers to some Vulkan functions - a subset used by the library.
1152
1153 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
1154 */
1155 typedef struct VmaVulkanFunctions {
1156 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
1157 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
1158 PFN_vkAllocateMemory vkAllocateMemory;
1159 PFN_vkFreeMemory vkFreeMemory;
1160 PFN_vkMapMemory vkMapMemory;
1161 PFN_vkUnmapMemory vkUnmapMemory;
1162 PFN_vkBindBufferMemory vkBindBufferMemory;
1163 PFN_vkBindImageMemory vkBindImageMemory;
1164 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
1165 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
1166 PFN_vkCreateBuffer vkCreateBuffer;
1167 PFN_vkDestroyBuffer vkDestroyBuffer;
1168 PFN_vkCreateImage vkCreateImage;
1169 PFN_vkDestroyImage vkDestroyImage;
1170 PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
1171 PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
1172 } VmaVulkanFunctions;
1173
1174 /// Description of a Allocator to be created.
1175 typedef struct VmaAllocatorCreateInfo
1176 {
1177 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1178 VmaAllocatorCreateFlags flags;
1179 /// Vulkan physical device.
1180 /** It must be valid throughout whole lifetime of created allocator. */
1181 VkPhysicalDevice physicalDevice;
1182 /// Vulkan device.
1183 /** It must be valid throughout whole lifetime of created allocator. */
1184 VkDevice device;
1185 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1186 /** Set to 0 to use default, which is currently 256 MiB. */
1187 VkDeviceSize preferredLargeHeapBlockSize;
1188 /// Custom CPU memory allocation callbacks. Optional.
1189 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1190 const VkAllocationCallbacks* pAllocationCallbacks;
1191 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1192 /** Optional, can be null. */
1193 const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
1194 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1195
1196 This value is used only when you make allocations with
1197 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1198 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1199
1200 For example, if you double-buffer your command buffers, so resources used for
1201 rendering in previous frame may still be in use by the GPU at the moment you
1202 allocate resources needed for the current frame, set this value to 1.
1203
1204 If you want to allow any allocations other than used in the current frame to
1205 become lost, set this value to 0.
1206 */
1207 uint32_t frameInUseCount;
1208 /** \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.
1209
1210 If not NULL, it must be a pointer to an array of
1211 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1212 maximum number of bytes that can be allocated out of particular Vulkan memory
1213 heap.
1214
1215 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1216 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1217
1218 If there is a limit defined for a heap:
1219
1220 - If user tries to allocate more memory from that heap using this allocator,
1221 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1222 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1223 value of this limit will be reported instead when using vmaGetMemoryProperties().
1224
1225 Warning! Using this feature may not be equivalent to installing a GPU with
1226 smaller amount of memory, because graphics driver doesn't necessary fail new
1227 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1228 exceeded. It may return success and just silently migrate some device memory
1229 blocks to system RAM.
1230 */
1231 const VkDeviceSize* pHeapSizeLimit;
1232 /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
1233
1234 If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
1235 you can pass null as this member, because the library will fetch pointers to
1236 Vulkan functions internally in a static way, like:
1237
1238 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
1239
1240 Fill this member if you want to provide your own pointers to Vulkan functions,
1241 e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
1242 */
1243 const VmaVulkanFunctions* pVulkanFunctions;
1244 } VmaAllocatorCreateInfo;
1245
1246 /// Creates Allocator object.
1247 VkResult vmaCreateAllocator(
1248 const VmaAllocatorCreateInfo* pCreateInfo,
1249 VmaAllocator* pAllocator);
1250
1251 /// Destroys allocator object.
1252 void vmaDestroyAllocator(
1253 VmaAllocator allocator);
1254
1255 /**
1256 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1257 You can access it here, without fetching it again on your own.
1258 */
1259 void vmaGetPhysicalDeviceProperties(
1260 VmaAllocator allocator,
1261 const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
1262
1263 /**
1264 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1265 You can access it here, without fetching it again on your own.
1266 */
1267 void vmaGetMemoryProperties(
1268 VmaAllocator allocator,
1269 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
1270
1271 /**
1272 \brief Given Memory Type Index, returns Property Flags of this memory type.
1273
1274 This is just a convenience function. Same information can be obtained using
1275 vmaGetMemoryProperties().
1276 */
1277 void vmaGetMemoryTypeProperties(
1278 VmaAllocator allocator,
1279 uint32_t memoryTypeIndex,
1280 VkMemoryPropertyFlags* pFlags);
1281
1282 /** \brief Sets index of the current frame.
1283
1284 This function must be used if you make allocations with
1285 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
1286 #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
1287 when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
1288 become lost in the current frame.
1289 */
1290 void vmaSetCurrentFrameIndex(
1291 VmaAllocator allocator,
1292 uint32_t frameIndex);
1293
1294 /** \brief Calculated statistics of memory usage in entire allocator.
1295 */
1296 typedef struct VmaStatInfo
1297 {
1298 /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
1299 uint32_t blockCount;
1300 /// Number of #VmaAllocation allocation objects allocated.
1301 uint32_t allocationCount;
1302 /// Number of free ranges of memory between allocations.
1303 uint32_t unusedRangeCount;
1304 /// Total number of bytes occupied by all allocations.
1305 VkDeviceSize usedBytes;
1306 /// Total number of bytes occupied by unused ranges.
1307 VkDeviceSize unusedBytes;
1308 VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
1309 VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
1310 } VmaStatInfo;
1311
1312 /// General statistics from current state of Allocator.
1313 typedef struct VmaStats
1314 {
1315 VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
1316 VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
1317 VmaStatInfo total;
1318 } VmaStats;
1319
1320 /// Retrieves statistics from current state of the Allocator.
1321 void vmaCalculateStats(
1322 VmaAllocator allocator,
1323 VmaStats* pStats);
1324
1325 #define VMA_STATS_STRING_ENABLED 1
1326
1327 #if VMA_STATS_STRING_ENABLED
1328
1329 /// Builds and returns statistics as string in JSON format.
1330 /** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
1331 */
1332 void vmaBuildStatsString(
1333 VmaAllocator allocator,
1334 char** ppStatsString,
1335 VkBool32 detailedMap);
1336
1337 void vmaFreeStatsString(
1338 VmaAllocator allocator,
1339 char* pStatsString);
1340
1341 #endif // #if VMA_STATS_STRING_ENABLED
1342
1343 /** \struct VmaPool
1344 \brief Represents custom memory pool
1345
1346 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
1347 Call function vmaDestroyPool() to destroy it.
1348
1349 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
1350 */
1351 VK_DEFINE_HANDLE(VmaPool)
1352
1353 typedef enum VmaMemoryUsage
1354 {
1355 /** No intended memory usage specified.
1356 Use other members of VmaAllocationCreateInfo to specify your requirements.
1357 */
1358 VMA_MEMORY_USAGE_UNKNOWN = 0,
1359 /** Memory will be used on device only, so fast access from the device is preferred.
1360 It usually means device-local GPU (video) memory.
1361 No need to be mappable on host.
1362 It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
1363
1364 Usage:
1365
1366 - Resources written and read by device, e.g. images used as attachments.
1367 - Resources transferred from host once (immutable) or infrequently and read by
1368 device multiple times, e.g. textures to be sampled, vertex buffers, uniform
1369 (constant) buffers, and majority of other types of resources used by device.
1370
1371 Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
1372 In such case, you are free to map it.
1373 You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
1374 */
1375 VMA_MEMORY_USAGE_GPU_ONLY = 1,
1376 /** Memory will be mappable on host.
1377 It usually means CPU (system) memory.
1378 Resources created in this pool may still be accessible to the device, but access to them can be slower.
1379 Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
1380 CPU read may be uncached.
1381 It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
1382
1383 Usage: Staging copy of resources used as transfer source.
1384 */
1385 VMA_MEMORY_USAGE_CPU_ONLY = 2,
1386 /**
1387 Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
1388 CPU reads may be uncached and very slow.
1389
1390 Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
1391 */
1392 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
1393 /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
1394 It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
1395
1396 Usage:
1397
1398 - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
1399 - 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.
1400 */
1401 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
1402 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
1403 } VmaMemoryUsage;
1404
1405 /// Flags to be passed as VmaAllocationCreateInfo::flags.
1406 typedef enum VmaAllocationCreateFlagBits {
1407 /** \brief Set this flag if the allocation should have its own memory block.
1408
1409 Use it for special, big resources, like fullscreen images used as attachments.
1410
1411 This flag must also be used for host visible resources that you want to map
1412 simultaneously because otherwise they might end up as regions of the same
1413 `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
1414 simultaneously is illegal.
1415
1416 You should not use this flag if VmaAllocationCreateInfo::pool is not null.
1417 */
1418 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
1419
1420 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
1421
1422 If new allocation cannot be placed in any of the existing blocks, allocation
1423 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
1424
1425 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
1426 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
1427
1428 If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
1429 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
1430 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
1431
1432 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
1433
1434 Is it valid to use this flag for allocation made from memory type that is not
1435 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
1436 useful if you need an allocation that is efficient to use on GPU
1437 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
1438 support it (e.g. Intel GPU).
1439
1440 You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
1441 */
1442 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
1443 /** Allocation created with this flag can become lost as a result of another
1444 allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
1445 must check it before use.
1446
1447 To check if allocation is not lost, call vmaGetAllocationInfo() and check if
1448 VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
1449
1450 For details about supporting lost allocations, see Lost Allocations
1451 chapter of User Guide on Main Page.
1452
1453 You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
1454 */
1455 VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
1456 /** While creating allocation using this flag, other allocations that were
1457 created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
1458
1459 For details about supporting lost allocations, see Lost Allocations
1460 chapter of User Guide on Main Page.
1461 */
1462 VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
1463 /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
1464 null-terminated string. Instead of copying pointer value, a local copy of the
1465 string is made and stored in allocation's `pUserData`. The string is automatically
1466 freed together with the allocation. It is also used in vmaBuildStatsString().
1467 */
1468 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
1469
1470 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1471 } VmaAllocationCreateFlagBits;
1472 typedef VkFlags VmaAllocationCreateFlags;
1473
1474 typedef struct VmaAllocationCreateInfo
1475 {
1476 /// Use #VmaAllocationCreateFlagBits enum.
1477 VmaAllocationCreateFlags flags;
1478 /** \brief Intended usage of memory.
1479
1480 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1481 If `pool` is not null, this member is ignored.
1482 */
1483 VmaMemoryUsage usage;
1484 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
1485
1486 Leave 0 if you specify memory requirements in other way. \n
1487 If `pool` is not null, this member is ignored.*/
1488 VkMemoryPropertyFlags requiredFlags;
1489 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1490
1491 Set to 0 if no additional flags are prefered. \n
1492 If `pool` is not null, this member is ignored. */
1493 VkMemoryPropertyFlags preferredFlags;
1494 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1495
1496 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1497 it meets other requirements specified by this structure, with no further
1498 restrictions on memory type index. \n
1499 If `pool` is not null, this member is ignored.
1500 */
1501 uint32_t memoryTypeBits;
1502 /** \brief Pool that this allocation should be created in.
1503
1504 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1505 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1506 */
1507 VmaPool pool;
1508 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1509
1510 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1511 null or pointer to a null-terminated string. The string will be then copied to
1512 internal buffer, so it doesn't need to be valid after allocation call.
1513 */
1514 void* pUserData;
1515 } VmaAllocationCreateInfo;
1516
1517 /**
1518 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1519
1520 This algorithm tries to find a memory type that:
1521
1522 - Is allowed by memoryTypeBits.
1523 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
1524 - Matches intended usage.
1525 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1526
1527 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1528 from this function or any other allocating function probably means that your
1529 device doesn't support any memory type with requested features for the specific
1530 type of resource you want to use it for. Please check parameters of your
1531 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1532 */
1533 VkResult vmaFindMemoryTypeIndex(
1534 VmaAllocator allocator,
1535 uint32_t memoryTypeBits,
1536 const VmaAllocationCreateInfo* pAllocationCreateInfo,
1537 uint32_t* pMemoryTypeIndex);
1538
1539 /**
1540 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1541
1542 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1543 It internally creates a temporary, dummy buffer that never has memory bound.
1544 It is just a convenience function, equivalent to calling:
1545
1546 - `vkCreateBuffer`
1547 - `vkGetBufferMemoryRequirements`
1548 - `vmaFindMemoryTypeIndex`
1549 - `vkDestroyBuffer`
1550 */
1551 VkResult vmaFindMemoryTypeIndexForBufferInfo(
1552 VmaAllocator allocator,
1553 const VkBufferCreateInfo* pBufferCreateInfo,
1554 const VmaAllocationCreateInfo* pAllocationCreateInfo,
1555 uint32_t* pMemoryTypeIndex);
1556
1557 /**
1558 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1559
1560 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1561 It internally creates a temporary, dummy image that never has memory bound.
1562 It is just a convenience function, equivalent to calling:
1563
1564 - `vkCreateImage`
1565 - `vkGetImageMemoryRequirements`
1566 - `vmaFindMemoryTypeIndex`
1567 - `vkDestroyImage`
1568 */
1569 VkResult vmaFindMemoryTypeIndexForImageInfo(
1570 VmaAllocator allocator,
1571 const VkImageCreateInfo* pImageCreateInfo,
1572 const VmaAllocationCreateInfo* pAllocationCreateInfo,
1573 uint32_t* pMemoryTypeIndex);
1574
1575 /// Flags to be passed as VmaPoolCreateInfo::flags.
1576 typedef enum VmaPoolCreateFlagBits {
1577 /** \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.
1578
1579 This is na optional optimization flag.
1580
1581 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
1582 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
1583 knows exact type of your allocations so it can handle Buffer-Image Granularity
1584 in the optimal way.
1585
1586 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
1587 exact type of such allocations is not known, so allocator must be conservative
1588 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
1589 (wasted memory). In that case, if you can make sure you always allocate only
1590 buffers and linear images or only optimal images out of this pool, use this flag
1591 to make allocator disregard Buffer-Image Granularity and so make allocations
1592 more optimal.
1593 */
1594 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
1595
1596 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
1597 } VmaPoolCreateFlagBits;
1598 typedef VkFlags VmaPoolCreateFlags;
1599
1600 /** \brief Describes parameter of created #VmaPool.
1601 */
1602 typedef struct VmaPoolCreateInfo {
1603 /** \brief Vulkan memory type index to allocate this pool from.
1604 */
1605 uint32_t memoryTypeIndex;
1606 /** \brief Use combination of #VmaPoolCreateFlagBits.
1607 */
1608 VmaPoolCreateFlags flags;
1609 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes.
1610
1611 Optional. Leave 0 to use default.
1612 */
1613 VkDeviceSize blockSize;
1614 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1615
1616 Set to 0 to have no preallocated blocks and let the pool be completely empty.
1617 */
1618 size_t minBlockCount;
1619 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1620
1621 Optional. Set to 0 to use `SIZE_MAX`, which means no limit.
1622
1623 Set to same value as minBlockCount to have fixed amount of memory allocated
1624 throuout whole lifetime of this pool.
1625 */
1626 size_t maxBlockCount;
1627 /** \brief Maximum number of additional frames that are in use at the same time as current frame.
1628
1629 This value is used only when you make allocations with
1630 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
1631 lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
1632
1633 For example, if you double-buffer your command buffers, so resources used for
1634 rendering in previous frame may still be in use by the GPU at the moment you
1635 allocate resources needed for the current frame, set this value to 1.
1636
1637 If you want to allow any allocations other than used in the current frame to
1638 become lost, set this value to 0.
1639 */
1640 uint32_t frameInUseCount;
1641 } VmaPoolCreateInfo;
1642
1643 /** \brief Describes parameter of existing #VmaPool.
1644 */
1645 typedef struct VmaPoolStats {
1646 /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
1647 */
1648 VkDeviceSize size;
1649 /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
1650 */
1651 VkDeviceSize unusedSize;
1652 /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
1653 */
1654 size_t allocationCount;
1655 /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
1656 */
1657 size_t unusedRangeCount;
1658 /** \brief Size of the largest continuous free memory region.
1659
1660 Making a new allocation of that size is not guaranteed to succeed because of
1661 possible additional margin required to respect alignment and buffer/image
1662 granularity.
1663 */
1664 VkDeviceSize unusedRangeSizeMax;
1665 } VmaPoolStats;
1666
1667 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
1668
1669 @param allocator Allocator object.
1670 @param pCreateInfo Parameters of pool to create.
1671 @param[out] pPool Handle to created pool.
1672 */
1673 VkResult vmaCreatePool(
1674 VmaAllocator allocator,
1675 const VmaPoolCreateInfo* pCreateInfo,
1676 VmaPool* pPool);
1677
1678 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
1679 */
1680 void vmaDestroyPool(
1681 VmaAllocator allocator,
1682 VmaPool pool);
1683
1684 /** \brief Retrieves statistics of existing #VmaPool object.
1685
1686 @param allocator Allocator object.
1687 @param pool Pool object.
1688 @param[out] pPoolStats Statistics of specified pool.
1689 */
1690 void vmaGetPoolStats(
1691 VmaAllocator allocator,
1692 VmaPool pool,
1693 VmaPoolStats* pPoolStats);
1694
1695 /** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
1696
1697 @param allocator Allocator object.
1698 @param pool Pool.
1699 @param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
1700 */
1701 void vmaMakePoolAllocationsLost(
1702 VmaAllocator allocator,
1703 VmaPool pool,
1704 size_t* pLostAllocationCount);
1705
1706 /** \struct VmaAllocation
1707 \brief Represents single memory allocation.
1708
1709 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
1710 plus unique offset.
1711
1712 There are multiple ways to create such object.
1713 You need to fill structure VmaAllocationCreateInfo.
1714 For more information see [Choosing memory type](@ref choosing_memory_type).
1715
1716 Although the library provides convenience functions that create Vulkan buffer or image,
1717 allocate memory for it and bind them together,
1718 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
1719 Allocation object can exist without buffer/image bound,
1720 binding can be done manually by the user, and destruction of it can be done
1721 independently of destruction of the allocation.
1722
1723 The object also remembers its size and some other information.
1724 To retrieve this information, use function vmaGetAllocationInfo() and inspect
1725 returned structure VmaAllocationInfo.
1726
1727 Some kinds allocations can be in lost state.
1728 For more information, see [Lost allocations](@ref lost_allocations).
1729 */
1730 VK_DEFINE_HANDLE(VmaAllocation)
1731
1732 /** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1733 */
1734 typedef struct VmaAllocationInfo {
1735 /** \brief Memory type index that this allocation was allocated from.
1736
1737 It never changes.
1738 */
1739 uint32_t memoryType;
1740 /** \brief Handle to Vulkan memory object.
1741
1742 Same memory object can be shared by multiple allocations.
1743
1744 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1745
1746 If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
1747 */
1748 VkDeviceMemory deviceMemory;
1749 /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
1750
1751 It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
1752 */
1753 VkDeviceSize offset;
1754 /** \brief Size of this allocation, in bytes.
1755
1756 It never changes, unless allocation is lost.
1757 */
1758 VkDeviceSize size;
1759 /** \brief Pointer to the beginning of this allocation as mapped data.
1760
1761 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1762 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
1763
1764 It can change after call to vmaMapMemory(), vmaUnmapMemory().
1765 It can also change after call to vmaDefragment() if this allocation is passed to the function.
1766 */
1767 void* pMappedData;
1768 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1769
1770 It can change after call to vmaSetAllocationUserData() for this allocation.
1771 */
1772 void* pUserData;
1773 } VmaAllocationInfo;
1774
1775 /** \brief General purpose memory allocation.
1776
1777 @param[out] pAllocation Handle to allocated memory.
1778 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1779
1780 You should free the memory using vmaFreeMemory().
1781
1782 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1783 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1784 */
1785 VkResult vmaAllocateMemory(
1786 VmaAllocator allocator,
1787 const VkMemoryRequirements* pVkMemoryRequirements,
1788 const VmaAllocationCreateInfo* pCreateInfo,
1789 VmaAllocation* pAllocation,
1790 VmaAllocationInfo* pAllocationInfo);
1791
1792 /**
1793 @param[out] pAllocation Handle to allocated memory.
1794 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1795
1796 You should free the memory using vmaFreeMemory().
1797 */
1798 VkResult vmaAllocateMemoryForBuffer(
1799 VmaAllocator allocator,
1800 VkBuffer buffer,
1801 const VmaAllocationCreateInfo* pCreateInfo,
1802 VmaAllocation* pAllocation,
1803 VmaAllocationInfo* pAllocationInfo);
1804
1805 /// Function similar to vmaAllocateMemoryForBuffer().
1806 VkResult vmaAllocateMemoryForImage(
1807 VmaAllocator allocator,
1808 VkImage image,
1809 const VmaAllocationCreateInfo* pCreateInfo,
1810 VmaAllocation* pAllocation,
1811 VmaAllocationInfo* pAllocationInfo);
1812
1813 /// Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1814 void vmaFreeMemory(
1815 VmaAllocator allocator,
1816 VmaAllocation allocation);
1817
1818 /** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
1819
1820 Current paramters of given allocation are returned in `pAllocationInfo`.
1821
1822 This function also atomically "touches" allocation - marks it as used in current frame,
1823 just like vmaTouchAllocation().
1824 If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
1825
1826 Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
1827 you can avoid calling it too often.
1828
1829 - You can retrieve same VmaAllocationInfo structure while creating your resource, from function
1830 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
1831 (e.g. due to defragmentation or allocation becoming lost).
1832 - If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
1833 */
1834 void vmaGetAllocationInfo(
1835 VmaAllocator allocator,
1836 VmaAllocation allocation,
1837 VmaAllocationInfo* pAllocationInfo);
1838
1839 /** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
1840
1841 If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1842 this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
1843 It then also atomically "touches" the allocation - marks it as used in current frame,
1844 so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
1845
1846 If the allocation is in lost state, the function returns `VK_FALSE`.
1847 Memory of such allocation, as well as buffer or image bound to it, should not be used.
1848 Lost allocation and the buffer/image still need to be destroyed.
1849
1850 If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
1851 this function always returns `VK_TRUE`.
1852 */
1853 VkBool32 vmaTouchAllocation(
1854 VmaAllocator allocator,
1855 VmaAllocation allocation);
1856
1857 /** \brief Sets pUserData in given allocation to new value.
1858
1859 If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
1860 pUserData must be either null, or pointer to a null-terminated string. The function
1861 makes local copy of the string and sets it as allocation's `pUserData`. String
1862 passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
1863 you can free it after this call. String previously pointed by allocation's
1864 pUserData is freed from memory.
1865
1866 If the flag was not used, the value of pointer `pUserData` is just copied to
1867 allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
1868 as a pointer, ordinal number or some handle to you own data.
1869 */
1870 void vmaSetAllocationUserData(
1871 VmaAllocator allocator,
1872 VmaAllocation allocation,
1873 void* pUserData);
1874
1875 /** \brief Creates new allocation that is in lost state from the beginning.
1876
1877 It can be useful if you need a dummy, non-null allocation.
1878
1879 You still need to destroy created object using vmaFreeMemory().
1880
1881 Returned allocation is not tied to any specific memory pool or memory type and
1882 not bound to any image or buffer. It has size = 0. It cannot be turned into
1883 a real, non-empty allocation.
1884 */
1885 void vmaCreateLostAllocation(
1886 VmaAllocator allocator,
1887 VmaAllocation* pAllocation);
1888
1889 /** \brief Maps memory represented by given allocation and returns pointer to it.
1890
1891 Maps memory represented by given allocation to make it accessible to CPU code.
1892 When succeeded, `*ppData` contains pointer to first byte of this memory.
1893 If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
1894 correctly offseted to the beginning of region assigned to this particular
1895 allocation.
1896
1897 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
1898 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
1899 multiple times simultaneously, it is safe to call this function on allocations
1900 assigned to the same memory block. Actual Vulkan memory will be mapped on first
1901 mapping and unmapped on last unmapping.
1902
1903 If the function succeeded, you must call vmaUnmapMemory() to unmap the
1904 allocation when mapping is no longer needed or before freeing the allocation, at
1905 the latest.
1906
1907 It also safe to call this function multiple times on the same allocation. You
1908 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
1909
1910 It is also safe to call this function on allocation created with
1911 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
1912 You must still call vmaUnmapMemory() same number of times as you called
1913 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
1914 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
1915
1916 This function fails when used on allocation made in memory type that is not
1917 `HOST_VISIBLE`.
1918
1919 This function always fails when called for allocation that was created with
1920 #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
1921 mapped.
1922 */
1923 VkResult vmaMapMemory(
1924 VmaAllocator allocator,
1925 VmaAllocation allocation,
1926 void** ppData);
1927
1928 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
1929
1930 For details, see description of vmaMapMemory().
1931 */
1932 void vmaUnmapMemory(
1933 VmaAllocator allocator,
1934 VmaAllocation allocation);
1935
1936 /** \brief Optional configuration parameters to be passed to function vmaDefragment(). */
1937 typedef struct VmaDefragmentationInfo {
1938 /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
1939
1940 Default is `VK_WHOLE_SIZE`, which means no limit.
1941 */
1942 VkDeviceSize maxBytesToMove;
1943 /** \brief Maximum number of allocations that can be moved to different place.
1944
1945 Default is `UINT32_MAX`, which means no limit.
1946 */
1947 uint32_t maxAllocationsToMove;
1948 } VmaDefragmentationInfo;
1949
1950 /** \brief Statistics returned by function vmaDefragment(). */
1951 typedef struct VmaDefragmentationStats {
1952 /// Total number of bytes that have been copied while moving allocations to different places.
1953 VkDeviceSize bytesMoved;
1954 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1955 VkDeviceSize bytesFreed;
1956 /// Number of allocations that have been moved to different places.
1957 uint32_t allocationsMoved;
1958 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
1959 uint32_t deviceMemoryBlocksFreed;
1960 } VmaDefragmentationStats;
1961
1962 /** \brief Compacts memory by moving allocations.
1963
1964 @param pAllocations Array of allocations that can be moved during this compation.
1965 @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
1966 @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.
1967 @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
1968 @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
1969 @return VK_SUCCESS if completed, VK_INCOMPLETE if succeeded but didn't make all possible optimizations because limits specified in pDefragmentationInfo have been reached, negative error code in case of error.
1970
1971 This function works by moving allocations to different places (different
1972 `VkDeviceMemory` objects and/or different offsets) in order to optimize memory
1973 usage. Only allocations that are in pAllocations array can be moved. All other
1974 allocations are considered nonmovable in this call. Basic rules:
1975
1976 - Only allocations made in memory types that have
1977 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag can be compacted. You may pass other
1978 allocations but it makes no sense - these will never be moved.
1979 - You may pass allocations made with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT but
1980 it makes no sense - they will never be moved.
1981 - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
1982 flag can be compacted. If not persistently mapped, memory will be mapped
1983 temporarily inside this function if needed.
1984 - You must not pass same #VmaAllocation object multiple times in pAllocations array.
1985
1986 The function also frees empty `VkDeviceMemory` blocks.
1987
1988 After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
1989 VmaAllocationInfo::offset changes. You must query them again using
1990 vmaGetAllocationInfo() if you need them.
1991
1992 If an allocation has been moved, data in memory is copied to new place
1993 automatically, but if it was bound to a buffer or an image, you must destroy
1994 that object yourself, create new one and bind it to the new memory pointed by
1995 the allocation. You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
1996 `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
1997 vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage()! Example:
1998
1999 \code
2000 VkDevice device = ...;
2001 VmaAllocator allocator = ...;
2002 std::vector<VkBuffer> buffers = ...;
2003 std::vector<VmaAllocation> allocations = ...;
2004
2005 std::vector<VkBool32> allocationsChanged(allocations.size());
2006 vmaDefragment(allocator, allocations.data(), allocations.size(), allocationsChanged.data(), nullptr, nullptr);
2007
2008 for(size_t i = 0; i < allocations.size(); ++i)
2009 {
2010 if(allocationsChanged[i])
2011 {
2012 VmaAllocationInfo allocInfo;
2013 vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
2014
2015 vkDestroyBuffer(device, buffers[i], nullptr);
2016
2017 VkBufferCreateInfo bufferInfo = ...;
2018 vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
2019
2020 // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
2021
2022 vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
2023 }
2024 }
2025 \endcode
2026
2027 Note: Please don't expect memory to be fully compacted after this call.
2028 Algorithms inside are based on some heuristics that try to maximize number of Vulkan
2029 memory blocks to make totally empty to release them, as well as to maximimze continuous
2030 empty space inside remaining blocks, while minimizing the number and size of data that
2031 needs to be moved. Some fragmentation still remains after this call. This is normal.
2032
2033 Warning: This function is not 100% correct according to Vulkan specification. Use it
2034 at your own risk. That's because Vulkan doesn't guarantee that memory
2035 requirements (size and alignment) for a new buffer or image are consistent. They
2036 may be different even for subsequent calls with the same parameters. It really
2037 does happen on some platforms, especially with images.
2038
2039 Warning: This function may be time-consuming, so you shouldn't call it too often
2040 (like every frame or after every resource creation/destruction).
2041 You can call it on special occasions (like when reloading a game level or
2042 when you just destroyed a lot of objects).
2043 */
2044 VkResult vmaDefragment(
2045 VmaAllocator allocator,
2046 VmaAllocation* pAllocations,
2047 size_t allocationCount,
2048 VkBool32* pAllocationsChanged,
2049 const VmaDefragmentationInfo *pDefragmentationInfo,
2050 VmaDefragmentationStats* pDefragmentationStats);
2051
2052 /** \brief Binds buffer to allocation.
2053
2054 Binds specified buffer to region of memory represented by specified allocation.
2055 Gets `VkDeviceMemory` handle and offset from the allocation.
2056 If you want to create a buffer, allocate memory for it and bind them together separately,
2057 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2058 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2059 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2060 (which is illegal in Vulkan).
2061
2062 It is recommended to use function vmaCreateBuffer() instead of this one.
2063 */
2064 VkResult vmaBindBufferMemory(
2065 VmaAllocator allocator,
2066 VmaAllocation allocation,
2067 VkBuffer buffer);
2068
2069 /** \brief Binds image to allocation.
2070
2071 Binds specified image to region of memory represented by specified allocation.
2072 Gets `VkDeviceMemory` handle and offset from the allocation.
2073 If you want to create an image, allocate memory for it and bind them together separately,
2074 you should use this function for binding instead of standard `vkBindImageMemory()`,
2075 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2076 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2077 (which is illegal in Vulkan).
2078
2079 It is recommended to use function vmaCreateImage() instead of this one.
2080 */
2081 VkResult vmaBindImageMemory(
2082 VmaAllocator allocator,
2083 VmaAllocation allocation,
2084 VkImage image);
2085
2086 /**
2087 @param[out] pBuffer Buffer that was created.
2088 @param[out] pAllocation Allocation that was created.
2089 @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2090
2091 This function automatically:
2092
2093 -# Creates buffer.
2094 -# Allocates appropriate memory for it.
2095 -# Binds the buffer with the memory.
2096
2097 If any of these operations fail, buffer and allocation are not created,
2098 returned value is negative error code, *pBuffer and *pAllocation are null.
2099
2100 If the function succeeded, you must destroy both buffer and allocation when you
2101 no longer need them using either convenience function vmaDestroyBuffer() or
2102 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2103
2104 If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2105 VK_KHR_dedicated_allocation extension is used internally to query driver whether
2106 it requires or prefers the new buffer to have dedicated allocation. If yes,
2107 and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
2108 and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2109 allocation for this buffer, just like when using
2110 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2111 */
2112 VkResult vmaCreateBuffer(
2113 VmaAllocator allocator,
2114 const VkBufferCreateInfo* pBufferCreateInfo,
2115 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2116 VkBuffer* pBuffer,
2117 VmaAllocation* pAllocation,
2118 VmaAllocationInfo* pAllocationInfo);
2119
2120 /** \brief Destroys Vulkan buffer and frees allocated memory.
2121
2122 This is just a convenience function equivalent to:
2123
2124 \code
2125 vkDestroyBuffer(device, buffer, allocationCallbacks);
2126 vmaFreeMemory(allocator, allocation);
2127 \endcode
2128
2129 It it safe to pass null as buffer and/or allocation.
2130 */
2131 void vmaDestroyBuffer(
2132 VmaAllocator allocator,
2133 VkBuffer buffer,
2134 VmaAllocation allocation);
2135
2136 /// Function similar to vmaCreateBuffer().
2137 VkResult vmaCreateImage(
2138 VmaAllocator allocator,
2139 const VkImageCreateInfo* pImageCreateInfo,
2140 const VmaAllocationCreateInfo* pAllocationCreateInfo,
2141 VkImage* pImage,
2142 VmaAllocation* pAllocation,
2143 VmaAllocationInfo* pAllocationInfo);
2144
2145 /** \brief Destroys Vulkan image and frees allocated memory.
2146
2147 This is just a convenience function equivalent to:
2148
2149 \code
2150 vkDestroyImage(device, image, allocationCallbacks);
2151 vmaFreeMemory(allocator, allocation);
2152 \endcode
2153
2154 It it safe to pass null as image and/or allocation.
2155 */
2156 void vmaDestroyImage(
2157 VmaAllocator allocator,
2158 VkImage image,
2159 VmaAllocation allocation);
2160
2161 #ifdef __cplusplus
2162 }
2163 #endif
2164
2165 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2166
2167 // For Visual Studio IntelliSense.
2168 #ifdef __INTELLISENSE__
2169 #define VMA_IMPLEMENTATION
2170 #endif
2171
2172 #ifdef VMA_IMPLEMENTATION
2173 #undef VMA_IMPLEMENTATION
2174
2175 #include <cstdint>
2176 #include <cstdlib>
2177 #include <cstring>
2178
2179 /*******************************************************************************
2180 CONFIGURATION SECTION
2181
2182 Define some of these macros before each #include of this header or change them
2183 here if you need other then default behavior depending on your environment.
2184 */
2185
2186 /*
2187 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2188 internally, like:
2189
2190 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2191
2192 Define to 0 if you are going to provide you own pointers to Vulkan functions via
2193 VmaAllocatorCreateInfo::pVulkanFunctions.
2194 */
2195 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2196 #define VMA_STATIC_VULKAN_FUNCTIONS 1
2197 #endif
2198
2199 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
2200 //#define VMA_USE_STL_CONTAINERS 1
2201
2202 /* Set this macro to 1 to make the library including and using STL containers:
2203 std::pair, std::vector, std::list, std::unordered_map.
2204
2205 Set it to 0 or undefined to make the library using its own implementation of
2206 the containers.
2207 */
2208 #if VMA_USE_STL_CONTAINERS
2209 #define VMA_USE_STL_VECTOR 1
2210 #define VMA_USE_STL_UNORDERED_MAP 1
2211 #define VMA_USE_STL_LIST 1
2212 #endif
2213
2214 #if VMA_USE_STL_VECTOR
2215 #include <vector>
2216 #endif
2217
2218 #if VMA_USE_STL_UNORDERED_MAP
2219 #include <unordered_map>
2220 #endif
2221
2222 #if VMA_USE_STL_LIST
2223 #include <list>
2224 #endif
2225
2226 /*
2227 Following headers are used in this CONFIGURATION section only, so feel free to
2228 remove them if not needed.
2229 */
2230 #include <cassert> // for assert
2231 #include <algorithm> // for min, max
2232 #include <mutex> // for std::mutex
2233 #include <atomic> // for std::atomic
2234
2235 #if !defined(_WIN32) && !defined(__APPLE__)
2236 #include <malloc.h> // for aligned_alloc()
2237 #endif
2238
2239 #ifndef VMA_NULL
2240 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2241 #define VMA_NULL nullptr
2242 #endif
2243
2244 #if defined(__APPLE__) || defined(__ANDROID__)
2245 #include <cstdlib>
aligned_alloc(size_t alignment,size_t size)2246 void *aligned_alloc(size_t alignment, size_t size)
2247 {
2248 // alignment must be >= sizeof(void*)
2249 if(alignment < sizeof(void*))
2250 {
2251 alignment = sizeof(void*);
2252 }
2253
2254 void *pointer;
2255 if(posix_memalign(&pointer, alignment, size) == 0)
2256 return pointer;
2257 return VMA_NULL;
2258 }
2259 #endif
2260
2261 // Normal assert to check for programmer's errors, especially in Debug configuration.
2262 #ifndef VMA_ASSERT
2263 #ifdef _DEBUG
2264 #define VMA_ASSERT(expr) assert(expr)
2265 #else
2266 #define VMA_ASSERT(expr)
2267 #endif
2268 #endif
2269
2270 // Assert that will be called very often, like inside data structures e.g. operator[].
2271 // Making it non-empty can make program slow.
2272 #ifndef VMA_HEAVY_ASSERT
2273 #ifdef _DEBUG
2274 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
2275 #else
2276 #define VMA_HEAVY_ASSERT(expr)
2277 #endif
2278 #endif
2279
2280 #ifndef VMA_ALIGN_OF
2281 #define VMA_ALIGN_OF(type) (__alignof(type))
2282 #endif
2283
2284 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
2285 #if defined(_WIN32)
2286 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
2287 #else
2288 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
2289 #endif
2290 #endif
2291
2292 #ifndef VMA_SYSTEM_FREE
2293 #if defined(_WIN32)
2294 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
2295 #else
2296 #define VMA_SYSTEM_FREE(ptr) free(ptr)
2297 #endif
2298 #endif
2299
2300 #ifndef VMA_MIN
2301 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
2302 #endif
2303
2304 #ifndef VMA_MAX
2305 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
2306 #endif
2307
2308 #ifndef VMA_SWAP
2309 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
2310 #endif
2311
2312 #ifndef VMA_SORT
2313 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
2314 #endif
2315
2316 #ifndef VMA_DEBUG_LOG
2317 #define VMA_DEBUG_LOG(format, ...)
2318 /*
2319 #define VMA_DEBUG_LOG(format, ...) do { \
2320 printf(format, __VA_ARGS__); \
2321 printf("\n"); \
2322 } while(false)
2323 */
2324 #endif
2325
2326 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
2327 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * outStr,size_t strLen,uint32_t num)2328 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
2329 {
2330 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
2331 }
VmaUint64ToStr(char * outStr,size_t strLen,uint64_t num)2332 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
2333 {
2334 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
2335 }
VmaPtrToStr(char * outStr,size_t strLen,const void * ptr)2336 static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
2337 {
2338 snprintf(outStr, strLen, "%p", ptr);
2339 }
2340 #endif
2341
2342 #ifndef VMA_MUTEX
2343 class VmaMutex
2344 {
2345 public:
VmaMutex()2346 VmaMutex() { }
~VmaMutex()2347 ~VmaMutex() { }
Lock()2348 void Lock() { m_Mutex.lock(); }
Unlock()2349 void Unlock() { m_Mutex.unlock(); }
2350 private:
2351 std::mutex m_Mutex;
2352 };
2353 #define VMA_MUTEX VmaMutex
2354 #endif
2355
2356 /*
2357 If providing your own implementation, you need to implement a subset of std::atomic:
2358
2359 - Constructor(uint32_t desired)
2360 - uint32_t load() const
2361 - void store(uint32_t desired)
2362 - bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
2363 */
2364 #ifndef VMA_ATOMIC_UINT32
2365 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
2366 #endif
2367
2368 #ifndef VMA_BEST_FIT
2369 /**
2370 Main parameter for function assessing how good is a free suballocation for a new
2371 allocation request.
2372
2373 - Set to 1 to use Best-Fit algorithm - prefer smaller blocks, as close to the
2374 size of requested allocations as possible.
2375 - Set to 0 to use Worst-Fit algorithm - prefer larger blocks, as large as
2376 possible.
2377
2378 Experiments in special testing environment showed that Best-Fit algorithm is
2379 better.
2380 */
2381 #define VMA_BEST_FIT (1)
2382 #endif
2383
2384 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
2385 /**
2386 Every allocation will have its own memory block.
2387 Define to 1 for debugging purposes only.
2388 */
2389 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
2390 #endif
2391
2392 #ifndef VMA_DEBUG_ALIGNMENT
2393 /**
2394 Minimum alignment of all suballocations, in bytes.
2395 Set to more than 1 for debugging purposes only. Must be power of two.
2396 */
2397 #define VMA_DEBUG_ALIGNMENT (1)
2398 #endif
2399
2400 #ifndef VMA_DEBUG_MARGIN
2401 /**
2402 Minimum margin between suballocations, in bytes.
2403 Set nonzero for debugging purposes only.
2404 */
2405 #define VMA_DEBUG_MARGIN (0)
2406 #endif
2407
2408 #ifndef VMA_DEBUG_GLOBAL_MUTEX
2409 /**
2410 Set this to 1 for debugging purposes only, to enable single mutex protecting all
2411 entry calls to the library. Can be useful for debugging multithreading issues.
2412 */
2413 #define VMA_DEBUG_GLOBAL_MUTEX (0)
2414 #endif
2415
2416 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
2417 /**
2418 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
2419 Set to more than 1 for debugging purposes only. Must be power of two.
2420 */
2421 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
2422 #endif
2423
2424 #ifndef VMA_SMALL_HEAP_MAX_SIZE
2425 /// Maximum size of a memory heap in Vulkan to consider it "small".
2426 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
2427 #endif
2428
2429 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
2430 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
2431 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
2432 #endif
2433
2434 static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
2435
2436 /*******************************************************************************
2437 END OF CONFIGURATION
2438 */
2439
2440 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
2441 VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
2442
2443 // Returns number of bits set to 1 in (v).
VmaCountBitsSet(uint32_t v)2444 static inline uint32_t VmaCountBitsSet(uint32_t v)
2445 {
2446 uint32_t c = v - ((v >> 1) & 0x55555555);
2447 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
2448 c = ((c >> 4) + c) & 0x0F0F0F0F;
2449 c = ((c >> 8) + c) & 0x00FF00FF;
2450 c = ((c >> 16) + c) & 0x0000FFFF;
2451 return c;
2452 }
2453
2454 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
2455 // Use types like uint32_t, uint64_t as T.
2456 template <typename T>
VmaAlignUp(T val,T align)2457 static inline T VmaAlignUp(T val, T align)
2458 {
2459 return (val + align - 1) / align * align;
2460 }
2461
2462 // Division with mathematical rounding to nearest number.
2463 template <typename T>
VmaRoundDiv(T x,T y)2464 inline T VmaRoundDiv(T x, T y)
2465 {
2466 return (x + (y / (T)2)) / y;
2467 }
2468
2469 #ifndef VMA_SORT
2470
2471 template<typename Iterator, typename Compare>
VmaQuickSortPartition(Iterator beg,Iterator end,Compare cmp)2472 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
2473 {
2474 Iterator centerValue = end; --centerValue;
2475 Iterator insertIndex = beg;
2476 for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
2477 {
2478 if(cmp(*memTypeIndex, *centerValue))
2479 {
2480 if(insertIndex != memTypeIndex)
2481 {
2482 VMA_SWAP(*memTypeIndex, *insertIndex);
2483 }
2484 ++insertIndex;
2485 }
2486 }
2487 if(insertIndex != centerValue)
2488 {
2489 VMA_SWAP(*insertIndex, *centerValue);
2490 }
2491 return insertIndex;
2492 }
2493
2494 template<typename Iterator, typename Compare>
VmaQuickSort(Iterator beg,Iterator end,Compare cmp)2495 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
2496 {
2497 if(beg < end)
2498 {
2499 Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
2500 VmaQuickSort<Iterator, Compare>(beg, it, cmp);
2501 VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
2502 }
2503 }
2504
2505 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
2506
2507 #endif // #ifndef VMA_SORT
2508
2509 /*
2510 Returns true if two memory blocks occupy overlapping pages.
2511 ResourceA must be in less memory offset than ResourceB.
2512
2513 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
2514 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
2515 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)2516 static inline bool VmaBlocksOnSamePage(
2517 VkDeviceSize resourceAOffset,
2518 VkDeviceSize resourceASize,
2519 VkDeviceSize resourceBOffset,
2520 VkDeviceSize pageSize)
2521 {
2522 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
2523 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
2524 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
2525 VkDeviceSize resourceBStart = resourceBOffset;
2526 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
2527 return resourceAEndPage == resourceBStartPage;
2528 }
2529
2530 enum VmaSuballocationType
2531 {
2532 VMA_SUBALLOCATION_TYPE_FREE = 0,
2533 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
2534 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
2535 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
2536 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
2537 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
2538 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
2539 };
2540
2541 /*
2542 Returns true if given suballocation types could conflict and must respect
2543 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
2544 or linear image and another one is optimal image. If type is unknown, behave
2545 conservatively.
2546 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)2547 static inline bool VmaIsBufferImageGranularityConflict(
2548 VmaSuballocationType suballocType1,
2549 VmaSuballocationType suballocType2)
2550 {
2551 if(suballocType1 > suballocType2)
2552 {
2553 VMA_SWAP(suballocType1, suballocType2);
2554 }
2555
2556 switch(suballocType1)
2557 {
2558 case VMA_SUBALLOCATION_TYPE_FREE:
2559 return false;
2560 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
2561 return true;
2562 case VMA_SUBALLOCATION_TYPE_BUFFER:
2563 return
2564 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
2565 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2566 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
2567 return
2568 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
2569 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
2570 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2571 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
2572 return
2573 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
2574 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
2575 return false;
2576 default:
2577 VMA_ASSERT(0);
2578 return true;
2579 }
2580 }
2581
2582 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
2583 struct VmaMutexLock
2584 {
2585 public:
VmaMutexLockVmaMutexLock2586 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
2587 m_pMutex(useMutex ? &mutex : VMA_NULL)
2588 {
2589 if(m_pMutex)
2590 {
2591 m_pMutex->Lock();
2592 }
2593 }
2594
~VmaMutexLockVmaMutexLock2595 ~VmaMutexLock()
2596 {
2597 if(m_pMutex)
2598 {
2599 m_pMutex->Unlock();
2600 }
2601 }
2602
2603 private:
2604 VMA_MUTEX* m_pMutex;
2605 };
2606
2607 #if VMA_DEBUG_GLOBAL_MUTEX
2608 static VMA_MUTEX gDebugGlobalMutex;
2609 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
2610 #else
2611 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
2612 #endif
2613
2614 // Minimum size of a free suballocation to register it in the free suballocation collection.
2615 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
2616
2617 /*
2618 Performs binary search and returns iterator to first element that is greater or
2619 equal to (key), according to comparison (cmp).
2620
2621 Cmp should return true if first argument is less than second argument.
2622
2623 Returned value is the found element, if present in the collection or place where
2624 new element with value (key) should be inserted.
2625 */
2626 template <typename IterT, typename KeyT, typename CmpT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,CmpT cmp)2627 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp)
2628 {
2629 size_t down = 0, up = (end - beg);
2630 while(down < up)
2631 {
2632 const size_t mid = (down + up) / 2;
2633 if(cmp(*(beg+mid), key))
2634 {
2635 down = mid + 1;
2636 }
2637 else
2638 {
2639 up = mid;
2640 }
2641 }
2642 return beg + down;
2643 }
2644
2645 ////////////////////////////////////////////////////////////////////////////////
2646 // Memory allocation
2647
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)2648 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
2649 {
2650 if((pAllocationCallbacks != VMA_NULL) &&
2651 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
2652 {
2653 return (*pAllocationCallbacks->pfnAllocation)(
2654 pAllocationCallbacks->pUserData,
2655 size,
2656 alignment,
2657 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
2658 }
2659 else
2660 {
2661 return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
2662 }
2663 }
2664
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)2665 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
2666 {
2667 if((pAllocationCallbacks != VMA_NULL) &&
2668 (pAllocationCallbacks->pfnFree != VMA_NULL))
2669 {
2670 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
2671 }
2672 else
2673 {
2674 VMA_SYSTEM_FREE(ptr);
2675 }
2676 }
2677
2678 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)2679 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
2680 {
2681 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
2682 }
2683
2684 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)2685 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
2686 {
2687 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
2688 }
2689
2690 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
2691
2692 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
2693
2694 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)2695 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
2696 {
2697 ptr->~T();
2698 VmaFree(pAllocationCallbacks, ptr);
2699 }
2700
2701 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)2702 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
2703 {
2704 if(ptr != VMA_NULL)
2705 {
2706 for(size_t i = count; i--; )
2707 {
2708 ptr[i].~T();
2709 }
2710 VmaFree(pAllocationCallbacks, ptr);
2711 }
2712 }
2713
2714 // STL-compatible allocator.
2715 template<typename T>
2716 class VmaStlAllocator
2717 {
2718 public:
2719 const VkAllocationCallbacks* const m_pCallbacks;
2720 typedef T value_type;
2721
VmaStlAllocator(const VkAllocationCallbacks * pCallbacks)2722 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
VmaStlAllocator(const VmaStlAllocator<U> & src)2723 template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
2724
allocate(size_t n)2725 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocate(T * p,size_t n)2726 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
2727
2728 template<typename U>
2729 bool operator==(const VmaStlAllocator<U>& rhs) const
2730 {
2731 return m_pCallbacks == rhs.m_pCallbacks;
2732 }
2733 template<typename U>
2734 bool operator!=(const VmaStlAllocator<U>& rhs) const
2735 {
2736 return m_pCallbacks != rhs.m_pCallbacks;
2737 }
2738
2739 VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
2740 };
2741
2742 #if VMA_USE_STL_VECTOR
2743
2744 #define VmaVector std::vector
2745
2746 template<typename T, typename allocatorT>
VmaVectorInsert(std::vector<T,allocatorT> & vec,size_t index,const T & item)2747 static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
2748 {
2749 vec.insert(vec.begin() + index, item);
2750 }
2751
2752 template<typename T, typename allocatorT>
VmaVectorRemove(std::vector<T,allocatorT> & vec,size_t index)2753 static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
2754 {
2755 vec.erase(vec.begin() + index);
2756 }
2757
2758 #else // #if VMA_USE_STL_VECTOR
2759
2760 /* Class with interface compatible with subset of std::vector.
2761 T must be POD because constructors and destructors are not called and memcpy is
2762 used for these objects. */
2763 template<typename T, typename AllocatorT>
2764 class VmaVector
2765 {
2766 public:
2767 typedef T value_type;
2768
VmaVector(const AllocatorT & allocator)2769 VmaVector(const AllocatorT& allocator) :
2770 m_Allocator(allocator),
2771 m_pArray(VMA_NULL),
2772 m_Count(0),
2773 m_Capacity(0)
2774 {
2775 }
2776
VmaVector(size_t count,const AllocatorT & allocator)2777 VmaVector(size_t count, const AllocatorT& allocator) :
2778 m_Allocator(allocator),
2779 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
2780 m_Count(count),
2781 m_Capacity(count)
2782 {
2783 }
2784
VmaVector(const VmaVector<T,AllocatorT> & src)2785 VmaVector(const VmaVector<T, AllocatorT>& src) :
2786 m_Allocator(src.m_Allocator),
2787 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
2788 m_Count(src.m_Count),
2789 m_Capacity(src.m_Count)
2790 {
2791 if(m_Count != 0)
2792 {
2793 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
2794 }
2795 }
2796
~VmaVector()2797 ~VmaVector()
2798 {
2799 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2800 }
2801
2802 VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
2803 {
2804 if(&rhs != this)
2805 {
2806 resize(rhs.m_Count);
2807 if(m_Count != 0)
2808 {
2809 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
2810 }
2811 }
2812 return *this;
2813 }
2814
empty()2815 bool empty() const { return m_Count == 0; }
size()2816 size_t size() const { return m_Count; }
data()2817 T* data() { return m_pArray; }
data()2818 const T* data() const { return m_pArray; }
2819
2820 T& operator[](size_t index)
2821 {
2822 VMA_HEAVY_ASSERT(index < m_Count);
2823 return m_pArray[index];
2824 }
2825 const T& operator[](size_t index) const
2826 {
2827 VMA_HEAVY_ASSERT(index < m_Count);
2828 return m_pArray[index];
2829 }
2830
front()2831 T& front()
2832 {
2833 VMA_HEAVY_ASSERT(m_Count > 0);
2834 return m_pArray[0];
2835 }
front()2836 const T& front() const
2837 {
2838 VMA_HEAVY_ASSERT(m_Count > 0);
2839 return m_pArray[0];
2840 }
back()2841 T& back()
2842 {
2843 VMA_HEAVY_ASSERT(m_Count > 0);
2844 return m_pArray[m_Count - 1];
2845 }
back()2846 const T& back() const
2847 {
2848 VMA_HEAVY_ASSERT(m_Count > 0);
2849 return m_pArray[m_Count - 1];
2850 }
2851
2852 void reserve(size_t newCapacity, bool freeMemory = false)
2853 {
2854 newCapacity = VMA_MAX(newCapacity, m_Count);
2855
2856 if((newCapacity < m_Capacity) && !freeMemory)
2857 {
2858 newCapacity = m_Capacity;
2859 }
2860
2861 if(newCapacity != m_Capacity)
2862 {
2863 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
2864 if(m_Count != 0)
2865 {
2866 memcpy(newArray, m_pArray, m_Count * sizeof(T));
2867 }
2868 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2869 m_Capacity = newCapacity;
2870 m_pArray = newArray;
2871 }
2872 }
2873
2874 void resize(size_t newCount, bool freeMemory = false)
2875 {
2876 size_t newCapacity = m_Capacity;
2877 if(newCount > m_Capacity)
2878 {
2879 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
2880 }
2881 else if(freeMemory)
2882 {
2883 newCapacity = newCount;
2884 }
2885
2886 if(newCapacity != m_Capacity)
2887 {
2888 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
2889 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
2890 if(elementsToCopy != 0)
2891 {
2892 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
2893 }
2894 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
2895 m_Capacity = newCapacity;
2896 m_pArray = newArray;
2897 }
2898
2899 m_Count = newCount;
2900 }
2901
2902 void clear(bool freeMemory = false)
2903 {
2904 resize(0, freeMemory);
2905 }
2906
insert(size_t index,const T & src)2907 void insert(size_t index, const T& src)
2908 {
2909 VMA_HEAVY_ASSERT(index <= m_Count);
2910 const size_t oldCount = size();
2911 resize(oldCount + 1);
2912 if(index < oldCount)
2913 {
2914 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
2915 }
2916 m_pArray[index] = src;
2917 }
2918
remove(size_t index)2919 void remove(size_t index)
2920 {
2921 VMA_HEAVY_ASSERT(index < m_Count);
2922 const size_t oldCount = size();
2923 if(index < oldCount - 1)
2924 {
2925 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
2926 }
2927 resize(oldCount - 1);
2928 }
2929
push_back(const T & src)2930 void push_back(const T& src)
2931 {
2932 const size_t newIndex = size();
2933 resize(newIndex + 1);
2934 m_pArray[newIndex] = src;
2935 }
2936
pop_back()2937 void pop_back()
2938 {
2939 VMA_HEAVY_ASSERT(m_Count > 0);
2940 resize(size() - 1);
2941 }
2942
push_front(const T & src)2943 void push_front(const T& src)
2944 {
2945 insert(0, src);
2946 }
2947
pop_front()2948 void pop_front()
2949 {
2950 VMA_HEAVY_ASSERT(m_Count > 0);
2951 remove(0);
2952 }
2953
2954 typedef T* iterator;
2955
begin()2956 iterator begin() { return m_pArray; }
end()2957 iterator end() { return m_pArray + m_Count; }
2958
2959 private:
2960 AllocatorT m_Allocator;
2961 T* m_pArray;
2962 size_t m_Count;
2963 size_t m_Capacity;
2964 };
2965
2966 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)2967 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
2968 {
2969 vec.insert(index, item);
2970 }
2971
2972 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)2973 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
2974 {
2975 vec.remove(index);
2976 }
2977
2978 #endif // #if VMA_USE_STL_VECTOR
2979
2980 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)2981 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
2982 {
2983 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
2984 vector.data(),
2985 vector.data() + vector.size(),
2986 value,
2987 CmpLess()) - vector.data();
2988 VmaVectorInsert(vector, indexToInsert, value);
2989 return indexToInsert;
2990 }
2991
2992 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)2993 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
2994 {
2995 CmpLess comparator;
2996 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
2997 vector.begin(),
2998 vector.end(),
2999 value,
3000 comparator);
3001 if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
3002 {
3003 size_t indexToRemove = it - vector.begin();
3004 VmaVectorRemove(vector, indexToRemove);
3005 return true;
3006 }
3007 return false;
3008 }
3009
3010 template<typename CmpLess, typename VectorT>
VmaVectorFindSorted(const VectorT & vector,const typename VectorT::value_type & value)3011 size_t VmaVectorFindSorted(const VectorT& vector, const typename VectorT::value_type& value)
3012 {
3013 CmpLess comparator;
3014 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
3015 vector.data(),
3016 vector.data() + vector.size(),
3017 value,
3018 comparator);
3019 if(it != vector.size() && !comparator(*it, value) && !comparator(value, *it))
3020 {
3021 return it - vector.begin();
3022 }
3023 else
3024 {
3025 return vector.size();
3026 }
3027 }
3028
3029 ////////////////////////////////////////////////////////////////////////////////
3030 // class VmaPoolAllocator
3031
3032 /*
3033 Allocator for objects of type T using a list of arrays (pools) to speed up
3034 allocation. Number of elements that can be allocated is not bounded because
3035 allocator can create multiple blocks.
3036 */
3037 template<typename T>
3038 class VmaPoolAllocator
3039 {
3040 public:
3041 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
3042 ~VmaPoolAllocator();
3043 void Clear();
3044 T* Alloc();
3045 void Free(T* ptr);
3046
3047 private:
3048 union Item
3049 {
3050 uint32_t NextFreeIndex;
3051 T Value;
3052 };
3053
3054 struct ItemBlock
3055 {
3056 Item* pItems;
3057 uint32_t FirstFreeIndex;
3058 };
3059
3060 const VkAllocationCallbacks* m_pAllocationCallbacks;
3061 size_t m_ItemsPerBlock;
3062 VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
3063
3064 ItemBlock& CreateNewBlock();
3065 };
3066
3067 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,size_t itemsPerBlock)3068 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
3069 m_pAllocationCallbacks(pAllocationCallbacks),
3070 m_ItemsPerBlock(itemsPerBlock),
3071 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
3072 {
3073 VMA_ASSERT(itemsPerBlock > 0);
3074 }
3075
3076 template<typename T>
~VmaPoolAllocator()3077 VmaPoolAllocator<T>::~VmaPoolAllocator()
3078 {
3079 Clear();
3080 }
3081
3082 template<typename T>
Clear()3083 void VmaPoolAllocator<T>::Clear()
3084 {
3085 for(size_t i = m_ItemBlocks.size(); i--; )
3086 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
3087 m_ItemBlocks.clear();
3088 }
3089
3090 template<typename T>
Alloc()3091 T* VmaPoolAllocator<T>::Alloc()
3092 {
3093 for(size_t i = m_ItemBlocks.size(); i--; )
3094 {
3095 ItemBlock& block = m_ItemBlocks[i];
3096 // This block has some free items: Use first one.
3097 if(block.FirstFreeIndex != UINT32_MAX)
3098 {
3099 Item* const pItem = &block.pItems[block.FirstFreeIndex];
3100 block.FirstFreeIndex = pItem->NextFreeIndex;
3101 return &pItem->Value;
3102 }
3103 }
3104
3105 // No block has free item: Create new one and use it.
3106 ItemBlock& newBlock = CreateNewBlock();
3107 Item* const pItem = &newBlock.pItems[0];
3108 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
3109 return &pItem->Value;
3110 }
3111
3112 template<typename T>
Free(T * ptr)3113 void VmaPoolAllocator<T>::Free(T* ptr)
3114 {
3115 // Search all memory blocks to find ptr.
3116 for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
3117 {
3118 ItemBlock& block = m_ItemBlocks[i];
3119
3120 // Casting to union.
3121 Item* pItemPtr;
3122 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
3123
3124 // Check if pItemPtr is in address range of this block.
3125 if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
3126 {
3127 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
3128 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
3129 block.FirstFreeIndex = index;
3130 return;
3131 }
3132 }
3133 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
3134 }
3135
3136 template<typename T>
CreateNewBlock()3137 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
3138 {
3139 ItemBlock newBlock = {
3140 vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
3141
3142 m_ItemBlocks.push_back(newBlock);
3143
3144 // Setup singly-linked list of all free items in this block.
3145 for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
3146 newBlock.pItems[i].NextFreeIndex = i + 1;
3147 newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
3148 return m_ItemBlocks.back();
3149 }
3150
3151 ////////////////////////////////////////////////////////////////////////////////
3152 // class VmaRawList, VmaList
3153
3154 #if VMA_USE_STL_LIST
3155
3156 #define VmaList std::list
3157
3158 #else // #if VMA_USE_STL_LIST
3159
3160 template<typename T>
3161 struct VmaListItem
3162 {
3163 VmaListItem* pPrev;
3164 VmaListItem* pNext;
3165 T Value;
3166 };
3167
3168 // Doubly linked list.
3169 template<typename T>
3170 class VmaRawList
3171 {
3172 public:
3173 typedef VmaListItem<T> ItemType;
3174
3175 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
3176 ~VmaRawList();
3177 void Clear();
3178
GetCount()3179 size_t GetCount() const { return m_Count; }
IsEmpty()3180 bool IsEmpty() const { return m_Count == 0; }
3181
Front()3182 ItemType* Front() { return m_pFront; }
Front()3183 const ItemType* Front() const { return m_pFront; }
Back()3184 ItemType* Back() { return m_pBack; }
Back()3185 const ItemType* Back() const { return m_pBack; }
3186
3187 ItemType* PushBack();
3188 ItemType* PushFront();
3189 ItemType* PushBack(const T& value);
3190 ItemType* PushFront(const T& value);
3191 void PopBack();
3192 void PopFront();
3193
3194 // Item can be null - it means PushBack.
3195 ItemType* InsertBefore(ItemType* pItem);
3196 // Item can be null - it means PushFront.
3197 ItemType* InsertAfter(ItemType* pItem);
3198
3199 ItemType* InsertBefore(ItemType* pItem, const T& value);
3200 ItemType* InsertAfter(ItemType* pItem, const T& value);
3201
3202 void Remove(ItemType* pItem);
3203
3204 private:
3205 const VkAllocationCallbacks* const m_pAllocationCallbacks;
3206 VmaPoolAllocator<ItemType> m_ItemAllocator;
3207 ItemType* m_pFront;
3208 ItemType* m_pBack;
3209 size_t m_Count;
3210
3211 // Declared not defined, to block copy constructor and assignment operator.
3212 VmaRawList(const VmaRawList<T>& src);
3213 VmaRawList<T>& operator=(const VmaRawList<T>& rhs);
3214 };
3215
3216 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)3217 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
3218 m_pAllocationCallbacks(pAllocationCallbacks),
3219 m_ItemAllocator(pAllocationCallbacks, 128),
3220 m_pFront(VMA_NULL),
3221 m_pBack(VMA_NULL),
3222 m_Count(0)
3223 {
3224 }
3225
3226 template<typename T>
~VmaRawList()3227 VmaRawList<T>::~VmaRawList()
3228 {
3229 // Intentionally not calling Clear, because that would be unnecessary
3230 // computations to return all items to m_ItemAllocator as free.
3231 }
3232
3233 template<typename T>
Clear()3234 void VmaRawList<T>::Clear()
3235 {
3236 if(IsEmpty() == false)
3237 {
3238 ItemType* pItem = m_pBack;
3239 while(pItem != VMA_NULL)
3240 {
3241 ItemType* const pPrevItem = pItem->pPrev;
3242 m_ItemAllocator.Free(pItem);
3243 pItem = pPrevItem;
3244 }
3245 m_pFront = VMA_NULL;
3246 m_pBack = VMA_NULL;
3247 m_Count = 0;
3248 }
3249 }
3250
3251 template<typename T>
PushBack()3252 VmaListItem<T>* VmaRawList<T>::PushBack()
3253 {
3254 ItemType* const pNewItem = m_ItemAllocator.Alloc();
3255 pNewItem->pNext = VMA_NULL;
3256 if(IsEmpty())
3257 {
3258 pNewItem->pPrev = VMA_NULL;
3259 m_pFront = pNewItem;
3260 m_pBack = pNewItem;
3261 m_Count = 1;
3262 }
3263 else
3264 {
3265 pNewItem->pPrev = m_pBack;
3266 m_pBack->pNext = pNewItem;
3267 m_pBack = pNewItem;
3268 ++m_Count;
3269 }
3270 return pNewItem;
3271 }
3272
3273 template<typename T>
PushFront()3274 VmaListItem<T>* VmaRawList<T>::PushFront()
3275 {
3276 ItemType* const pNewItem = m_ItemAllocator.Alloc();
3277 pNewItem->pPrev = VMA_NULL;
3278 if(IsEmpty())
3279 {
3280 pNewItem->pNext = VMA_NULL;
3281 m_pFront = pNewItem;
3282 m_pBack = pNewItem;
3283 m_Count = 1;
3284 }
3285 else
3286 {
3287 pNewItem->pNext = m_pFront;
3288 m_pFront->pPrev = pNewItem;
3289 m_pFront = pNewItem;
3290 ++m_Count;
3291 }
3292 return pNewItem;
3293 }
3294
3295 template<typename T>
PushBack(const T & value)3296 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
3297 {
3298 ItemType* const pNewItem = PushBack();
3299 pNewItem->Value = value;
3300 return pNewItem;
3301 }
3302
3303 template<typename T>
PushFront(const T & value)3304 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
3305 {
3306 ItemType* const pNewItem = PushFront();
3307 pNewItem->Value = value;
3308 return pNewItem;
3309 }
3310
3311 template<typename T>
PopBack()3312 void VmaRawList<T>::PopBack()
3313 {
3314 VMA_HEAVY_ASSERT(m_Count > 0);
3315 ItemType* const pBackItem = m_pBack;
3316 ItemType* const pPrevItem = pBackItem->pPrev;
3317 if(pPrevItem != VMA_NULL)
3318 {
3319 pPrevItem->pNext = VMA_NULL;
3320 }
3321 m_pBack = pPrevItem;
3322 m_ItemAllocator.Free(pBackItem);
3323 --m_Count;
3324 }
3325
3326 template<typename T>
PopFront()3327 void VmaRawList<T>::PopFront()
3328 {
3329 VMA_HEAVY_ASSERT(m_Count > 0);
3330 ItemType* const pFrontItem = m_pFront;
3331 ItemType* const pNextItem = pFrontItem->pNext;
3332 if(pNextItem != VMA_NULL)
3333 {
3334 pNextItem->pPrev = VMA_NULL;
3335 }
3336 m_pFront = pNextItem;
3337 m_ItemAllocator.Free(pFrontItem);
3338 --m_Count;
3339 }
3340
3341 template<typename T>
Remove(ItemType * pItem)3342 void VmaRawList<T>::Remove(ItemType* pItem)
3343 {
3344 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
3345 VMA_HEAVY_ASSERT(m_Count > 0);
3346
3347 if(pItem->pPrev != VMA_NULL)
3348 {
3349 pItem->pPrev->pNext = pItem->pNext;
3350 }
3351 else
3352 {
3353 VMA_HEAVY_ASSERT(m_pFront == pItem);
3354 m_pFront = pItem->pNext;
3355 }
3356
3357 if(pItem->pNext != VMA_NULL)
3358 {
3359 pItem->pNext->pPrev = pItem->pPrev;
3360 }
3361 else
3362 {
3363 VMA_HEAVY_ASSERT(m_pBack == pItem);
3364 m_pBack = pItem->pPrev;
3365 }
3366
3367 m_ItemAllocator.Free(pItem);
3368 --m_Count;
3369 }
3370
3371 template<typename T>
InsertBefore(ItemType * pItem)3372 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
3373 {
3374 if(pItem != VMA_NULL)
3375 {
3376 ItemType* const prevItem = pItem->pPrev;
3377 ItemType* const newItem = m_ItemAllocator.Alloc();
3378 newItem->pPrev = prevItem;
3379 newItem->pNext = pItem;
3380 pItem->pPrev = newItem;
3381 if(prevItem != VMA_NULL)
3382 {
3383 prevItem->pNext = newItem;
3384 }
3385 else
3386 {
3387 VMA_HEAVY_ASSERT(m_pFront == pItem);
3388 m_pFront = newItem;
3389 }
3390 ++m_Count;
3391 return newItem;
3392 }
3393 else
3394 return PushBack();
3395 }
3396
3397 template<typename T>
InsertAfter(ItemType * pItem)3398 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
3399 {
3400 if(pItem != VMA_NULL)
3401 {
3402 ItemType* const nextItem = pItem->pNext;
3403 ItemType* const newItem = m_ItemAllocator.Alloc();
3404 newItem->pNext = nextItem;
3405 newItem->pPrev = pItem;
3406 pItem->pNext = newItem;
3407 if(nextItem != VMA_NULL)
3408 {
3409 nextItem->pPrev = newItem;
3410 }
3411 else
3412 {
3413 VMA_HEAVY_ASSERT(m_pBack == pItem);
3414 m_pBack = newItem;
3415 }
3416 ++m_Count;
3417 return newItem;
3418 }
3419 else
3420 return PushFront();
3421 }
3422
3423 template<typename T>
InsertBefore(ItemType * pItem,const T & value)3424 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
3425 {
3426 ItemType* const newItem = InsertBefore(pItem);
3427 newItem->Value = value;
3428 return newItem;
3429 }
3430
3431 template<typename T>
InsertAfter(ItemType * pItem,const T & value)3432 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
3433 {
3434 ItemType* const newItem = InsertAfter(pItem);
3435 newItem->Value = value;
3436 return newItem;
3437 }
3438
3439 template<typename T, typename AllocatorT>
3440 class VmaList
3441 {
3442 public:
3443 class iterator
3444 {
3445 public:
iterator()3446 iterator() :
3447 m_pList(VMA_NULL),
3448 m_pItem(VMA_NULL)
3449 {
3450 }
3451
3452 T& operator*() const
3453 {
3454 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3455 return m_pItem->Value;
3456 }
3457 T* operator->() const
3458 {
3459 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3460 return &m_pItem->Value;
3461 }
3462
3463 iterator& operator++()
3464 {
3465 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3466 m_pItem = m_pItem->pNext;
3467 return *this;
3468 }
3469 iterator& operator--()
3470 {
3471 if(m_pItem != VMA_NULL)
3472 {
3473 m_pItem = m_pItem->pPrev;
3474 }
3475 else
3476 {
3477 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
3478 m_pItem = m_pList->Back();
3479 }
3480 return *this;
3481 }
3482
3483 iterator operator++(int)
3484 {
3485 iterator result = *this;
3486 ++*this;
3487 return result;
3488 }
3489 iterator operator--(int)
3490 {
3491 iterator result = *this;
3492 --*this;
3493 return result;
3494 }
3495
3496 bool operator==(const iterator& rhs) const
3497 {
3498 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3499 return m_pItem == rhs.m_pItem;
3500 }
3501 bool operator!=(const iterator& rhs) const
3502 {
3503 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3504 return m_pItem != rhs.m_pItem;
3505 }
3506
3507 private:
3508 VmaRawList<T>* m_pList;
3509 VmaListItem<T>* m_pItem;
3510
iterator(VmaRawList<T> * pList,VmaListItem<T> * pItem)3511 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
3512 m_pList(pList),
3513 m_pItem(pItem)
3514 {
3515 }
3516
3517 friend class VmaList<T, AllocatorT>;
3518 };
3519
3520 class const_iterator
3521 {
3522 public:
const_iterator()3523 const_iterator() :
3524 m_pList(VMA_NULL),
3525 m_pItem(VMA_NULL)
3526 {
3527 }
3528
const_iterator(const iterator & src)3529 const_iterator(const iterator& src) :
3530 m_pList(src.m_pList),
3531 m_pItem(src.m_pItem)
3532 {
3533 }
3534
3535 const T& operator*() const
3536 {
3537 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3538 return m_pItem->Value;
3539 }
3540 const T* operator->() const
3541 {
3542 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3543 return &m_pItem->Value;
3544 }
3545
3546 const_iterator& operator++()
3547 {
3548 VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
3549 m_pItem = m_pItem->pNext;
3550 return *this;
3551 }
3552 const_iterator& operator--()
3553 {
3554 if(m_pItem != VMA_NULL)
3555 {
3556 m_pItem = m_pItem->pPrev;
3557 }
3558 else
3559 {
3560 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
3561 m_pItem = m_pList->Back();
3562 }
3563 return *this;
3564 }
3565
3566 const_iterator operator++(int)
3567 {
3568 const_iterator result = *this;
3569 ++*this;
3570 return result;
3571 }
3572 const_iterator operator--(int)
3573 {
3574 const_iterator result = *this;
3575 --*this;
3576 return result;
3577 }
3578
3579 bool operator==(const const_iterator& rhs) const
3580 {
3581 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3582 return m_pItem == rhs.m_pItem;
3583 }
3584 bool operator!=(const const_iterator& rhs) const
3585 {
3586 VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
3587 return m_pItem != rhs.m_pItem;
3588 }
3589
3590 private:
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)3591 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
3592 m_pList(pList),
3593 m_pItem(pItem)
3594 {
3595 }
3596
3597 const VmaRawList<T>* m_pList;
3598 const VmaListItem<T>* m_pItem;
3599
3600 friend class VmaList<T, AllocatorT>;
3601 };
3602
VmaList(const AllocatorT & allocator)3603 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
3604
empty()3605 bool empty() const { return m_RawList.IsEmpty(); }
size()3606 size_t size() const { return m_RawList.GetCount(); }
3607
begin()3608 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()3609 iterator end() { return iterator(&m_RawList, VMA_NULL); }
3610
cbegin()3611 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()3612 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
3613
clear()3614 void clear() { m_RawList.Clear(); }
push_back(const T & value)3615 void push_back(const T& value) { m_RawList.PushBack(value); }
erase(iterator it)3616 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
insert(iterator it,const T & value)3617 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
3618
3619 private:
3620 VmaRawList<T> m_RawList;
3621 };
3622
3623 #endif // #if VMA_USE_STL_LIST
3624
3625 ////////////////////////////////////////////////////////////////////////////////
3626 // class VmaMap
3627
3628 // Unused in this version.
3629 #if 0
3630
3631 #if VMA_USE_STL_UNORDERED_MAP
3632
3633 #define VmaPair std::pair
3634
3635 #define VMA_MAP_TYPE(KeyT, ValueT) \
3636 std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
3637
3638 #else // #if VMA_USE_STL_UNORDERED_MAP
3639
3640 template<typename T1, typename T2>
3641 struct VmaPair
3642 {
3643 T1 first;
3644 T2 second;
3645
3646 VmaPair() : first(), second() { }
3647 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
3648 };
3649
3650 /* Class compatible with subset of interface of std::unordered_map.
3651 KeyT, ValueT must be POD because they will be stored in VmaVector.
3652 */
3653 template<typename KeyT, typename ValueT>
3654 class VmaMap
3655 {
3656 public:
3657 typedef VmaPair<KeyT, ValueT> PairType;
3658 typedef PairType* iterator;
3659
3660 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
3661
3662 iterator begin() { return m_Vector.begin(); }
3663 iterator end() { return m_Vector.end(); }
3664
3665 void insert(const PairType& pair);
3666 iterator find(const KeyT& key);
3667 void erase(iterator it);
3668
3669 private:
3670 VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
3671 };
3672
3673 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
3674
3675 template<typename FirstT, typename SecondT>
3676 struct VmaPairFirstLess
3677 {
3678 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
3679 {
3680 return lhs.first < rhs.first;
3681 }
3682 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
3683 {
3684 return lhs.first < rhsFirst;
3685 }
3686 };
3687
3688 template<typename KeyT, typename ValueT>
3689 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
3690 {
3691 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3692 m_Vector.data(),
3693 m_Vector.data() + m_Vector.size(),
3694 pair,
3695 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
3696 VmaVectorInsert(m_Vector, indexToInsert, pair);
3697 }
3698
3699 template<typename KeyT, typename ValueT>
3700 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
3701 {
3702 PairType* it = VmaBinaryFindFirstNotLess(
3703 m_Vector.data(),
3704 m_Vector.data() + m_Vector.size(),
3705 key,
3706 VmaPairFirstLess<KeyT, ValueT>());
3707 if((it != m_Vector.end()) && (it->first == key))
3708 {
3709 return it;
3710 }
3711 else
3712 {
3713 return m_Vector.end();
3714 }
3715 }
3716
3717 template<typename KeyT, typename ValueT>
3718 void VmaMap<KeyT, ValueT>::erase(iterator it)
3719 {
3720 VmaVectorRemove(m_Vector, it - m_Vector.begin());
3721 }
3722
3723 #endif // #if VMA_USE_STL_UNORDERED_MAP
3724
3725 #endif // #if 0
3726
3727 ////////////////////////////////////////////////////////////////////////////////
3728
3729 class VmaDeviceMemoryBlock;
3730
3731 struct VmaAllocation_T
3732 {
3733 private:
3734 static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
3735
3736 enum FLAGS
3737 {
3738 FLAG_USER_DATA_STRING = 0x01,
3739 };
3740
3741 public:
3742 enum ALLOCATION_TYPE
3743 {
3744 ALLOCATION_TYPE_NONE,
3745 ALLOCATION_TYPE_BLOCK,
3746 ALLOCATION_TYPE_DEDICATED,
3747 };
3748
VmaAllocation_TVmaAllocation_T3749 VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
3750 m_Alignment(1),
3751 m_Size(0),
3752 m_pUserData(VMA_NULL),
3753 m_LastUseFrameIndex(currentFrameIndex),
3754 m_Type((uint8_t)ALLOCATION_TYPE_NONE),
3755 m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
3756 m_MapCount(0),
3757 m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
3758 {
3759 }
3760
~VmaAllocation_TVmaAllocation_T3761 ~VmaAllocation_T()
3762 {
3763 VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
3764
3765 // Check if owned string was freed.
3766 VMA_ASSERT(m_pUserData == VMA_NULL);
3767 }
3768
InitBlockAllocationVmaAllocation_T3769 void InitBlockAllocation(
3770 VmaPool hPool,
3771 VmaDeviceMemoryBlock* block,
3772 VkDeviceSize offset,
3773 VkDeviceSize alignment,
3774 VkDeviceSize size,
3775 VmaSuballocationType suballocationType,
3776 bool mapped,
3777 bool canBecomeLost)
3778 {
3779 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3780 VMA_ASSERT(block != VMA_NULL);
3781 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
3782 m_Alignment = alignment;
3783 m_Size = size;
3784 m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
3785 m_SuballocationType = (uint8_t)suballocationType;
3786 m_BlockAllocation.m_hPool = hPool;
3787 m_BlockAllocation.m_Block = block;
3788 m_BlockAllocation.m_Offset = offset;
3789 m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
3790 }
3791
InitLostVmaAllocation_T3792 void InitLost()
3793 {
3794 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3795 VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
3796 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
3797 m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
3798 m_BlockAllocation.m_Block = VMA_NULL;
3799 m_BlockAllocation.m_Offset = 0;
3800 m_BlockAllocation.m_CanBecomeLost = true;
3801 }
3802
3803 void ChangeBlockAllocation(
3804 VmaAllocator hAllocator,
3805 VmaDeviceMemoryBlock* block,
3806 VkDeviceSize offset);
3807
3808 // pMappedData not null means allocation is created with MAPPED flag.
InitDedicatedAllocationVmaAllocation_T3809 void InitDedicatedAllocation(
3810 uint32_t memoryTypeIndex,
3811 VkDeviceMemory hMemory,
3812 VmaSuballocationType suballocationType,
3813 void* pMappedData,
3814 VkDeviceSize size)
3815 {
3816 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
3817 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
3818 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
3819 m_Alignment = 0;
3820 m_Size = size;
3821 m_SuballocationType = (uint8_t)suballocationType;
3822 m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
3823 m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
3824 m_DedicatedAllocation.m_hMemory = hMemory;
3825 m_DedicatedAllocation.m_pMappedData = pMappedData;
3826 }
3827
GetTypeVmaAllocation_T3828 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T3829 VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T3830 VkDeviceSize GetSize() const { return m_Size; }
IsUserDataStringVmaAllocation_T3831 bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
GetUserDataVmaAllocation_T3832 void* GetUserData() const { return m_pUserData; }
3833 void SetUserData(VmaAllocator hAllocator, void* pUserData);
GetSuballocationTypeVmaAllocation_T3834 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
3835
GetBlockVmaAllocation_T3836 VmaDeviceMemoryBlock* GetBlock() const
3837 {
3838 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
3839 return m_BlockAllocation.m_Block;
3840 }
3841 VkDeviceSize GetOffset() const;
3842 VkDeviceMemory GetMemory() const;
3843 uint32_t GetMemoryTypeIndex() const;
IsPersistentMapVmaAllocation_T3844 bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
3845 void* GetMappedData() const;
3846 bool CanBecomeLost() const;
3847 VmaPool GetPool() const;
3848
GetLastUseFrameIndexVmaAllocation_T3849 uint32_t GetLastUseFrameIndex() const
3850 {
3851 return m_LastUseFrameIndex.load();
3852 }
CompareExchangeLastUseFrameIndexVmaAllocation_T3853 bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
3854 {
3855 return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
3856 }
3857 /*
3858 - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
3859 makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
3860 - Else, returns false.
3861
3862 If hAllocation is already lost, assert - you should not call it then.
3863 If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
3864 */
3865 bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
3866
DedicatedAllocCalcStatsInfoVmaAllocation_T3867 void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
3868 {
3869 VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
3870 outInfo.blockCount = 1;
3871 outInfo.allocationCount = 1;
3872 outInfo.unusedRangeCount = 0;
3873 outInfo.usedBytes = m_Size;
3874 outInfo.unusedBytes = 0;
3875 outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
3876 outInfo.unusedRangeSizeMin = UINT64_MAX;
3877 outInfo.unusedRangeSizeMax = 0;
3878 }
3879
3880 void BlockAllocMap();
3881 void BlockAllocUnmap();
3882 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
3883 void DedicatedAllocUnmap(VmaAllocator hAllocator);
3884
3885 private:
3886 VkDeviceSize m_Alignment;
3887 VkDeviceSize m_Size;
3888 void* m_pUserData;
3889 VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
3890 uint8_t m_Type; // ALLOCATION_TYPE
3891 uint8_t m_SuballocationType; // VmaSuballocationType
3892 // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
3893 // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
3894 uint8_t m_MapCount;
3895 uint8_t m_Flags; // enum FLAGS
3896
3897 // Allocation out of VmaDeviceMemoryBlock.
3898 struct BlockAllocation
3899 {
3900 VmaPool m_hPool; // Null if belongs to general memory.
3901 VmaDeviceMemoryBlock* m_Block;
3902 VkDeviceSize m_Offset;
3903 bool m_CanBecomeLost;
3904 };
3905
3906 // Allocation for an object that has its own private VkDeviceMemory.
3907 struct DedicatedAllocation
3908 {
3909 uint32_t m_MemoryTypeIndex;
3910 VkDeviceMemory m_hMemory;
3911 void* m_pMappedData; // Not null means memory is mapped.
3912 };
3913
3914 union
3915 {
3916 // Allocation out of VmaDeviceMemoryBlock.
3917 BlockAllocation m_BlockAllocation;
3918 // Allocation for an object that has its own private VkDeviceMemory.
3919 DedicatedAllocation m_DedicatedAllocation;
3920 };
3921
3922 void FreeUserDataString(VmaAllocator hAllocator);
3923 };
3924
3925 /*
3926 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
3927 allocated memory block or free.
3928 */
3929 struct VmaSuballocation
3930 {
3931 VkDeviceSize offset;
3932 VkDeviceSize size;
3933 VmaAllocation hAllocation;
3934 VmaSuballocationType type;
3935 };
3936
3937 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
3938
3939 // Cost of one additional allocation lost, as equivalent in bytes.
3940 static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
3941
3942 /*
3943 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
3944
3945 If canMakeOtherLost was false:
3946 - item points to a FREE suballocation.
3947 - itemsToMakeLostCount is 0.
3948
3949 If canMakeOtherLost was true:
3950 - item points to first of sequence of suballocations, which are either FREE,
3951 or point to VmaAllocations that can become lost.
3952 - itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
3953 the requested allocation to succeed.
3954 */
3955 struct VmaAllocationRequest
3956 {
3957 VkDeviceSize offset;
3958 VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
3959 VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
3960 VmaSuballocationList::iterator item;
3961 size_t itemsToMakeLostCount;
3962
CalcCostVmaAllocationRequest3963 VkDeviceSize CalcCost() const
3964 {
3965 return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
3966 }
3967 };
3968
3969 /*
3970 Data structure used for bookkeeping of allocations and unused ranges of memory
3971 in a single VkDeviceMemory block.
3972 */
3973 class VmaBlockMetadata
3974 {
3975 public:
3976 VmaBlockMetadata(VmaAllocator hAllocator);
3977 ~VmaBlockMetadata();
3978 void Init(VkDeviceSize size);
3979
3980 // Validates all data structures inside this object. If not valid, returns false.
3981 bool Validate() const;
GetSize()3982 VkDeviceSize GetSize() const { return m_Size; }
GetAllocationCount()3983 size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
GetSumFreeSize()3984 VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
3985 VkDeviceSize GetUnusedRangeSizeMax() const;
3986 // Returns true if this block is empty - contains only single free suballocation.
3987 bool IsEmpty() const;
3988
3989 void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
3990 void AddPoolStats(VmaPoolStats& inoutStats) const;
3991
3992 #if VMA_STATS_STRING_ENABLED
3993 void PrintDetailedMap(class VmaJsonWriter& json) const;
3994 #endif
3995
3996 // Creates trivial request for case when block is empty.
3997 void CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest);
3998
3999 // Tries to find a place for suballocation with given parameters inside this block.
4000 // If succeeded, fills pAllocationRequest and returns true.
4001 // If failed, returns false.
4002 bool CreateAllocationRequest(
4003 uint32_t currentFrameIndex,
4004 uint32_t frameInUseCount,
4005 VkDeviceSize bufferImageGranularity,
4006 VkDeviceSize allocSize,
4007 VkDeviceSize allocAlignment,
4008 VmaSuballocationType allocType,
4009 bool canMakeOtherLost,
4010 VmaAllocationRequest* pAllocationRequest);
4011
4012 bool MakeRequestedAllocationsLost(
4013 uint32_t currentFrameIndex,
4014 uint32_t frameInUseCount,
4015 VmaAllocationRequest* pAllocationRequest);
4016
4017 uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
4018
4019 // Makes actual allocation based on request. Request must already be checked and valid.
4020 void Alloc(
4021 const VmaAllocationRequest& request,
4022 VmaSuballocationType type,
4023 VkDeviceSize allocSize,
4024 VmaAllocation hAllocation);
4025
4026 // Frees suballocation assigned to given memory region.
4027 void Free(const VmaAllocation allocation);
4028 void FreeAtOffset(VkDeviceSize offset);
4029
4030 private:
4031 VkDeviceSize m_Size;
4032 uint32_t m_FreeCount;
4033 VkDeviceSize m_SumFreeSize;
4034 VmaSuballocationList m_Suballocations;
4035 // Suballocations that are free and have size greater than certain threshold.
4036 // Sorted by size, ascending.
4037 VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
4038
4039 bool ValidateFreeSuballocationList() const;
4040
4041 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
4042 // If yes, fills pOffset and returns true. If no, returns false.
4043 bool CheckAllocation(
4044 uint32_t currentFrameIndex,
4045 uint32_t frameInUseCount,
4046 VkDeviceSize bufferImageGranularity,
4047 VkDeviceSize allocSize,
4048 VkDeviceSize allocAlignment,
4049 VmaSuballocationType allocType,
4050 VmaSuballocationList::const_iterator suballocItem,
4051 bool canMakeOtherLost,
4052 VkDeviceSize* pOffset,
4053 size_t* itemsToMakeLostCount,
4054 VkDeviceSize* pSumFreeSize,
4055 VkDeviceSize* pSumItemSize) const;
4056 // Given free suballocation, it merges it with following one, which must also be free.
4057 void MergeFreeWithNext(VmaSuballocationList::iterator item);
4058 // Releases given suballocation, making it free.
4059 // Merges it with adjacent free suballocations if applicable.
4060 // Returns iterator to new free suballocation at this place.
4061 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
4062 // Given free suballocation, it inserts it into sorted list of
4063 // m_FreeSuballocationsBySize if it's suitable.
4064 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
4065 // Given free suballocation, it removes it from sorted list of
4066 // m_FreeSuballocationsBySize if it's suitable.
4067 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
4068 };
4069
4070 /*
4071 Represents a single block of device memory (`VkDeviceMemory`) with all the
4072 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
4073
4074 Thread-safety: This class must be externally synchronized.
4075 */
4076 class VmaDeviceMemoryBlock
4077 {
4078 public:
4079 VmaBlockMetadata m_Metadata;
4080
4081 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
4082
~VmaDeviceMemoryBlock()4083 ~VmaDeviceMemoryBlock()
4084 {
4085 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
4086 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
4087 }
4088
4089 // Always call after construction.
4090 void Init(
4091 uint32_t newMemoryTypeIndex,
4092 VkDeviceMemory newMemory,
4093 VkDeviceSize newSize);
4094 // Always call before destruction.
4095 void Destroy(VmaAllocator allocator);
4096
GetDeviceMemory()4097 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()4098 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetMappedData()4099 void* GetMappedData() const { return m_pMappedData; }
4100
4101 // Validates all data structures inside this object. If not valid, returns false.
4102 bool Validate() const;
4103
4104 // ppData can be null.
4105 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
4106 void Unmap(VmaAllocator hAllocator, uint32_t count);
4107
4108 VkResult BindBufferMemory(
4109 const VmaAllocator hAllocator,
4110 const VmaAllocation hAllocation,
4111 VkBuffer hBuffer);
4112 VkResult BindImageMemory(
4113 const VmaAllocator hAllocator,
4114 const VmaAllocation hAllocation,
4115 VkImage hImage);
4116
4117 private:
4118 uint32_t m_MemoryTypeIndex;
4119 VkDeviceMemory m_hMemory;
4120
4121 // Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
4122 // Also protects m_MapCount, m_pMappedData.
4123 VMA_MUTEX m_Mutex;
4124 uint32_t m_MapCount;
4125 void* m_pMappedData;
4126 };
4127
4128 struct VmaPointerLess
4129 {
operatorVmaPointerLess4130 bool operator()(const void* lhs, const void* rhs) const
4131 {
4132 return lhs < rhs;
4133 }
4134 };
4135
4136 class VmaDefragmentator;
4137
4138 /*
4139 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
4140 Vulkan memory type.
4141
4142 Synchronized internally with a mutex.
4143 */
4144 struct VmaBlockVector
4145 {
4146 VmaBlockVector(
4147 VmaAllocator hAllocator,
4148 uint32_t memoryTypeIndex,
4149 VkDeviceSize preferredBlockSize,
4150 size_t minBlockCount,
4151 size_t maxBlockCount,
4152 VkDeviceSize bufferImageGranularity,
4153 uint32_t frameInUseCount,
4154 bool isCustomPool);
4155 ~VmaBlockVector();
4156
4157 VkResult CreateMinBlocks();
4158
GetMemoryTypeIndexVmaBlockVector4159 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSizeVmaBlockVector4160 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularityVmaBlockVector4161 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetFrameInUseCountVmaBlockVector4162 uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
4163
4164 void GetPoolStats(VmaPoolStats* pStats);
4165
IsEmptyVmaBlockVector4166 bool IsEmpty() const { return m_Blocks.empty(); }
4167
4168 VkResult Allocate(
4169 VmaPool hCurrentPool,
4170 uint32_t currentFrameIndex,
4171 const VkMemoryRequirements& vkMemReq,
4172 const VmaAllocationCreateInfo& createInfo,
4173 VmaSuballocationType suballocType,
4174 VmaAllocation* pAllocation);
4175
4176 void Free(
4177 VmaAllocation hAllocation);
4178
4179 // Adds statistics of this BlockVector to pStats.
4180 void AddStats(VmaStats* pStats);
4181
4182 #if VMA_STATS_STRING_ENABLED
4183 void PrintDetailedMap(class VmaJsonWriter& json);
4184 #endif
4185
4186 void MakePoolAllocationsLost(
4187 uint32_t currentFrameIndex,
4188 size_t* pLostAllocationCount);
4189
4190 VmaDefragmentator* EnsureDefragmentator(
4191 VmaAllocator hAllocator,
4192 uint32_t currentFrameIndex);
4193
4194 VkResult Defragment(
4195 VmaDefragmentationStats* pDefragmentationStats,
4196 VkDeviceSize& maxBytesToMove,
4197 uint32_t& maxAllocationsToMove);
4198
4199 void DestroyDefragmentator();
4200
4201 private:
4202 friend class VmaDefragmentator;
4203
4204 const VmaAllocator m_hAllocator;
4205 const uint32_t m_MemoryTypeIndex;
4206 const VkDeviceSize m_PreferredBlockSize;
4207 const size_t m_MinBlockCount;
4208 const size_t m_MaxBlockCount;
4209 const VkDeviceSize m_BufferImageGranularity;
4210 const uint32_t m_FrameInUseCount;
4211 const bool m_IsCustomPool;
4212 VMA_MUTEX m_Mutex;
4213 // Incrementally sorted by sumFreeSize, ascending.
4214 VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
4215 /* There can be at most one allocation that is completely empty - a
4216 hysteresis to avoid pessimistic case of alternating creation and destruction
4217 of a VkDeviceMemory. */
4218 bool m_HasEmptyBlock;
4219 VmaDefragmentator* m_pDefragmentator;
4220
4221 size_t CalcMaxBlockSize() const;
4222
4223 // Finds and removes given block from vector.
4224 void Remove(VmaDeviceMemoryBlock* pBlock);
4225
4226 // Performs single step in sorting m_Blocks. They may not be fully sorted
4227 // after this call.
4228 void IncrementallySortBlocks();
4229
4230 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
4231 };
4232
4233 struct VmaPool_T
4234 {
4235 public:
4236 VmaBlockVector m_BlockVector;
4237
4238 // Takes ownership.
4239 VmaPool_T(
4240 VmaAllocator hAllocator,
4241 const VmaPoolCreateInfo& createInfo);
4242 ~VmaPool_T();
4243
GetBlockVectorVmaPool_T4244 VmaBlockVector& GetBlockVector() { return m_BlockVector; }
4245
4246 #if VMA_STATS_STRING_ENABLED
4247 //void PrintDetailedMap(class VmaStringBuilder& sb);
4248 #endif
4249 };
4250
4251 class VmaDefragmentator
4252 {
4253 const VmaAllocator m_hAllocator;
4254 VmaBlockVector* const m_pBlockVector;
4255 uint32_t m_CurrentFrameIndex;
4256 VkDeviceSize m_BytesMoved;
4257 uint32_t m_AllocationsMoved;
4258
4259 struct AllocationInfo
4260 {
4261 VmaAllocation m_hAllocation;
4262 VkBool32* m_pChanged;
4263
AllocationInfoAllocationInfo4264 AllocationInfo() :
4265 m_hAllocation(VK_NULL_HANDLE),
4266 m_pChanged(VMA_NULL)
4267 {
4268 }
4269 };
4270
4271 struct AllocationInfoSizeGreater
4272 {
operatorAllocationInfoSizeGreater4273 bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
4274 {
4275 return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
4276 }
4277 };
4278
4279 // Used between AddAllocation and Defragment.
4280 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
4281
4282 struct BlockInfo
4283 {
4284 VmaDeviceMemoryBlock* m_pBlock;
4285 bool m_HasNonMovableAllocations;
4286 VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
4287
BlockInfoBlockInfo4288 BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
4289 m_pBlock(VMA_NULL),
4290 m_HasNonMovableAllocations(true),
4291 m_Allocations(pAllocationCallbacks),
4292 m_pMappedDataForDefragmentation(VMA_NULL)
4293 {
4294 }
4295
CalcHasNonMovableAllocationsBlockInfo4296 void CalcHasNonMovableAllocations()
4297 {
4298 const size_t blockAllocCount = m_pBlock->m_Metadata.GetAllocationCount();
4299 const size_t defragmentAllocCount = m_Allocations.size();
4300 m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
4301 }
4302
SortAllocationsBySizeDescecndingBlockInfo4303 void SortAllocationsBySizeDescecnding()
4304 {
4305 VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
4306 }
4307
4308 VkResult EnsureMapping(VmaAllocator hAllocator, void** ppMappedData);
4309 void Unmap(VmaAllocator hAllocator);
4310
4311 private:
4312 // Not null if mapped for defragmentation only, not originally mapped.
4313 void* m_pMappedDataForDefragmentation;
4314 };
4315
4316 struct BlockPointerLess
4317 {
operatorBlockPointerLess4318 bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
4319 {
4320 return pLhsBlockInfo->m_pBlock < pRhsBlock;
4321 }
operatorBlockPointerLess4322 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
4323 {
4324 return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
4325 }
4326 };
4327
4328 // 1. Blocks with some non-movable allocations go first.
4329 // 2. Blocks with smaller sumFreeSize go first.
4330 struct BlockInfoCompareMoveDestination
4331 {
operatorBlockInfoCompareMoveDestination4332 bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
4333 {
4334 if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
4335 {
4336 return true;
4337 }
4338 if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
4339 {
4340 return false;
4341 }
4342 if(pLhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_Metadata.GetSumFreeSize())
4343 {
4344 return true;
4345 }
4346 return false;
4347 }
4348 };
4349
4350 typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
4351 BlockInfoVector m_Blocks;
4352
4353 VkResult DefragmentRound(
4354 VkDeviceSize maxBytesToMove,
4355 uint32_t maxAllocationsToMove);
4356
4357 static bool MoveMakesSense(
4358 size_t dstBlockIndex, VkDeviceSize dstOffset,
4359 size_t srcBlockIndex, VkDeviceSize srcOffset);
4360
4361 public:
4362 VmaDefragmentator(
4363 VmaAllocator hAllocator,
4364 VmaBlockVector* pBlockVector,
4365 uint32_t currentFrameIndex);
4366
4367 ~VmaDefragmentator();
4368
GetBytesMoved()4369 VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
GetAllocationsMoved()4370 uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
4371
4372 void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
4373
4374 VkResult Defragment(
4375 VkDeviceSize maxBytesToMove,
4376 uint32_t maxAllocationsToMove);
4377 };
4378
4379 // Main allocator object.
4380 struct VmaAllocator_T
4381 {
4382 bool m_UseMutex;
4383 bool m_UseKhrDedicatedAllocation;
4384 VkDevice m_hDevice;
4385 bool m_AllocationCallbacksSpecified;
4386 VkAllocationCallbacks m_AllocationCallbacks;
4387 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
4388
4389 // Number of bytes free out of limit, or VK_WHOLE_SIZE if not limit for that heap.
4390 VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
4391 VMA_MUTEX m_HeapSizeLimitMutex;
4392
4393 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
4394 VkPhysicalDeviceMemoryProperties m_MemProps;
4395
4396 // Default pools.
4397 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
4398
4399 // Each vector is sorted by memory (handle value).
4400 typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
4401 AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
4402 VMA_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
4403
4404 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
4405 ~VmaAllocator_T();
4406
GetAllocationCallbacksVmaAllocator_T4407 const VkAllocationCallbacks* GetAllocationCallbacks() const
4408 {
4409 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
4410 }
GetVulkanFunctionsVmaAllocator_T4411 const VmaVulkanFunctions& GetVulkanFunctions() const
4412 {
4413 return m_VulkanFunctions;
4414 }
4415
GetBufferImageGranularityVmaAllocator_T4416 VkDeviceSize GetBufferImageGranularity() const
4417 {
4418 return VMA_MAX(
4419 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
4420 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
4421 }
4422
GetMemoryHeapCountVmaAllocator_T4423 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T4424 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
4425
MemoryTypeIndexToHeapIndexVmaAllocator_T4426 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
4427 {
4428 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
4429 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
4430 }
4431
4432 void GetBufferMemoryRequirements(
4433 VkBuffer hBuffer,
4434 VkMemoryRequirements& memReq,
4435 bool& requiresDedicatedAllocation,
4436 bool& prefersDedicatedAllocation) const;
4437 void GetImageMemoryRequirements(
4438 VkImage hImage,
4439 VkMemoryRequirements& memReq,
4440 bool& requiresDedicatedAllocation,
4441 bool& prefersDedicatedAllocation) const;
4442
4443 // Main allocation function.
4444 VkResult AllocateMemory(
4445 const VkMemoryRequirements& vkMemReq,
4446 bool requiresDedicatedAllocation,
4447 bool prefersDedicatedAllocation,
4448 VkBuffer dedicatedBuffer,
4449 VkImage dedicatedImage,
4450 const VmaAllocationCreateInfo& createInfo,
4451 VmaSuballocationType suballocType,
4452 VmaAllocation* pAllocation);
4453
4454 // Main deallocation function.
4455 void FreeMemory(const VmaAllocation allocation);
4456
4457 void CalculateStats(VmaStats* pStats);
4458
4459 #if VMA_STATS_STRING_ENABLED
4460 void PrintDetailedMap(class VmaJsonWriter& json);
4461 #endif
4462
4463 VkResult Defragment(
4464 VmaAllocation* pAllocations,
4465 size_t allocationCount,
4466 VkBool32* pAllocationsChanged,
4467 const VmaDefragmentationInfo* pDefragmentationInfo,
4468 VmaDefragmentationStats* pDefragmentationStats);
4469
4470 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
4471 bool TouchAllocation(VmaAllocation hAllocation);
4472
4473 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
4474 void DestroyPool(VmaPool pool);
4475 void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
4476
4477 void SetCurrentFrameIndex(uint32_t frameIndex);
4478
4479 void MakePoolAllocationsLost(
4480 VmaPool hPool,
4481 size_t* pLostAllocationCount);
4482
4483 void CreateLostAllocation(VmaAllocation* pAllocation);
4484
4485 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
4486 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
4487
4488 VkResult Map(VmaAllocation hAllocation, void** ppData);
4489 void Unmap(VmaAllocation hAllocation);
4490
4491 VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
4492 VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
4493
4494 private:
4495 VkDeviceSize m_PreferredLargeHeapBlockSize;
4496
4497 VkPhysicalDevice m_PhysicalDevice;
4498 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
4499
4500 VMA_MUTEX m_PoolsMutex;
4501 // Protected by m_PoolsMutex. Sorted by pointer value.
4502 VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
4503
4504 VmaVulkanFunctions m_VulkanFunctions;
4505
4506 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
4507
4508 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
4509
4510 VkResult AllocateMemoryOfType(
4511 const VkMemoryRequirements& vkMemReq,
4512 bool dedicatedAllocation,
4513 VkBuffer dedicatedBuffer,
4514 VkImage dedicatedImage,
4515 const VmaAllocationCreateInfo& createInfo,
4516 uint32_t memTypeIndex,
4517 VmaSuballocationType suballocType,
4518 VmaAllocation* pAllocation);
4519
4520 // Allocates and registers new VkDeviceMemory specifically for single allocation.
4521 VkResult AllocateDedicatedMemory(
4522 VkDeviceSize size,
4523 VmaSuballocationType suballocType,
4524 uint32_t memTypeIndex,
4525 bool map,
4526 bool isUserDataString,
4527 void* pUserData,
4528 VkBuffer dedicatedBuffer,
4529 VkImage dedicatedImage,
4530 VmaAllocation* pAllocation);
4531
4532 // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
4533 void FreeDedicatedMemory(VmaAllocation allocation);
4534 };
4535
4536 ////////////////////////////////////////////////////////////////////////////////
4537 // Memory allocation #2 after VmaAllocator_T definition
4538
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)4539 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
4540 {
4541 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
4542 }
4543
VmaFree(VmaAllocator hAllocator,void * ptr)4544 static void VmaFree(VmaAllocator hAllocator, void* ptr)
4545 {
4546 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
4547 }
4548
4549 template<typename T>
VmaAllocate(VmaAllocator hAllocator)4550 static T* VmaAllocate(VmaAllocator hAllocator)
4551 {
4552 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
4553 }
4554
4555 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)4556 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
4557 {
4558 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
4559 }
4560
4561 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)4562 static void vma_delete(VmaAllocator hAllocator, T* ptr)
4563 {
4564 if(ptr != VMA_NULL)
4565 {
4566 ptr->~T();
4567 VmaFree(hAllocator, ptr);
4568 }
4569 }
4570
4571 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)4572 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
4573 {
4574 if(ptr != VMA_NULL)
4575 {
4576 for(size_t i = count; i--; )
4577 ptr[i].~T();
4578 VmaFree(hAllocator, ptr);
4579 }
4580 }
4581
4582 ////////////////////////////////////////////////////////////////////////////////
4583 // VmaStringBuilder
4584
4585 #if VMA_STATS_STRING_ENABLED
4586
4587 class VmaStringBuilder
4588 {
4589 public:
VmaStringBuilder(VmaAllocator alloc)4590 VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
GetLength()4591 size_t GetLength() const { return m_Data.size(); }
GetData()4592 const char* GetData() const { return m_Data.data(); }
4593
Add(char ch)4594 void Add(char ch) { m_Data.push_back(ch); }
4595 void Add(const char* pStr);
AddNewLine()4596 void AddNewLine() { Add('\n'); }
4597 void AddNumber(uint32_t num);
4598 void AddNumber(uint64_t num);
4599 void AddPointer(const void* ptr);
4600
4601 private:
4602 VmaVector< char, VmaStlAllocator<char> > m_Data;
4603 };
4604
Add(const char * pStr)4605 void VmaStringBuilder::Add(const char* pStr)
4606 {
4607 const size_t strLen = strlen(pStr);
4608 if(strLen > 0)
4609 {
4610 const size_t oldCount = m_Data.size();
4611 m_Data.resize(oldCount + strLen);
4612 memcpy(m_Data.data() + oldCount, pStr, strLen);
4613 }
4614 }
4615
AddNumber(uint32_t num)4616 void VmaStringBuilder::AddNumber(uint32_t num)
4617 {
4618 char buf[11];
4619 VmaUint32ToStr(buf, sizeof(buf), num);
4620 Add(buf);
4621 }
4622
AddNumber(uint64_t num)4623 void VmaStringBuilder::AddNumber(uint64_t num)
4624 {
4625 char buf[21];
4626 VmaUint64ToStr(buf, sizeof(buf), num);
4627 Add(buf);
4628 }
4629
AddPointer(const void * ptr)4630 void VmaStringBuilder::AddPointer(const void* ptr)
4631 {
4632 char buf[21];
4633 VmaPtrToStr(buf, sizeof(buf), ptr);
4634 Add(buf);
4635 }
4636
4637 #endif // #if VMA_STATS_STRING_ENABLED
4638
4639 ////////////////////////////////////////////////////////////////////////////////
4640 // VmaJsonWriter
4641
4642 #if VMA_STATS_STRING_ENABLED
4643
4644 class VmaJsonWriter
4645 {
4646 public:
4647 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
4648 ~VmaJsonWriter();
4649
4650 void BeginObject(bool singleLine = false);
4651 void EndObject();
4652
4653 void BeginArray(bool singleLine = false);
4654 void EndArray();
4655
4656 void WriteString(const char* pStr);
4657 void BeginString(const char* pStr = VMA_NULL);
4658 void ContinueString(const char* pStr);
4659 void ContinueString(uint32_t n);
4660 void ContinueString(uint64_t n);
4661 void ContinueString_Pointer(const void* ptr);
4662 void EndString(const char* pStr = VMA_NULL);
4663
4664 void WriteNumber(uint32_t n);
4665 void WriteNumber(uint64_t n);
4666 void WriteBool(bool b);
4667 void WriteNull();
4668
4669 private:
4670 static const char* const INDENT;
4671
4672 enum COLLECTION_TYPE
4673 {
4674 COLLECTION_TYPE_OBJECT,
4675 COLLECTION_TYPE_ARRAY,
4676 };
4677 struct StackItem
4678 {
4679 COLLECTION_TYPE type;
4680 uint32_t valueCount;
4681 bool singleLineMode;
4682 };
4683
4684 VmaStringBuilder& m_SB;
4685 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
4686 bool m_InsideString;
4687
4688 void BeginValue(bool isString);
4689 void WriteIndent(bool oneLess = false);
4690 };
4691
4692 const char* const VmaJsonWriter::INDENT = " ";
4693
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)4694 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
4695 m_SB(sb),
4696 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
4697 m_InsideString(false)
4698 {
4699 }
4700
~VmaJsonWriter()4701 VmaJsonWriter::~VmaJsonWriter()
4702 {
4703 VMA_ASSERT(!m_InsideString);
4704 VMA_ASSERT(m_Stack.empty());
4705 }
4706
BeginObject(bool singleLine)4707 void VmaJsonWriter::BeginObject(bool singleLine)
4708 {
4709 VMA_ASSERT(!m_InsideString);
4710
4711 BeginValue(false);
4712 m_SB.Add('{');
4713
4714 StackItem item;
4715 item.type = COLLECTION_TYPE_OBJECT;
4716 item.valueCount = 0;
4717 item.singleLineMode = singleLine;
4718 m_Stack.push_back(item);
4719 }
4720
EndObject()4721 void VmaJsonWriter::EndObject()
4722 {
4723 VMA_ASSERT(!m_InsideString);
4724
4725 WriteIndent(true);
4726 m_SB.Add('}');
4727
4728 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
4729 m_Stack.pop_back();
4730 }
4731
BeginArray(bool singleLine)4732 void VmaJsonWriter::BeginArray(bool singleLine)
4733 {
4734 VMA_ASSERT(!m_InsideString);
4735
4736 BeginValue(false);
4737 m_SB.Add('[');
4738
4739 StackItem item;
4740 item.type = COLLECTION_TYPE_ARRAY;
4741 item.valueCount = 0;
4742 item.singleLineMode = singleLine;
4743 m_Stack.push_back(item);
4744 }
4745
EndArray()4746 void VmaJsonWriter::EndArray()
4747 {
4748 VMA_ASSERT(!m_InsideString);
4749
4750 WriteIndent(true);
4751 m_SB.Add(']');
4752
4753 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
4754 m_Stack.pop_back();
4755 }
4756
WriteString(const char * pStr)4757 void VmaJsonWriter::WriteString(const char* pStr)
4758 {
4759 BeginString(pStr);
4760 EndString();
4761 }
4762
BeginString(const char * pStr)4763 void VmaJsonWriter::BeginString(const char* pStr)
4764 {
4765 VMA_ASSERT(!m_InsideString);
4766
4767 BeginValue(true);
4768 m_SB.Add('"');
4769 m_InsideString = true;
4770 if(pStr != VMA_NULL && pStr[0] != '\0')
4771 {
4772 ContinueString(pStr);
4773 }
4774 }
4775
ContinueString(const char * pStr)4776 void VmaJsonWriter::ContinueString(const char* pStr)
4777 {
4778 VMA_ASSERT(m_InsideString);
4779
4780 const size_t strLen = strlen(pStr);
4781 for(size_t i = 0; i < strLen; ++i)
4782 {
4783 char ch = pStr[i];
4784 if(ch == '\'')
4785 {
4786 m_SB.Add("\\\\");
4787 }
4788 else if(ch == '"')
4789 {
4790 m_SB.Add("\\\"");
4791 }
4792 else if(ch >= 32)
4793 {
4794 m_SB.Add(ch);
4795 }
4796 else switch(ch)
4797 {
4798 case '\b':
4799 m_SB.Add("\\b");
4800 break;
4801 case '\f':
4802 m_SB.Add("\\f");
4803 break;
4804 case '\n':
4805 m_SB.Add("\\n");
4806 break;
4807 case '\r':
4808 m_SB.Add("\\r");
4809 break;
4810 case '\t':
4811 m_SB.Add("\\t");
4812 break;
4813 default:
4814 VMA_ASSERT(0 && "Character not currently supported.");
4815 break;
4816 }
4817 }
4818 }
4819
ContinueString(uint32_t n)4820 void VmaJsonWriter::ContinueString(uint32_t n)
4821 {
4822 VMA_ASSERT(m_InsideString);
4823 m_SB.AddNumber(n);
4824 }
4825
ContinueString(uint64_t n)4826 void VmaJsonWriter::ContinueString(uint64_t n)
4827 {
4828 VMA_ASSERT(m_InsideString);
4829 m_SB.AddNumber(n);
4830 }
4831
ContinueString_Pointer(const void * ptr)4832 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
4833 {
4834 VMA_ASSERT(m_InsideString);
4835 m_SB.AddPointer(ptr);
4836 }
4837
EndString(const char * pStr)4838 void VmaJsonWriter::EndString(const char* pStr)
4839 {
4840 VMA_ASSERT(m_InsideString);
4841 if(pStr != VMA_NULL && pStr[0] != '\0')
4842 {
4843 ContinueString(pStr);
4844 }
4845 m_SB.Add('"');
4846 m_InsideString = false;
4847 }
4848
WriteNumber(uint32_t n)4849 void VmaJsonWriter::WriteNumber(uint32_t n)
4850 {
4851 VMA_ASSERT(!m_InsideString);
4852 BeginValue(false);
4853 m_SB.AddNumber(n);
4854 }
4855
WriteNumber(uint64_t n)4856 void VmaJsonWriter::WriteNumber(uint64_t n)
4857 {
4858 VMA_ASSERT(!m_InsideString);
4859 BeginValue(false);
4860 m_SB.AddNumber(n);
4861 }
4862
WriteBool(bool b)4863 void VmaJsonWriter::WriteBool(bool b)
4864 {
4865 VMA_ASSERT(!m_InsideString);
4866 BeginValue(false);
4867 m_SB.Add(b ? "true" : "false");
4868 }
4869
WriteNull()4870 void VmaJsonWriter::WriteNull()
4871 {
4872 VMA_ASSERT(!m_InsideString);
4873 BeginValue(false);
4874 m_SB.Add("null");
4875 }
4876
BeginValue(bool isString)4877 void VmaJsonWriter::BeginValue(bool isString)
4878 {
4879 if(!m_Stack.empty())
4880 {
4881 StackItem& currItem = m_Stack.back();
4882 if(currItem.type == COLLECTION_TYPE_OBJECT &&
4883 currItem.valueCount % 2 == 0)
4884 {
4885 VMA_ASSERT(isString);
4886 }
4887
4888 if(currItem.type == COLLECTION_TYPE_OBJECT &&
4889 currItem.valueCount % 2 != 0)
4890 {
4891 m_SB.Add(": ");
4892 }
4893 else if(currItem.valueCount > 0)
4894 {
4895 m_SB.Add(", ");
4896 WriteIndent();
4897 }
4898 else
4899 {
4900 WriteIndent();
4901 }
4902 ++currItem.valueCount;
4903 }
4904 }
4905
WriteIndent(bool oneLess)4906 void VmaJsonWriter::WriteIndent(bool oneLess)
4907 {
4908 if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
4909 {
4910 m_SB.AddNewLine();
4911
4912 size_t count = m_Stack.size();
4913 if(count > 0 && oneLess)
4914 {
4915 --count;
4916 }
4917 for(size_t i = 0; i < count; ++i)
4918 {
4919 m_SB.Add(INDENT);
4920 }
4921 }
4922 }
4923
4924 #endif // #if VMA_STATS_STRING_ENABLED
4925
4926 ////////////////////////////////////////////////////////////////////////////////
4927
SetUserData(VmaAllocator hAllocator,void * pUserData)4928 void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
4929 {
4930 if(IsUserDataString())
4931 {
4932 VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
4933
4934 FreeUserDataString(hAllocator);
4935
4936 if(pUserData != VMA_NULL)
4937 {
4938 const char* const newStrSrc = (char*)pUserData;
4939 const size_t newStrLen = strlen(newStrSrc);
4940 char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
4941 memcpy(newStrDst, newStrSrc, newStrLen + 1);
4942 m_pUserData = newStrDst;
4943 }
4944 }
4945 else
4946 {
4947 m_pUserData = pUserData;
4948 }
4949 }
4950
ChangeBlockAllocation(VmaAllocator hAllocator,VmaDeviceMemoryBlock * block,VkDeviceSize offset)4951 void VmaAllocation_T::ChangeBlockAllocation(
4952 VmaAllocator hAllocator,
4953 VmaDeviceMemoryBlock* block,
4954 VkDeviceSize offset)
4955 {
4956 VMA_ASSERT(block != VMA_NULL);
4957 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
4958
4959 // Move mapping reference counter from old block to new block.
4960 if(block != m_BlockAllocation.m_Block)
4961 {
4962 uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
4963 if(IsPersistentMap())
4964 ++mapRefCount;
4965 m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
4966 block->Map(hAllocator, mapRefCount, VMA_NULL);
4967 }
4968
4969 m_BlockAllocation.m_Block = block;
4970 m_BlockAllocation.m_Offset = offset;
4971 }
4972
GetOffset()4973 VkDeviceSize VmaAllocation_T::GetOffset() const
4974 {
4975 switch(m_Type)
4976 {
4977 case ALLOCATION_TYPE_BLOCK:
4978 return m_BlockAllocation.m_Offset;
4979 case ALLOCATION_TYPE_DEDICATED:
4980 return 0;
4981 default:
4982 VMA_ASSERT(0);
4983 return 0;
4984 }
4985 }
4986
GetMemory()4987 VkDeviceMemory VmaAllocation_T::GetMemory() const
4988 {
4989 switch(m_Type)
4990 {
4991 case ALLOCATION_TYPE_BLOCK:
4992 return m_BlockAllocation.m_Block->GetDeviceMemory();
4993 case ALLOCATION_TYPE_DEDICATED:
4994 return m_DedicatedAllocation.m_hMemory;
4995 default:
4996 VMA_ASSERT(0);
4997 return VK_NULL_HANDLE;
4998 }
4999 }
5000
GetMemoryTypeIndex()5001 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
5002 {
5003 switch(m_Type)
5004 {
5005 case ALLOCATION_TYPE_BLOCK:
5006 return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
5007 case ALLOCATION_TYPE_DEDICATED:
5008 return m_DedicatedAllocation.m_MemoryTypeIndex;
5009 default:
5010 VMA_ASSERT(0);
5011 return UINT32_MAX;
5012 }
5013 }
5014
GetMappedData()5015 void* VmaAllocation_T::GetMappedData() const
5016 {
5017 switch(m_Type)
5018 {
5019 case ALLOCATION_TYPE_BLOCK:
5020 if(m_MapCount != 0)
5021 {
5022 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
5023 VMA_ASSERT(pBlockData != VMA_NULL);
5024 return (char*)pBlockData + m_BlockAllocation.m_Offset;
5025 }
5026 else
5027 {
5028 return VMA_NULL;
5029 }
5030 break;
5031 case ALLOCATION_TYPE_DEDICATED:
5032 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
5033 return m_DedicatedAllocation.m_pMappedData;
5034 default:
5035 VMA_ASSERT(0);
5036 return VMA_NULL;
5037 }
5038 }
5039
CanBecomeLost()5040 bool VmaAllocation_T::CanBecomeLost() const
5041 {
5042 switch(m_Type)
5043 {
5044 case ALLOCATION_TYPE_BLOCK:
5045 return m_BlockAllocation.m_CanBecomeLost;
5046 case ALLOCATION_TYPE_DEDICATED:
5047 return false;
5048 default:
5049 VMA_ASSERT(0);
5050 return false;
5051 }
5052 }
5053
GetPool()5054 VmaPool VmaAllocation_T::GetPool() const
5055 {
5056 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
5057 return m_BlockAllocation.m_hPool;
5058 }
5059
MakeLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)5060 bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
5061 {
5062 VMA_ASSERT(CanBecomeLost());
5063
5064 /*
5065 Warning: This is a carefully designed algorithm.
5066 Do not modify unless you really know what you're doing :)
5067 */
5068 uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
5069 for(;;)
5070 {
5071 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
5072 {
5073 VMA_ASSERT(0);
5074 return false;
5075 }
5076 else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
5077 {
5078 return false;
5079 }
5080 else // Last use time earlier than current time.
5081 {
5082 if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
5083 {
5084 // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
5085 // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
5086 return true;
5087 }
5088 }
5089 }
5090 }
5091
FreeUserDataString(VmaAllocator hAllocator)5092 void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
5093 {
5094 VMA_ASSERT(IsUserDataString());
5095 if(m_pUserData != VMA_NULL)
5096 {
5097 char* const oldStr = (char*)m_pUserData;
5098 const size_t oldStrLen = strlen(oldStr);
5099 vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
5100 m_pUserData = VMA_NULL;
5101 }
5102 }
5103
BlockAllocMap()5104 void VmaAllocation_T::BlockAllocMap()
5105 {
5106 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
5107
5108 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
5109 {
5110 ++m_MapCount;
5111 }
5112 else
5113 {
5114 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
5115 }
5116 }
5117
BlockAllocUnmap()5118 void VmaAllocation_T::BlockAllocUnmap()
5119 {
5120 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
5121
5122 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
5123 {
5124 --m_MapCount;
5125 }
5126 else
5127 {
5128 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
5129 }
5130 }
5131
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)5132 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
5133 {
5134 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
5135
5136 if(m_MapCount != 0)
5137 {
5138 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
5139 {
5140 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
5141 *ppData = m_DedicatedAllocation.m_pMappedData;
5142 ++m_MapCount;
5143 return VK_SUCCESS;
5144 }
5145 else
5146 {
5147 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
5148 return VK_ERROR_MEMORY_MAP_FAILED;
5149 }
5150 }
5151 else
5152 {
5153 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
5154 hAllocator->m_hDevice,
5155 m_DedicatedAllocation.m_hMemory,
5156 0, // offset
5157 VK_WHOLE_SIZE,
5158 0, // flags
5159 ppData);
5160 if(result == VK_SUCCESS)
5161 {
5162 m_DedicatedAllocation.m_pMappedData = *ppData;
5163 m_MapCount = 1;
5164 }
5165 return result;
5166 }
5167 }
5168
DedicatedAllocUnmap(VmaAllocator hAllocator)5169 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
5170 {
5171 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
5172
5173 if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
5174 {
5175 --m_MapCount;
5176 if(m_MapCount == 0)
5177 {
5178 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
5179 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
5180 hAllocator->m_hDevice,
5181 m_DedicatedAllocation.m_hMemory);
5182 }
5183 }
5184 else
5185 {
5186 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
5187 }
5188 }
5189
5190 #if VMA_STATS_STRING_ENABLED
5191
5192 // Correspond to values of enum VmaSuballocationType.
5193 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
5194 "FREE",
5195 "UNKNOWN",
5196 "BUFFER",
5197 "IMAGE_UNKNOWN",
5198 "IMAGE_LINEAR",
5199 "IMAGE_OPTIMAL",
5200 };
5201
VmaPrintStatInfo(VmaJsonWriter & json,const VmaStatInfo & stat)5202 static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
5203 {
5204 json.BeginObject();
5205
5206 json.WriteString("Blocks");
5207 json.WriteNumber(stat.blockCount);
5208
5209 json.WriteString("Allocations");
5210 json.WriteNumber(stat.allocationCount);
5211
5212 json.WriteString("UnusedRanges");
5213 json.WriteNumber(stat.unusedRangeCount);
5214
5215 json.WriteString("UsedBytes");
5216 json.WriteNumber(stat.usedBytes);
5217
5218 json.WriteString("UnusedBytes");
5219 json.WriteNumber(stat.unusedBytes);
5220
5221 if(stat.allocationCount > 1)
5222 {
5223 json.WriteString("AllocationSize");
5224 json.BeginObject(true);
5225 json.WriteString("Min");
5226 json.WriteNumber(stat.allocationSizeMin);
5227 json.WriteString("Avg");
5228 json.WriteNumber(stat.allocationSizeAvg);
5229 json.WriteString("Max");
5230 json.WriteNumber(stat.allocationSizeMax);
5231 json.EndObject();
5232 }
5233
5234 if(stat.unusedRangeCount > 1)
5235 {
5236 json.WriteString("UnusedRangeSize");
5237 json.BeginObject(true);
5238 json.WriteString("Min");
5239 json.WriteNumber(stat.unusedRangeSizeMin);
5240 json.WriteString("Avg");
5241 json.WriteNumber(stat.unusedRangeSizeAvg);
5242 json.WriteString("Max");
5243 json.WriteNumber(stat.unusedRangeSizeMax);
5244 json.EndObject();
5245 }
5246
5247 json.EndObject();
5248 }
5249
5250 #endif // #if VMA_STATS_STRING_ENABLED
5251
5252 struct VmaSuballocationItemSizeLess
5253 {
operatorVmaSuballocationItemSizeLess5254 bool operator()(
5255 const VmaSuballocationList::iterator lhs,
5256 const VmaSuballocationList::iterator rhs) const
5257 {
5258 return lhs->size < rhs->size;
5259 }
operatorVmaSuballocationItemSizeLess5260 bool operator()(
5261 const VmaSuballocationList::iterator lhs,
5262 VkDeviceSize rhsSize) const
5263 {
5264 return lhs->size < rhsSize;
5265 }
5266 };
5267
5268 ////////////////////////////////////////////////////////////////////////////////
5269 // class VmaBlockMetadata
5270
VmaBlockMetadata(VmaAllocator hAllocator)5271 VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
5272 m_Size(0),
5273 m_FreeCount(0),
5274 m_SumFreeSize(0),
5275 m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
5276 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
5277 {
5278 }
5279
~VmaBlockMetadata()5280 VmaBlockMetadata::~VmaBlockMetadata()
5281 {
5282 }
5283
Init(VkDeviceSize size)5284 void VmaBlockMetadata::Init(VkDeviceSize size)
5285 {
5286 m_Size = size;
5287 m_FreeCount = 1;
5288 m_SumFreeSize = size;
5289
5290 VmaSuballocation suballoc = {};
5291 suballoc.offset = 0;
5292 suballoc.size = size;
5293 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5294 suballoc.hAllocation = VK_NULL_HANDLE;
5295
5296 m_Suballocations.push_back(suballoc);
5297 VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
5298 --suballocItem;
5299 m_FreeSuballocationsBySize.push_back(suballocItem);
5300 }
5301
Validate()5302 bool VmaBlockMetadata::Validate() const
5303 {
5304 if(m_Suballocations.empty())
5305 {
5306 return false;
5307 }
5308
5309 // Expected offset of new suballocation as calculates from previous ones.
5310 VkDeviceSize calculatedOffset = 0;
5311 // Expected number of free suballocations as calculated from traversing their list.
5312 uint32_t calculatedFreeCount = 0;
5313 // Expected sum size of free suballocations as calculated from traversing their list.
5314 VkDeviceSize calculatedSumFreeSize = 0;
5315 // Expected number of free suballocations that should be registered in
5316 // m_FreeSuballocationsBySize calculated from traversing their list.
5317 size_t freeSuballocationsToRegister = 0;
5318 // True if previous visisted suballocation was free.
5319 bool prevFree = false;
5320
5321 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
5322 suballocItem != m_Suballocations.cend();
5323 ++suballocItem)
5324 {
5325 const VmaSuballocation& subAlloc = *suballocItem;
5326
5327 // Actual offset of this suballocation doesn't match expected one.
5328 if(subAlloc.offset != calculatedOffset)
5329 {
5330 return false;
5331 }
5332
5333 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
5334 // Two adjacent free suballocations are invalid. They should be merged.
5335 if(prevFree && currFree)
5336 {
5337 return false;
5338 }
5339
5340 if(currFree != (subAlloc.hAllocation == VK_NULL_HANDLE))
5341 {
5342 return false;
5343 }
5344
5345 if(currFree)
5346 {
5347 calculatedSumFreeSize += subAlloc.size;
5348 ++calculatedFreeCount;
5349 if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5350 {
5351 ++freeSuballocationsToRegister;
5352 }
5353 }
5354 else
5355 {
5356 if(subAlloc.hAllocation->GetOffset() != subAlloc.offset)
5357 {
5358 return false;
5359 }
5360 if(subAlloc.hAllocation->GetSize() != subAlloc.size)
5361 {
5362 return false;
5363 }
5364 }
5365
5366 calculatedOffset += subAlloc.size;
5367 prevFree = currFree;
5368 }
5369
5370 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
5371 // match expected one.
5372 if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister)
5373 {
5374 return false;
5375 }
5376
5377 VkDeviceSize lastSize = 0;
5378 for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
5379 {
5380 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
5381
5382 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
5383 if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
5384 {
5385 return false;
5386 }
5387 // They must be sorted by size ascending.
5388 if(suballocItem->size < lastSize)
5389 {
5390 return false;
5391 }
5392
5393 lastSize = suballocItem->size;
5394 }
5395
5396 // Check if totals match calculacted values.
5397 if(!ValidateFreeSuballocationList() ||
5398 (calculatedOffset != m_Size) ||
5399 (calculatedSumFreeSize != m_SumFreeSize) ||
5400 (calculatedFreeCount != m_FreeCount))
5401 {
5402 return false;
5403 }
5404
5405 return true;
5406 }
5407
GetUnusedRangeSizeMax()5408 VkDeviceSize VmaBlockMetadata::GetUnusedRangeSizeMax() const
5409 {
5410 if(!m_FreeSuballocationsBySize.empty())
5411 {
5412 return m_FreeSuballocationsBySize.back()->size;
5413 }
5414 else
5415 {
5416 return 0;
5417 }
5418 }
5419
IsEmpty()5420 bool VmaBlockMetadata::IsEmpty() const
5421 {
5422 return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
5423 }
5424
CalcAllocationStatInfo(VmaStatInfo & outInfo)5425 void VmaBlockMetadata::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
5426 {
5427 outInfo.blockCount = 1;
5428
5429 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
5430 outInfo.allocationCount = rangeCount - m_FreeCount;
5431 outInfo.unusedRangeCount = m_FreeCount;
5432
5433 outInfo.unusedBytes = m_SumFreeSize;
5434 outInfo.usedBytes = m_Size - outInfo.unusedBytes;
5435
5436 outInfo.allocationSizeMin = UINT64_MAX;
5437 outInfo.allocationSizeMax = 0;
5438 outInfo.unusedRangeSizeMin = UINT64_MAX;
5439 outInfo.unusedRangeSizeMax = 0;
5440
5441 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
5442 suballocItem != m_Suballocations.cend();
5443 ++suballocItem)
5444 {
5445 const VmaSuballocation& suballoc = *suballocItem;
5446 if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
5447 {
5448 outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
5449 outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
5450 }
5451 else
5452 {
5453 outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
5454 outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
5455 }
5456 }
5457 }
5458
AddPoolStats(VmaPoolStats & inoutStats)5459 void VmaBlockMetadata::AddPoolStats(VmaPoolStats& inoutStats) const
5460 {
5461 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
5462
5463 inoutStats.size += m_Size;
5464 inoutStats.unusedSize += m_SumFreeSize;
5465 inoutStats.allocationCount += rangeCount - m_FreeCount;
5466 inoutStats.unusedRangeCount += m_FreeCount;
5467 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
5468 }
5469
5470 #if VMA_STATS_STRING_ENABLED
5471
PrintDetailedMap(class VmaJsonWriter & json)5472 void VmaBlockMetadata::PrintDetailedMap(class VmaJsonWriter& json) const
5473 {
5474 json.BeginObject();
5475
5476 json.WriteString("TotalBytes");
5477 json.WriteNumber(m_Size);
5478
5479 json.WriteString("UnusedBytes");
5480 json.WriteNumber(m_SumFreeSize);
5481
5482 json.WriteString("Allocations");
5483 json.WriteNumber((uint64_t)m_Suballocations.size() - m_FreeCount);
5484
5485 json.WriteString("UnusedRanges");
5486 json.WriteNumber(m_FreeCount);
5487
5488 json.WriteString("Suballocations");
5489 json.BeginArray();
5490 size_t i = 0;
5491 for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
5492 suballocItem != m_Suballocations.cend();
5493 ++suballocItem, ++i)
5494 {
5495 json.BeginObject(true);
5496
5497 json.WriteString("Type");
5498 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[suballocItem->type]);
5499
5500 json.WriteString("Size");
5501 json.WriteNumber(suballocItem->size);
5502
5503 json.WriteString("Offset");
5504 json.WriteNumber(suballocItem->offset);
5505
5506 if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
5507 {
5508 const void* pUserData = suballocItem->hAllocation->GetUserData();
5509 if(pUserData != VMA_NULL)
5510 {
5511 json.WriteString("UserData");
5512 if(suballocItem->hAllocation->IsUserDataString())
5513 {
5514 json.WriteString((const char*)pUserData);
5515 }
5516 else
5517 {
5518 json.BeginString();
5519 json.ContinueString_Pointer(pUserData);
5520 json.EndString();
5521 }
5522 }
5523 }
5524
5525 json.EndObject();
5526 }
5527 json.EndArray();
5528
5529 json.EndObject();
5530 }
5531
5532 #endif // #if VMA_STATS_STRING_ENABLED
5533
5534 /*
5535 How many suitable free suballocations to analyze before choosing best one.
5536 - Set to 1 to use First-Fit algorithm - first suitable free suballocation will
5537 be chosen.
5538 - Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free
5539 suballocations will be analized and best one will be chosen.
5540 - Any other value is also acceptable.
5541 */
5542 //static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8;
5543
CreateFirstAllocationRequest(VmaAllocationRequest * pAllocationRequest)5544 void VmaBlockMetadata::CreateFirstAllocationRequest(VmaAllocationRequest* pAllocationRequest)
5545 {
5546 VMA_ASSERT(IsEmpty());
5547 pAllocationRequest->offset = 0;
5548 pAllocationRequest->sumFreeSize = m_SumFreeSize;
5549 pAllocationRequest->sumItemSize = 0;
5550 pAllocationRequest->item = m_Suballocations.begin();
5551 pAllocationRequest->itemsToMakeLostCount = 0;
5552 }
5553
CreateAllocationRequest(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,bool canMakeOtherLost,VmaAllocationRequest * pAllocationRequest)5554 bool VmaBlockMetadata::CreateAllocationRequest(
5555 uint32_t currentFrameIndex,
5556 uint32_t frameInUseCount,
5557 VkDeviceSize bufferImageGranularity,
5558 VkDeviceSize allocSize,
5559 VkDeviceSize allocAlignment,
5560 VmaSuballocationType allocType,
5561 bool canMakeOtherLost,
5562 VmaAllocationRequest* pAllocationRequest)
5563 {
5564 VMA_ASSERT(allocSize > 0);
5565 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
5566 VMA_ASSERT(pAllocationRequest != VMA_NULL);
5567 VMA_HEAVY_ASSERT(Validate());
5568
5569 // There is not enough total free space in this block to fullfill the request: Early return.
5570 if(canMakeOtherLost == false && m_SumFreeSize < allocSize)
5571 {
5572 return false;
5573 }
5574
5575 // New algorithm, efficiently searching freeSuballocationsBySize.
5576 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
5577 if(freeSuballocCount > 0)
5578 {
5579 if(VMA_BEST_FIT)
5580 {
5581 // Find first free suballocation with size not less than allocSize.
5582 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
5583 m_FreeSuballocationsBySize.data(),
5584 m_FreeSuballocationsBySize.data() + freeSuballocCount,
5585 allocSize,
5586 VmaSuballocationItemSizeLess());
5587 size_t index = it - m_FreeSuballocationsBySize.data();
5588 for(; index < freeSuballocCount; ++index)
5589 {
5590 if(CheckAllocation(
5591 currentFrameIndex,
5592 frameInUseCount,
5593 bufferImageGranularity,
5594 allocSize,
5595 allocAlignment,
5596 allocType,
5597 m_FreeSuballocationsBySize[index],
5598 false, // canMakeOtherLost
5599 &pAllocationRequest->offset,
5600 &pAllocationRequest->itemsToMakeLostCount,
5601 &pAllocationRequest->sumFreeSize,
5602 &pAllocationRequest->sumItemSize))
5603 {
5604 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
5605 return true;
5606 }
5607 }
5608 }
5609 else
5610 {
5611 // Search staring from biggest suballocations.
5612 for(size_t index = freeSuballocCount; index--; )
5613 {
5614 if(CheckAllocation(
5615 currentFrameIndex,
5616 frameInUseCount,
5617 bufferImageGranularity,
5618 allocSize,
5619 allocAlignment,
5620 allocType,
5621 m_FreeSuballocationsBySize[index],
5622 false, // canMakeOtherLost
5623 &pAllocationRequest->offset,
5624 &pAllocationRequest->itemsToMakeLostCount,
5625 &pAllocationRequest->sumFreeSize,
5626 &pAllocationRequest->sumItemSize))
5627 {
5628 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
5629 return true;
5630 }
5631 }
5632 }
5633 }
5634
5635 if(canMakeOtherLost)
5636 {
5637 // Brute-force algorithm. TODO: Come up with something better.
5638
5639 pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
5640 pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
5641
5642 VmaAllocationRequest tmpAllocRequest = {};
5643 for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
5644 suballocIt != m_Suballocations.end();
5645 ++suballocIt)
5646 {
5647 if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
5648 suballocIt->hAllocation->CanBecomeLost())
5649 {
5650 if(CheckAllocation(
5651 currentFrameIndex,
5652 frameInUseCount,
5653 bufferImageGranularity,
5654 allocSize,
5655 allocAlignment,
5656 allocType,
5657 suballocIt,
5658 canMakeOtherLost,
5659 &tmpAllocRequest.offset,
5660 &tmpAllocRequest.itemsToMakeLostCount,
5661 &tmpAllocRequest.sumFreeSize,
5662 &tmpAllocRequest.sumItemSize))
5663 {
5664 tmpAllocRequest.item = suballocIt;
5665
5666 if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost())
5667 {
5668 *pAllocationRequest = tmpAllocRequest;
5669 }
5670 }
5671 }
5672 }
5673
5674 if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
5675 {
5676 return true;
5677 }
5678 }
5679
5680 return false;
5681 }
5682
MakeRequestedAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount,VmaAllocationRequest * pAllocationRequest)5683 bool VmaBlockMetadata::MakeRequestedAllocationsLost(
5684 uint32_t currentFrameIndex,
5685 uint32_t frameInUseCount,
5686 VmaAllocationRequest* pAllocationRequest)
5687 {
5688 while(pAllocationRequest->itemsToMakeLostCount > 0)
5689 {
5690 if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
5691 {
5692 ++pAllocationRequest->item;
5693 }
5694 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
5695 VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
5696 VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
5697 if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
5698 {
5699 pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
5700 --pAllocationRequest->itemsToMakeLostCount;
5701 }
5702 else
5703 {
5704 return false;
5705 }
5706 }
5707
5708 VMA_HEAVY_ASSERT(Validate());
5709 VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
5710 VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
5711
5712 return true;
5713 }
5714
MakeAllocationsLost(uint32_t currentFrameIndex,uint32_t frameInUseCount)5715 uint32_t VmaBlockMetadata::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
5716 {
5717 uint32_t lostAllocationCount = 0;
5718 for(VmaSuballocationList::iterator it = m_Suballocations.begin();
5719 it != m_Suballocations.end();
5720 ++it)
5721 {
5722 if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
5723 it->hAllocation->CanBecomeLost() &&
5724 it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
5725 {
5726 it = FreeSuballocation(it);
5727 ++lostAllocationCount;
5728 }
5729 }
5730 return lostAllocationCount;
5731 }
5732
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,VkDeviceSize allocSize,VmaAllocation hAllocation)5733 void VmaBlockMetadata::Alloc(
5734 const VmaAllocationRequest& request,
5735 VmaSuballocationType type,
5736 VkDeviceSize allocSize,
5737 VmaAllocation hAllocation)
5738 {
5739 VMA_ASSERT(request.item != m_Suballocations.end());
5740 VmaSuballocation& suballoc = *request.item;
5741 // Given suballocation is a free block.
5742 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
5743 // Given offset is inside this suballocation.
5744 VMA_ASSERT(request.offset >= suballoc.offset);
5745 const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
5746 VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
5747 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
5748
5749 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
5750 // it to become used.
5751 UnregisterFreeSuballocation(request.item);
5752
5753 suballoc.offset = request.offset;
5754 suballoc.size = allocSize;
5755 suballoc.type = type;
5756 suballoc.hAllocation = hAllocation;
5757
5758 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
5759 if(paddingEnd)
5760 {
5761 VmaSuballocation paddingSuballoc = {};
5762 paddingSuballoc.offset = request.offset + allocSize;
5763 paddingSuballoc.size = paddingEnd;
5764 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5765 VmaSuballocationList::iterator next = request.item;
5766 ++next;
5767 const VmaSuballocationList::iterator paddingEndItem =
5768 m_Suballocations.insert(next, paddingSuballoc);
5769 RegisterFreeSuballocation(paddingEndItem);
5770 }
5771
5772 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
5773 if(paddingBegin)
5774 {
5775 VmaSuballocation paddingSuballoc = {};
5776 paddingSuballoc.offset = request.offset - paddingBegin;
5777 paddingSuballoc.size = paddingBegin;
5778 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
5779 const VmaSuballocationList::iterator paddingBeginItem =
5780 m_Suballocations.insert(request.item, paddingSuballoc);
5781 RegisterFreeSuballocation(paddingBeginItem);
5782 }
5783
5784 // Update totals.
5785 m_FreeCount = m_FreeCount - 1;
5786 if(paddingBegin > 0)
5787 {
5788 ++m_FreeCount;
5789 }
5790 if(paddingEnd > 0)
5791 {
5792 ++m_FreeCount;
5793 }
5794 m_SumFreeSize -= allocSize;
5795 }
5796
Free(const VmaAllocation allocation)5797 void VmaBlockMetadata::Free(const VmaAllocation allocation)
5798 {
5799 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
5800 suballocItem != m_Suballocations.end();
5801 ++suballocItem)
5802 {
5803 VmaSuballocation& suballoc = *suballocItem;
5804 if(suballoc.hAllocation == allocation)
5805 {
5806 FreeSuballocation(suballocItem);
5807 VMA_HEAVY_ASSERT(Validate());
5808 return;
5809 }
5810 }
5811 VMA_ASSERT(0 && "Not found!");
5812 }
5813
FreeAtOffset(VkDeviceSize offset)5814 void VmaBlockMetadata::FreeAtOffset(VkDeviceSize offset)
5815 {
5816 for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
5817 suballocItem != m_Suballocations.end();
5818 ++suballocItem)
5819 {
5820 VmaSuballocation& suballoc = *suballocItem;
5821 if(suballoc.offset == offset)
5822 {
5823 FreeSuballocation(suballocItem);
5824 return;
5825 }
5826 }
5827 VMA_ASSERT(0 && "Not found!");
5828 }
5829
ValidateFreeSuballocationList()5830 bool VmaBlockMetadata::ValidateFreeSuballocationList() const
5831 {
5832 VkDeviceSize lastSize = 0;
5833 for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
5834 {
5835 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
5836
5837 if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
5838 {
5839 VMA_ASSERT(0);
5840 return false;
5841 }
5842 if(it->size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
5843 {
5844 VMA_ASSERT(0);
5845 return false;
5846 }
5847 if(it->size < lastSize)
5848 {
5849 VMA_ASSERT(0);
5850 return false;
5851 }
5852
5853 lastSize = it->size;
5854 }
5855 return true;
5856 }
5857
CheckAllocation(uint32_t currentFrameIndex,uint32_t frameInUseCount,VkDeviceSize bufferImageGranularity,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaSuballocationList::const_iterator suballocItem,bool canMakeOtherLost,VkDeviceSize * pOffset,size_t * itemsToMakeLostCount,VkDeviceSize * pSumFreeSize,VkDeviceSize * pSumItemSize)5858 bool VmaBlockMetadata::CheckAllocation(
5859 uint32_t currentFrameIndex,
5860 uint32_t frameInUseCount,
5861 VkDeviceSize bufferImageGranularity,
5862 VkDeviceSize allocSize,
5863 VkDeviceSize allocAlignment,
5864 VmaSuballocationType allocType,
5865 VmaSuballocationList::const_iterator suballocItem,
5866 bool canMakeOtherLost,
5867 VkDeviceSize* pOffset,
5868 size_t* itemsToMakeLostCount,
5869 VkDeviceSize* pSumFreeSize,
5870 VkDeviceSize* pSumItemSize) const
5871 {
5872 VMA_ASSERT(allocSize > 0);
5873 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
5874 VMA_ASSERT(suballocItem != m_Suballocations.cend());
5875 VMA_ASSERT(pOffset != VMA_NULL);
5876
5877 *itemsToMakeLostCount = 0;
5878 *pSumFreeSize = 0;
5879 *pSumItemSize = 0;
5880
5881 if(canMakeOtherLost)
5882 {
5883 if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5884 {
5885 *pSumFreeSize = suballocItem->size;
5886 }
5887 else
5888 {
5889 if(suballocItem->hAllocation->CanBecomeLost() &&
5890 suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5891 {
5892 ++*itemsToMakeLostCount;
5893 *pSumItemSize = suballocItem->size;
5894 }
5895 else
5896 {
5897 return false;
5898 }
5899 }
5900
5901 // Remaining size is too small for this request: Early return.
5902 if(m_Size - suballocItem->offset < allocSize)
5903 {
5904 return false;
5905 }
5906
5907 // Start from offset equal to beginning of this suballocation.
5908 *pOffset = suballocItem->offset;
5909
5910 // Apply VMA_DEBUG_MARGIN at the beginning.
5911 if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin())
5912 {
5913 *pOffset += VMA_DEBUG_MARGIN;
5914 }
5915
5916 // Apply alignment.
5917 const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
5918 *pOffset = VmaAlignUp(*pOffset, alignment);
5919
5920 // Check previous suballocations for BufferImageGranularity conflicts.
5921 // Make bigger alignment if necessary.
5922 if(bufferImageGranularity > 1)
5923 {
5924 bool bufferImageGranularityConflict = false;
5925 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
5926 while(prevSuballocItem != m_Suballocations.cbegin())
5927 {
5928 --prevSuballocItem;
5929 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
5930 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
5931 {
5932 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
5933 {
5934 bufferImageGranularityConflict = true;
5935 break;
5936 }
5937 }
5938 else
5939 // Already on previous page.
5940 break;
5941 }
5942 if(bufferImageGranularityConflict)
5943 {
5944 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
5945 }
5946 }
5947
5948 // Now that we have final *pOffset, check if we are past suballocItem.
5949 // If yes, return false - this function should be called for another suballocItem as starting point.
5950 if(*pOffset >= suballocItem->offset + suballocItem->size)
5951 {
5952 return false;
5953 }
5954
5955 // Calculate padding at the beginning based on current offset.
5956 const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
5957
5958 // Calculate required margin at the end if this is not last suballocation.
5959 VmaSuballocationList::const_iterator next = suballocItem;
5960 ++next;
5961 const VkDeviceSize requiredEndMargin =
5962 (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
5963
5964 const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
5965 // Another early return check.
5966 if(suballocItem->offset + totalSize > m_Size)
5967 {
5968 return false;
5969 }
5970
5971 // Advance lastSuballocItem until desired size is reached.
5972 // Update itemsToMakeLostCount.
5973 VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
5974 if(totalSize > suballocItem->size)
5975 {
5976 VkDeviceSize remainingSize = totalSize - suballocItem->size;
5977 while(remainingSize > 0)
5978 {
5979 ++lastSuballocItem;
5980 if(lastSuballocItem == m_Suballocations.cend())
5981 {
5982 return false;
5983 }
5984 if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
5985 {
5986 *pSumFreeSize += lastSuballocItem->size;
5987 }
5988 else
5989 {
5990 VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
5991 if(lastSuballocItem->hAllocation->CanBecomeLost() &&
5992 lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
5993 {
5994 ++*itemsToMakeLostCount;
5995 *pSumItemSize += lastSuballocItem->size;
5996 }
5997 else
5998 {
5999 return false;
6000 }
6001 }
6002 remainingSize = (lastSuballocItem->size < remainingSize) ?
6003 remainingSize - lastSuballocItem->size : 0;
6004 }
6005 }
6006
6007 // Check next suballocations for BufferImageGranularity conflicts.
6008 // If conflict exists, we must mark more allocations lost or fail.
6009 if(bufferImageGranularity > 1)
6010 {
6011 VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
6012 ++nextSuballocItem;
6013 while(nextSuballocItem != m_Suballocations.cend())
6014 {
6015 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
6016 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
6017 {
6018 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
6019 {
6020 VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
6021 if(nextSuballoc.hAllocation->CanBecomeLost() &&
6022 nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
6023 {
6024 ++*itemsToMakeLostCount;
6025 }
6026 else
6027 {
6028 return false;
6029 }
6030 }
6031 }
6032 else
6033 {
6034 // Already on next page.
6035 break;
6036 }
6037 ++nextSuballocItem;
6038 }
6039 }
6040 }
6041 else
6042 {
6043 const VmaSuballocation& suballoc = *suballocItem;
6044 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
6045
6046 *pSumFreeSize = suballoc.size;
6047
6048 // Size of this suballocation is too small for this request: Early return.
6049 if(suballoc.size < allocSize)
6050 {
6051 return false;
6052 }
6053
6054 // Start from offset equal to beginning of this suballocation.
6055 *pOffset = suballoc.offset;
6056
6057 // Apply VMA_DEBUG_MARGIN at the beginning.
6058 if((VMA_DEBUG_MARGIN > 0) && suballocItem != m_Suballocations.cbegin())
6059 {
6060 *pOffset += VMA_DEBUG_MARGIN;
6061 }
6062
6063 // Apply alignment.
6064 const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
6065 *pOffset = VmaAlignUp(*pOffset, alignment);
6066
6067 // Check previous suballocations for BufferImageGranularity conflicts.
6068 // Make bigger alignment if necessary.
6069 if(bufferImageGranularity > 1)
6070 {
6071 bool bufferImageGranularityConflict = false;
6072 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
6073 while(prevSuballocItem != m_Suballocations.cbegin())
6074 {
6075 --prevSuballocItem;
6076 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
6077 if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
6078 {
6079 if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
6080 {
6081 bufferImageGranularityConflict = true;
6082 break;
6083 }
6084 }
6085 else
6086 // Already on previous page.
6087 break;
6088 }
6089 if(bufferImageGranularityConflict)
6090 {
6091 *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
6092 }
6093 }
6094
6095 // Calculate padding at the beginning based on current offset.
6096 const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
6097
6098 // Calculate required margin at the end if this is not last suballocation.
6099 VmaSuballocationList::const_iterator next = suballocItem;
6100 ++next;
6101 const VkDeviceSize requiredEndMargin =
6102 (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
6103
6104 // Fail if requested size plus margin before and after is bigger than size of this suballocation.
6105 if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
6106 {
6107 return false;
6108 }
6109
6110 // Check next suballocations for BufferImageGranularity conflicts.
6111 // If conflict exists, allocation cannot be made here.
6112 if(bufferImageGranularity > 1)
6113 {
6114 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
6115 ++nextSuballocItem;
6116 while(nextSuballocItem != m_Suballocations.cend())
6117 {
6118 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
6119 if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
6120 {
6121 if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
6122 {
6123 return false;
6124 }
6125 }
6126 else
6127 {
6128 // Already on next page.
6129 break;
6130 }
6131 ++nextSuballocItem;
6132 }
6133 }
6134 }
6135
6136 // All tests passed: Success. pOffset is already filled.
6137 return true;
6138 }
6139
MergeFreeWithNext(VmaSuballocationList::iterator item)6140 void VmaBlockMetadata::MergeFreeWithNext(VmaSuballocationList::iterator item)
6141 {
6142 VMA_ASSERT(item != m_Suballocations.end());
6143 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
6144
6145 VmaSuballocationList::iterator nextItem = item;
6146 ++nextItem;
6147 VMA_ASSERT(nextItem != m_Suballocations.end());
6148 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
6149
6150 item->size += nextItem->size;
6151 --m_FreeCount;
6152 m_Suballocations.erase(nextItem);
6153 }
6154
FreeSuballocation(VmaSuballocationList::iterator suballocItem)6155 VmaSuballocationList::iterator VmaBlockMetadata::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
6156 {
6157 // Change this suballocation to be marked as free.
6158 VmaSuballocation& suballoc = *suballocItem;
6159 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
6160 suballoc.hAllocation = VK_NULL_HANDLE;
6161
6162 // Update totals.
6163 ++m_FreeCount;
6164 m_SumFreeSize += suballoc.size;
6165
6166 // Merge with previous and/or next suballocation if it's also free.
6167 bool mergeWithNext = false;
6168 bool mergeWithPrev = false;
6169
6170 VmaSuballocationList::iterator nextItem = suballocItem;
6171 ++nextItem;
6172 if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
6173 {
6174 mergeWithNext = true;
6175 }
6176
6177 VmaSuballocationList::iterator prevItem = suballocItem;
6178 if(suballocItem != m_Suballocations.begin())
6179 {
6180 --prevItem;
6181 if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
6182 {
6183 mergeWithPrev = true;
6184 }
6185 }
6186
6187 if(mergeWithNext)
6188 {
6189 UnregisterFreeSuballocation(nextItem);
6190 MergeFreeWithNext(suballocItem);
6191 }
6192
6193 if(mergeWithPrev)
6194 {
6195 UnregisterFreeSuballocation(prevItem);
6196 MergeFreeWithNext(prevItem);
6197 RegisterFreeSuballocation(prevItem);
6198 return prevItem;
6199 }
6200 else
6201 {
6202 RegisterFreeSuballocation(suballocItem);
6203 return suballocItem;
6204 }
6205 }
6206
RegisterFreeSuballocation(VmaSuballocationList::iterator item)6207 void VmaBlockMetadata::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
6208 {
6209 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
6210 VMA_ASSERT(item->size > 0);
6211
6212 // You may want to enable this validation at the beginning or at the end of
6213 // this function, depending on what do you want to check.
6214 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6215
6216 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6217 {
6218 if(m_FreeSuballocationsBySize.empty())
6219 {
6220 m_FreeSuballocationsBySize.push_back(item);
6221 }
6222 else
6223 {
6224 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
6225 }
6226 }
6227
6228 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6229 }
6230
6231
UnregisterFreeSuballocation(VmaSuballocationList::iterator item)6232 void VmaBlockMetadata::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
6233 {
6234 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
6235 VMA_ASSERT(item->size > 0);
6236
6237 // You may want to enable this validation at the beginning or at the end of
6238 // this function, depending on what do you want to check.
6239 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6240
6241 if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
6242 {
6243 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
6244 m_FreeSuballocationsBySize.data(),
6245 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
6246 item,
6247 VmaSuballocationItemSizeLess());
6248 for(size_t index = it - m_FreeSuballocationsBySize.data();
6249 index < m_FreeSuballocationsBySize.size();
6250 ++index)
6251 {
6252 if(m_FreeSuballocationsBySize[index] == item)
6253 {
6254 VmaVectorRemove(m_FreeSuballocationsBySize, index);
6255 return;
6256 }
6257 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
6258 }
6259 VMA_ASSERT(0 && "Not found.");
6260 }
6261
6262 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
6263 }
6264
6265 ////////////////////////////////////////////////////////////////////////////////
6266 // class VmaDeviceMemoryBlock
6267
VmaDeviceMemoryBlock(VmaAllocator hAllocator)6268 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
6269 m_Metadata(hAllocator),
6270 m_MemoryTypeIndex(UINT32_MAX),
6271 m_hMemory(VK_NULL_HANDLE),
6272 m_MapCount(0),
6273 m_pMappedData(VMA_NULL)
6274 {
6275 }
6276
Init(uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize)6277 void VmaDeviceMemoryBlock::Init(
6278 uint32_t newMemoryTypeIndex,
6279 VkDeviceMemory newMemory,
6280 VkDeviceSize newSize)
6281 {
6282 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
6283
6284 m_MemoryTypeIndex = newMemoryTypeIndex;
6285 m_hMemory = newMemory;
6286
6287 m_Metadata.Init(newSize);
6288 }
6289
Destroy(VmaAllocator allocator)6290 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
6291 {
6292 // This is the most important assert in the entire library.
6293 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
6294 VMA_ASSERT(m_Metadata.IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
6295
6296 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
6297 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_Metadata.GetSize(), m_hMemory);
6298 m_hMemory = VK_NULL_HANDLE;
6299 }
6300
Validate()6301 bool VmaDeviceMemoryBlock::Validate() const
6302 {
6303 if((m_hMemory == VK_NULL_HANDLE) ||
6304 (m_Metadata.GetSize() == 0))
6305 {
6306 return false;
6307 }
6308
6309 return m_Metadata.Validate();
6310 }
6311
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)6312 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
6313 {
6314 if(count == 0)
6315 {
6316 return VK_SUCCESS;
6317 }
6318
6319 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6320 if(m_MapCount != 0)
6321 {
6322 m_MapCount += count;
6323 VMA_ASSERT(m_pMappedData != VMA_NULL);
6324 if(ppData != VMA_NULL)
6325 {
6326 *ppData = m_pMappedData;
6327 }
6328 return VK_SUCCESS;
6329 }
6330 else
6331 {
6332 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
6333 hAllocator->m_hDevice,
6334 m_hMemory,
6335 0, // offset
6336 VK_WHOLE_SIZE,
6337 0, // flags
6338 &m_pMappedData);
6339 if(result == VK_SUCCESS)
6340 {
6341 if(ppData != VMA_NULL)
6342 {
6343 *ppData = m_pMappedData;
6344 }
6345 m_MapCount = count;
6346 }
6347 return result;
6348 }
6349 }
6350
Unmap(VmaAllocator hAllocator,uint32_t count)6351 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
6352 {
6353 if(count == 0)
6354 {
6355 return;
6356 }
6357
6358 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6359 if(m_MapCount >= count)
6360 {
6361 m_MapCount -= count;
6362 if(m_MapCount == 0)
6363 {
6364 m_pMappedData = VMA_NULL;
6365 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
6366 }
6367 }
6368 else
6369 {
6370 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
6371 }
6372 }
6373
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkBuffer hBuffer)6374 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
6375 const VmaAllocator hAllocator,
6376 const VmaAllocation hAllocation,
6377 VkBuffer hBuffer)
6378 {
6379 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
6380 hAllocation->GetBlock() == this);
6381 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
6382 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6383 return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
6384 hAllocator->m_hDevice,
6385 hBuffer,
6386 m_hMemory,
6387 hAllocation->GetOffset());
6388 }
6389
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkImage hImage)6390 VkResult VmaDeviceMemoryBlock::BindImageMemory(
6391 const VmaAllocator hAllocator,
6392 const VmaAllocation hAllocation,
6393 VkImage hImage)
6394 {
6395 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
6396 hAllocation->GetBlock() == this);
6397 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
6398 VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
6399 return hAllocator->GetVulkanFunctions().vkBindImageMemory(
6400 hAllocator->m_hDevice,
6401 hImage,
6402 m_hMemory,
6403 hAllocation->GetOffset());
6404 }
6405
InitStatInfo(VmaStatInfo & outInfo)6406 static void InitStatInfo(VmaStatInfo& outInfo)
6407 {
6408 memset(&outInfo, 0, sizeof(outInfo));
6409 outInfo.allocationSizeMin = UINT64_MAX;
6410 outInfo.unusedRangeSizeMin = UINT64_MAX;
6411 }
6412
6413 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
VmaAddStatInfo(VmaStatInfo & inoutInfo,const VmaStatInfo & srcInfo)6414 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
6415 {
6416 inoutInfo.blockCount += srcInfo.blockCount;
6417 inoutInfo.allocationCount += srcInfo.allocationCount;
6418 inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
6419 inoutInfo.usedBytes += srcInfo.usedBytes;
6420 inoutInfo.unusedBytes += srcInfo.unusedBytes;
6421 inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
6422 inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
6423 inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
6424 inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
6425 }
6426
VmaPostprocessCalcStatInfo(VmaStatInfo & inoutInfo)6427 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
6428 {
6429 inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
6430 VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
6431 inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
6432 VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
6433 }
6434
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo)6435 VmaPool_T::VmaPool_T(
6436 VmaAllocator hAllocator,
6437 const VmaPoolCreateInfo& createInfo) :
6438 m_BlockVector(
6439 hAllocator,
6440 createInfo.memoryTypeIndex,
6441 createInfo.blockSize,
6442 createInfo.minBlockCount,
6443 createInfo.maxBlockCount,
6444 (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
6445 createInfo.frameInUseCount,
6446 true) // isCustomPool
6447 {
6448 }
6449
~VmaPool_T()6450 VmaPool_T::~VmaPool_T()
6451 {
6452 }
6453
6454 #if VMA_STATS_STRING_ENABLED
6455
6456 #endif // #if VMA_STATS_STRING_ENABLED
6457
VmaBlockVector(VmaAllocator hAllocator,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,uint32_t frameInUseCount,bool isCustomPool)6458 VmaBlockVector::VmaBlockVector(
6459 VmaAllocator hAllocator,
6460 uint32_t memoryTypeIndex,
6461 VkDeviceSize preferredBlockSize,
6462 size_t minBlockCount,
6463 size_t maxBlockCount,
6464 VkDeviceSize bufferImageGranularity,
6465 uint32_t frameInUseCount,
6466 bool isCustomPool) :
6467 m_hAllocator(hAllocator),
6468 m_MemoryTypeIndex(memoryTypeIndex),
6469 m_PreferredBlockSize(preferredBlockSize),
6470 m_MinBlockCount(minBlockCount),
6471 m_MaxBlockCount(maxBlockCount),
6472 m_BufferImageGranularity(bufferImageGranularity),
6473 m_FrameInUseCount(frameInUseCount),
6474 m_IsCustomPool(isCustomPool),
6475 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
6476 m_HasEmptyBlock(false),
6477 m_pDefragmentator(VMA_NULL)
6478 {
6479 }
6480
~VmaBlockVector()6481 VmaBlockVector::~VmaBlockVector()
6482 {
6483 VMA_ASSERT(m_pDefragmentator == VMA_NULL);
6484
6485 for(size_t i = m_Blocks.size(); i--; )
6486 {
6487 m_Blocks[i]->Destroy(m_hAllocator);
6488 vma_delete(m_hAllocator, m_Blocks[i]);
6489 }
6490 }
6491
CreateMinBlocks()6492 VkResult VmaBlockVector::CreateMinBlocks()
6493 {
6494 for(size_t i = 0; i < m_MinBlockCount; ++i)
6495 {
6496 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
6497 if(res != VK_SUCCESS)
6498 {
6499 return res;
6500 }
6501 }
6502 return VK_SUCCESS;
6503 }
6504
GetPoolStats(VmaPoolStats * pStats)6505 void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
6506 {
6507 pStats->size = 0;
6508 pStats->unusedSize = 0;
6509 pStats->allocationCount = 0;
6510 pStats->unusedRangeCount = 0;
6511 pStats->unusedRangeSizeMax = 0;
6512
6513 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6514
6515 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6516 {
6517 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
6518 VMA_ASSERT(pBlock);
6519 VMA_HEAVY_ASSERT(pBlock->Validate());
6520 pBlock->m_Metadata.AddPoolStats(*pStats);
6521 }
6522 }
6523
6524 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
6525
Allocate(VmaPool hCurrentPool,uint32_t currentFrameIndex,const VkMemoryRequirements & vkMemReq,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)6526 VkResult VmaBlockVector::Allocate(
6527 VmaPool hCurrentPool,
6528 uint32_t currentFrameIndex,
6529 const VkMemoryRequirements& vkMemReq,
6530 const VmaAllocationCreateInfo& createInfo,
6531 VmaSuballocationType suballocType,
6532 VmaAllocation* pAllocation)
6533 {
6534 const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
6535 const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
6536
6537 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6538
6539 // 1. Search existing allocations. Try to allocate without making other allocations lost.
6540 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
6541 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
6542 {
6543 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
6544 VMA_ASSERT(pCurrBlock);
6545 VmaAllocationRequest currRequest = {};
6546 if(pCurrBlock->m_Metadata.CreateAllocationRequest(
6547 currentFrameIndex,
6548 m_FrameInUseCount,
6549 m_BufferImageGranularity,
6550 vkMemReq.size,
6551 vkMemReq.alignment,
6552 suballocType,
6553 false, // canMakeOtherLost
6554 &currRequest))
6555 {
6556 // Allocate from pCurrBlock.
6557 VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
6558
6559 if(mapped)
6560 {
6561 VkResult res = pCurrBlock->Map(m_hAllocator, 1, VMA_NULL);
6562 if(res != VK_SUCCESS)
6563 {
6564 return res;
6565 }
6566 }
6567
6568 // We no longer have an empty Allocation.
6569 if(pCurrBlock->m_Metadata.IsEmpty())
6570 {
6571 m_HasEmptyBlock = false;
6572 }
6573
6574 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6575 pCurrBlock->m_Metadata.Alloc(currRequest, suballocType, vkMemReq.size, *pAllocation);
6576 (*pAllocation)->InitBlockAllocation(
6577 hCurrentPool,
6578 pCurrBlock,
6579 currRequest.offset,
6580 vkMemReq.alignment,
6581 vkMemReq.size,
6582 suballocType,
6583 mapped,
6584 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6585 VMA_HEAVY_ASSERT(pCurrBlock->Validate());
6586 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
6587 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6588 return VK_SUCCESS;
6589 }
6590 }
6591
6592 const bool canCreateNewBlock =
6593 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
6594 (m_Blocks.size() < m_MaxBlockCount);
6595
6596 // 2. Try to create new block.
6597 if(canCreateNewBlock)
6598 {
6599 // Calculate optimal size for new block.
6600 VkDeviceSize newBlockSize = m_PreferredBlockSize;
6601 uint32_t newBlockSizeShift = 0;
6602 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
6603
6604 // Allocating blocks of other sizes is allowed only in default pools.
6605 // In custom pools block size is fixed.
6606 if(m_IsCustomPool == false)
6607 {
6608 // Allocate 1/8, 1/4, 1/2 as first blocks.
6609 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
6610 for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
6611 {
6612 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
6613 if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= vkMemReq.size * 2)
6614 {
6615 newBlockSize = smallerNewBlockSize;
6616 ++newBlockSizeShift;
6617 }
6618 else
6619 {
6620 break;
6621 }
6622 }
6623 }
6624
6625 size_t newBlockIndex = 0;
6626 VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
6627 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
6628 if(m_IsCustomPool == false)
6629 {
6630 while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
6631 {
6632 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
6633 if(smallerNewBlockSize >= vkMemReq.size)
6634 {
6635 newBlockSize = smallerNewBlockSize;
6636 ++newBlockSizeShift;
6637 res = CreateBlock(newBlockSize, &newBlockIndex);
6638 }
6639 else
6640 {
6641 break;
6642 }
6643 }
6644 }
6645
6646 if(res == VK_SUCCESS)
6647 {
6648 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
6649 VMA_ASSERT(pBlock->m_Metadata.GetSize() >= vkMemReq.size);
6650
6651 if(mapped)
6652 {
6653 res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
6654 if(res != VK_SUCCESS)
6655 {
6656 return res;
6657 }
6658 }
6659
6660 // Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled.
6661 VmaAllocationRequest allocRequest;
6662 pBlock->m_Metadata.CreateFirstAllocationRequest(&allocRequest);
6663 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6664 pBlock->m_Metadata.Alloc(allocRequest, suballocType, vkMemReq.size, *pAllocation);
6665 (*pAllocation)->InitBlockAllocation(
6666 hCurrentPool,
6667 pBlock,
6668 allocRequest.offset,
6669 vkMemReq.alignment,
6670 vkMemReq.size,
6671 suballocType,
6672 mapped,
6673 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6674 VMA_HEAVY_ASSERT(pBlock->Validate());
6675 VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize);
6676 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6677 return VK_SUCCESS;
6678 }
6679 }
6680
6681 const bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
6682
6683 // 3. Try to allocate from existing blocks with making other allocations lost.
6684 if(canMakeOtherLost)
6685 {
6686 uint32_t tryIndex = 0;
6687 for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
6688 {
6689 VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
6690 VmaAllocationRequest bestRequest = {};
6691 VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
6692
6693 // 1. Search existing allocations.
6694 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
6695 for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
6696 {
6697 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
6698 VMA_ASSERT(pCurrBlock);
6699 VmaAllocationRequest currRequest = {};
6700 if(pCurrBlock->m_Metadata.CreateAllocationRequest(
6701 currentFrameIndex,
6702 m_FrameInUseCount,
6703 m_BufferImageGranularity,
6704 vkMemReq.size,
6705 vkMemReq.alignment,
6706 suballocType,
6707 canMakeOtherLost,
6708 &currRequest))
6709 {
6710 const VkDeviceSize currRequestCost = currRequest.CalcCost();
6711 if(pBestRequestBlock == VMA_NULL ||
6712 currRequestCost < bestRequestCost)
6713 {
6714 pBestRequestBlock = pCurrBlock;
6715 bestRequest = currRequest;
6716 bestRequestCost = currRequestCost;
6717
6718 if(bestRequestCost == 0)
6719 {
6720 break;
6721 }
6722 }
6723 }
6724 }
6725
6726 if(pBestRequestBlock != VMA_NULL)
6727 {
6728 if(mapped)
6729 {
6730 VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
6731 if(res != VK_SUCCESS)
6732 {
6733 return res;
6734 }
6735 }
6736
6737 if(pBestRequestBlock->m_Metadata.MakeRequestedAllocationsLost(
6738 currentFrameIndex,
6739 m_FrameInUseCount,
6740 &bestRequest))
6741 {
6742 // We no longer have an empty Allocation.
6743 if(pBestRequestBlock->m_Metadata.IsEmpty())
6744 {
6745 m_HasEmptyBlock = false;
6746 }
6747 // Allocate from this pBlock.
6748 *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
6749 pBestRequestBlock->m_Metadata.Alloc(bestRequest, suballocType, vkMemReq.size, *pAllocation);
6750 (*pAllocation)->InitBlockAllocation(
6751 hCurrentPool,
6752 pBestRequestBlock,
6753 bestRequest.offset,
6754 vkMemReq.alignment,
6755 vkMemReq.size,
6756 suballocType,
6757 mapped,
6758 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
6759 VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
6760 VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
6761 (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
6762 return VK_SUCCESS;
6763 }
6764 // else: Some allocations must have been touched while we are here. Next try.
6765 }
6766 else
6767 {
6768 // Could not find place in any of the blocks - break outer loop.
6769 break;
6770 }
6771 }
6772 /* Maximum number of tries exceeded - a very unlike event when many other
6773 threads are simultaneously touching allocations making it impossible to make
6774 lost at the same time as we try to allocate. */
6775 if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
6776 {
6777 return VK_ERROR_TOO_MANY_OBJECTS;
6778 }
6779 }
6780
6781 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
6782 }
6783
Free(VmaAllocation hAllocation)6784 void VmaBlockVector::Free(
6785 VmaAllocation hAllocation)
6786 {
6787 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
6788
6789 // Scope for lock.
6790 {
6791 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6792
6793 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
6794
6795 if(hAllocation->IsPersistentMap())
6796 {
6797 pBlock->Unmap(m_hAllocator, 1);
6798 }
6799
6800 pBlock->m_Metadata.Free(hAllocation);
6801 VMA_HEAVY_ASSERT(pBlock->Validate());
6802
6803 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
6804
6805 // pBlock became empty after this deallocation.
6806 if(pBlock->m_Metadata.IsEmpty())
6807 {
6808 // Already has empty Allocation. We don't want to have two, so delete this one.
6809 if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
6810 {
6811 pBlockToDelete = pBlock;
6812 Remove(pBlock);
6813 }
6814 // We now have first empty Allocation.
6815 else
6816 {
6817 m_HasEmptyBlock = true;
6818 }
6819 }
6820 // pBlock didn't become empty, but we have another empty block - find and free that one.
6821 // (This is optional, heuristics.)
6822 else if(m_HasEmptyBlock)
6823 {
6824 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
6825 if(pLastBlock->m_Metadata.IsEmpty() && m_Blocks.size() > m_MinBlockCount)
6826 {
6827 pBlockToDelete = pLastBlock;
6828 m_Blocks.pop_back();
6829 m_HasEmptyBlock = false;
6830 }
6831 }
6832
6833 IncrementallySortBlocks();
6834 }
6835
6836 // Destruction of a free Allocation. Deferred until this point, outside of mutex
6837 // lock, for performance reason.
6838 if(pBlockToDelete != VMA_NULL)
6839 {
6840 VMA_DEBUG_LOG(" Deleted empty allocation");
6841 pBlockToDelete->Destroy(m_hAllocator);
6842 vma_delete(m_hAllocator, pBlockToDelete);
6843 }
6844 }
6845
CalcMaxBlockSize()6846 size_t VmaBlockVector::CalcMaxBlockSize() const
6847 {
6848 size_t result = 0;
6849 for(size_t i = m_Blocks.size(); i--; )
6850 {
6851 result = VMA_MAX((uint64_t)result, (uint64_t)m_Blocks[i]->m_Metadata.GetSize());
6852 if(result >= m_PreferredBlockSize)
6853 {
6854 break;
6855 }
6856 }
6857 return result;
6858 }
6859
Remove(VmaDeviceMemoryBlock * pBlock)6860 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
6861 {
6862 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
6863 {
6864 if(m_Blocks[blockIndex] == pBlock)
6865 {
6866 VmaVectorRemove(m_Blocks, blockIndex);
6867 return;
6868 }
6869 }
6870 VMA_ASSERT(0);
6871 }
6872
IncrementallySortBlocks()6873 void VmaBlockVector::IncrementallySortBlocks()
6874 {
6875 // Bubble sort only until first swap.
6876 for(size_t i = 1; i < m_Blocks.size(); ++i)
6877 {
6878 if(m_Blocks[i - 1]->m_Metadata.GetSumFreeSize() > m_Blocks[i]->m_Metadata.GetSumFreeSize())
6879 {
6880 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
6881 return;
6882 }
6883 }
6884 }
6885
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)6886 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
6887 {
6888 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
6889 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
6890 allocInfo.allocationSize = blockSize;
6891 VkDeviceMemory mem = VK_NULL_HANDLE;
6892 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
6893 if(res < 0)
6894 {
6895 return res;
6896 }
6897
6898 // New VkDeviceMemory successfully created.
6899
6900 // Create new Allocation for it.
6901 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
6902 pBlock->Init(
6903 m_MemoryTypeIndex,
6904 mem,
6905 allocInfo.allocationSize);
6906
6907 m_Blocks.push_back(pBlock);
6908 if(pNewBlockIndex != VMA_NULL)
6909 {
6910 *pNewBlockIndex = m_Blocks.size() - 1;
6911 }
6912
6913 return VK_SUCCESS;
6914 }
6915
6916 #if VMA_STATS_STRING_ENABLED
6917
PrintDetailedMap(class VmaJsonWriter & json)6918 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
6919 {
6920 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6921
6922 json.BeginObject();
6923
6924 if(m_IsCustomPool)
6925 {
6926 json.WriteString("MemoryTypeIndex");
6927 json.WriteNumber(m_MemoryTypeIndex);
6928
6929 json.WriteString("BlockSize");
6930 json.WriteNumber(m_PreferredBlockSize);
6931
6932 json.WriteString("BlockCount");
6933 json.BeginObject(true);
6934 if(m_MinBlockCount > 0)
6935 {
6936 json.WriteString("Min");
6937 json.WriteNumber((uint64_t)m_MinBlockCount);
6938 }
6939 if(m_MaxBlockCount < SIZE_MAX)
6940 {
6941 json.WriteString("Max");
6942 json.WriteNumber((uint64_t)m_MaxBlockCount);
6943 }
6944 json.WriteString("Cur");
6945 json.WriteNumber((uint64_t)m_Blocks.size());
6946 json.EndObject();
6947
6948 if(m_FrameInUseCount > 0)
6949 {
6950 json.WriteString("FrameInUseCount");
6951 json.WriteNumber(m_FrameInUseCount);
6952 }
6953 }
6954 else
6955 {
6956 json.WriteString("PreferredBlockSize");
6957 json.WriteNumber(m_PreferredBlockSize);
6958 }
6959
6960 json.WriteString("Blocks");
6961 json.BeginArray();
6962 for(size_t i = 0; i < m_Blocks.size(); ++i)
6963 {
6964 m_Blocks[i]->m_Metadata.PrintDetailedMap(json);
6965 }
6966 json.EndArray();
6967
6968 json.EndObject();
6969 }
6970
6971 #endif // #if VMA_STATS_STRING_ENABLED
6972
EnsureDefragmentator(VmaAllocator hAllocator,uint32_t currentFrameIndex)6973 VmaDefragmentator* VmaBlockVector::EnsureDefragmentator(
6974 VmaAllocator hAllocator,
6975 uint32_t currentFrameIndex)
6976 {
6977 if(m_pDefragmentator == VMA_NULL)
6978 {
6979 m_pDefragmentator = vma_new(m_hAllocator, VmaDefragmentator)(
6980 hAllocator,
6981 this,
6982 currentFrameIndex);
6983 }
6984
6985 return m_pDefragmentator;
6986 }
6987
Defragment(VmaDefragmentationStats * pDefragmentationStats,VkDeviceSize & maxBytesToMove,uint32_t & maxAllocationsToMove)6988 VkResult VmaBlockVector::Defragment(
6989 VmaDefragmentationStats* pDefragmentationStats,
6990 VkDeviceSize& maxBytesToMove,
6991 uint32_t& maxAllocationsToMove)
6992 {
6993 if(m_pDefragmentator == VMA_NULL)
6994 {
6995 return VK_SUCCESS;
6996 }
6997
6998 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
6999
7000 // Defragment.
7001 VkResult result = m_pDefragmentator->Defragment(maxBytesToMove, maxAllocationsToMove);
7002
7003 // Accumulate statistics.
7004 if(pDefragmentationStats != VMA_NULL)
7005 {
7006 const VkDeviceSize bytesMoved = m_pDefragmentator->GetBytesMoved();
7007 const uint32_t allocationsMoved = m_pDefragmentator->GetAllocationsMoved();
7008 pDefragmentationStats->bytesMoved += bytesMoved;
7009 pDefragmentationStats->allocationsMoved += allocationsMoved;
7010 VMA_ASSERT(bytesMoved <= maxBytesToMove);
7011 VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
7012 maxBytesToMove -= bytesMoved;
7013 maxAllocationsToMove -= allocationsMoved;
7014 }
7015
7016 // Free empty blocks.
7017 m_HasEmptyBlock = false;
7018 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
7019 {
7020 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
7021 if(pBlock->m_Metadata.IsEmpty())
7022 {
7023 if(m_Blocks.size() > m_MinBlockCount)
7024 {
7025 if(pDefragmentationStats != VMA_NULL)
7026 {
7027 ++pDefragmentationStats->deviceMemoryBlocksFreed;
7028 pDefragmentationStats->bytesFreed += pBlock->m_Metadata.GetSize();
7029 }
7030
7031 VmaVectorRemove(m_Blocks, blockIndex);
7032 pBlock->Destroy(m_hAllocator);
7033 vma_delete(m_hAllocator, pBlock);
7034 }
7035 else
7036 {
7037 m_HasEmptyBlock = true;
7038 }
7039 }
7040 }
7041
7042 return result;
7043 }
7044
DestroyDefragmentator()7045 void VmaBlockVector::DestroyDefragmentator()
7046 {
7047 if(m_pDefragmentator != VMA_NULL)
7048 {
7049 vma_delete(m_hAllocator, m_pDefragmentator);
7050 m_pDefragmentator = VMA_NULL;
7051 }
7052 }
7053
MakePoolAllocationsLost(uint32_t currentFrameIndex,size_t * pLostAllocationCount)7054 void VmaBlockVector::MakePoolAllocationsLost(
7055 uint32_t currentFrameIndex,
7056 size_t* pLostAllocationCount)
7057 {
7058 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
7059 size_t lostAllocationCount = 0;
7060 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
7061 {
7062 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
7063 VMA_ASSERT(pBlock);
7064 lostAllocationCount += pBlock->m_Metadata.MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
7065 }
7066 if(pLostAllocationCount != VMA_NULL)
7067 {
7068 *pLostAllocationCount = lostAllocationCount;
7069 }
7070 }
7071
AddStats(VmaStats * pStats)7072 void VmaBlockVector::AddStats(VmaStats* pStats)
7073 {
7074 const uint32_t memTypeIndex = m_MemoryTypeIndex;
7075 const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
7076
7077 VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
7078
7079 for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
7080 {
7081 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
7082 VMA_ASSERT(pBlock);
7083 VMA_HEAVY_ASSERT(pBlock->Validate());
7084 VmaStatInfo allocationStatInfo;
7085 pBlock->m_Metadata.CalcAllocationStatInfo(allocationStatInfo);
7086 VmaAddStatInfo(pStats->total, allocationStatInfo);
7087 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
7088 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
7089 }
7090 }
7091
7092 ////////////////////////////////////////////////////////////////////////////////
7093 // VmaDefragmentator members definition
7094
VmaDefragmentator(VmaAllocator hAllocator,VmaBlockVector * pBlockVector,uint32_t currentFrameIndex)7095 VmaDefragmentator::VmaDefragmentator(
7096 VmaAllocator hAllocator,
7097 VmaBlockVector* pBlockVector,
7098 uint32_t currentFrameIndex) :
7099 m_hAllocator(hAllocator),
7100 m_pBlockVector(pBlockVector),
7101 m_CurrentFrameIndex(currentFrameIndex),
7102 m_BytesMoved(0),
7103 m_AllocationsMoved(0),
7104 m_Allocations(VmaStlAllocator<AllocationInfo>(hAllocator->GetAllocationCallbacks())),
7105 m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
7106 {
7107 }
7108
~VmaDefragmentator()7109 VmaDefragmentator::~VmaDefragmentator()
7110 {
7111 for(size_t i = m_Blocks.size(); i--; )
7112 {
7113 vma_delete(m_hAllocator, m_Blocks[i]);
7114 }
7115 }
7116
AddAllocation(VmaAllocation hAlloc,VkBool32 * pChanged)7117 void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
7118 {
7119 AllocationInfo allocInfo;
7120 allocInfo.m_hAllocation = hAlloc;
7121 allocInfo.m_pChanged = pChanged;
7122 m_Allocations.push_back(allocInfo);
7123 }
7124
EnsureMapping(VmaAllocator hAllocator,void ** ppMappedData)7125 VkResult VmaDefragmentator::BlockInfo::EnsureMapping(VmaAllocator hAllocator, void** ppMappedData)
7126 {
7127 // It has already been mapped for defragmentation.
7128 if(m_pMappedDataForDefragmentation)
7129 {
7130 *ppMappedData = m_pMappedDataForDefragmentation;
7131 return VK_SUCCESS;
7132 }
7133
7134 // It is originally mapped.
7135 if(m_pBlock->GetMappedData())
7136 {
7137 *ppMappedData = m_pBlock->GetMappedData();
7138 return VK_SUCCESS;
7139 }
7140
7141 // Map on first usage.
7142 VkResult res = m_pBlock->Map(hAllocator, 1, &m_pMappedDataForDefragmentation);
7143 *ppMappedData = m_pMappedDataForDefragmentation;
7144 return res;
7145 }
7146
Unmap(VmaAllocator hAllocator)7147 void VmaDefragmentator::BlockInfo::Unmap(VmaAllocator hAllocator)
7148 {
7149 if(m_pMappedDataForDefragmentation != VMA_NULL)
7150 {
7151 m_pBlock->Unmap(hAllocator, 1);
7152 }
7153 }
7154
DefragmentRound(VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)7155 VkResult VmaDefragmentator::DefragmentRound(
7156 VkDeviceSize maxBytesToMove,
7157 uint32_t maxAllocationsToMove)
7158 {
7159 if(m_Blocks.empty())
7160 {
7161 return VK_SUCCESS;
7162 }
7163
7164 size_t srcBlockIndex = m_Blocks.size() - 1;
7165 size_t srcAllocIndex = SIZE_MAX;
7166 for(;;)
7167 {
7168 // 1. Find next allocation to move.
7169 // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
7170 // 1.2. Then start from last to first m_Allocations - they are sorted from largest to smallest.
7171 while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
7172 {
7173 if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
7174 {
7175 // Finished: no more allocations to process.
7176 if(srcBlockIndex == 0)
7177 {
7178 return VK_SUCCESS;
7179 }
7180 else
7181 {
7182 --srcBlockIndex;
7183 srcAllocIndex = SIZE_MAX;
7184 }
7185 }
7186 else
7187 {
7188 srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
7189 }
7190 }
7191
7192 BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
7193 AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
7194
7195 const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
7196 const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
7197 const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
7198 const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
7199
7200 // 2. Try to find new place for this allocation in preceding or current block.
7201 for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
7202 {
7203 BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
7204 VmaAllocationRequest dstAllocRequest;
7205 if(pDstBlockInfo->m_pBlock->m_Metadata.CreateAllocationRequest(
7206 m_CurrentFrameIndex,
7207 m_pBlockVector->GetFrameInUseCount(),
7208 m_pBlockVector->GetBufferImageGranularity(),
7209 size,
7210 alignment,
7211 suballocType,
7212 false, // canMakeOtherLost
7213 &dstAllocRequest) &&
7214 MoveMakesSense(
7215 dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
7216 {
7217 VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
7218
7219 // Reached limit on number of allocations or bytes to move.
7220 if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
7221 (m_BytesMoved + size > maxBytesToMove))
7222 {
7223 return VK_INCOMPLETE;
7224 }
7225
7226 void* pDstMappedData = VMA_NULL;
7227 VkResult res = pDstBlockInfo->EnsureMapping(m_hAllocator, &pDstMappedData);
7228 if(res != VK_SUCCESS)
7229 {
7230 return res;
7231 }
7232
7233 void* pSrcMappedData = VMA_NULL;
7234 res = pSrcBlockInfo->EnsureMapping(m_hAllocator, &pSrcMappedData);
7235 if(res != VK_SUCCESS)
7236 {
7237 return res;
7238 }
7239
7240 // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
7241 memcpy(
7242 reinterpret_cast<char*>(pDstMappedData) + dstAllocRequest.offset,
7243 reinterpret_cast<char*>(pSrcMappedData) + srcOffset,
7244 static_cast<size_t>(size));
7245
7246 pDstBlockInfo->m_pBlock->m_Metadata.Alloc(dstAllocRequest, suballocType, size, allocInfo.m_hAllocation);
7247 pSrcBlockInfo->m_pBlock->m_Metadata.FreeAtOffset(srcOffset);
7248
7249 allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
7250
7251 if(allocInfo.m_pChanged != VMA_NULL)
7252 {
7253 *allocInfo.m_pChanged = VK_TRUE;
7254 }
7255
7256 ++m_AllocationsMoved;
7257 m_BytesMoved += size;
7258
7259 VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
7260
7261 break;
7262 }
7263 }
7264
7265 // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
7266
7267 if(srcAllocIndex > 0)
7268 {
7269 --srcAllocIndex;
7270 }
7271 else
7272 {
7273 if(srcBlockIndex > 0)
7274 {
7275 --srcBlockIndex;
7276 srcAllocIndex = SIZE_MAX;
7277 }
7278 else
7279 {
7280 return VK_SUCCESS;
7281 }
7282 }
7283 }
7284 }
7285
Defragment(VkDeviceSize maxBytesToMove,uint32_t maxAllocationsToMove)7286 VkResult VmaDefragmentator::Defragment(
7287 VkDeviceSize maxBytesToMove,
7288 uint32_t maxAllocationsToMove)
7289 {
7290 if(m_Allocations.empty())
7291 {
7292 return VK_SUCCESS;
7293 }
7294
7295 // Create block info for each block.
7296 const size_t blockCount = m_pBlockVector->m_Blocks.size();
7297 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
7298 {
7299 BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
7300 pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
7301 m_Blocks.push_back(pBlockInfo);
7302 }
7303
7304 // Sort them by m_pBlock pointer value.
7305 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
7306
7307 // Move allocation infos from m_Allocations to appropriate m_Blocks[memTypeIndex].m_Allocations.
7308 for(size_t blockIndex = 0, allocCount = m_Allocations.size(); blockIndex < allocCount; ++blockIndex)
7309 {
7310 AllocationInfo& allocInfo = m_Allocations[blockIndex];
7311 // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
7312 if(allocInfo.m_hAllocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
7313 {
7314 VmaDeviceMemoryBlock* pBlock = allocInfo.m_hAllocation->GetBlock();
7315 BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
7316 if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
7317 {
7318 (*it)->m_Allocations.push_back(allocInfo);
7319 }
7320 else
7321 {
7322 VMA_ASSERT(0);
7323 }
7324 }
7325 }
7326 m_Allocations.clear();
7327
7328 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
7329 {
7330 BlockInfo* pBlockInfo = m_Blocks[blockIndex];
7331 pBlockInfo->CalcHasNonMovableAllocations();
7332 pBlockInfo->SortAllocationsBySizeDescecnding();
7333 }
7334
7335 // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
7336 VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
7337
7338 // Execute defragmentation rounds (the main part).
7339 VkResult result = VK_SUCCESS;
7340 for(size_t round = 0; (round < 2) && (result == VK_SUCCESS); ++round)
7341 {
7342 result = DefragmentRound(maxBytesToMove, maxAllocationsToMove);
7343 }
7344
7345 // Unmap blocks that were mapped for defragmentation.
7346 for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
7347 {
7348 m_Blocks[blockIndex]->Unmap(m_hAllocator);
7349 }
7350
7351 return result;
7352 }
7353
MoveMakesSense(size_t dstBlockIndex,VkDeviceSize dstOffset,size_t srcBlockIndex,VkDeviceSize srcOffset)7354 bool VmaDefragmentator::MoveMakesSense(
7355 size_t dstBlockIndex, VkDeviceSize dstOffset,
7356 size_t srcBlockIndex, VkDeviceSize srcOffset)
7357 {
7358 if(dstBlockIndex < srcBlockIndex)
7359 {
7360 return true;
7361 }
7362 if(dstBlockIndex > srcBlockIndex)
7363 {
7364 return false;
7365 }
7366 if(dstOffset < srcOffset)
7367 {
7368 return true;
7369 }
7370 return false;
7371 }
7372
7373 ////////////////////////////////////////////////////////////////////////////////
7374 // VmaAllocator_T
7375
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)7376 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
7377 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
7378 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
7379 m_hDevice(pCreateInfo->device),
7380 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
7381 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
7382 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
7383 m_PreferredLargeHeapBlockSize(0),
7384 m_PhysicalDevice(pCreateInfo->physicalDevice),
7385 m_CurrentFrameIndex(0),
7386 m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks()))
7387 {
7388 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
7389
7390 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
7391 memset(&m_MemProps, 0, sizeof(m_MemProps));
7392 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
7393
7394 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
7395 memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
7396
7397 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
7398 {
7399 m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
7400 }
7401
7402 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
7403 {
7404 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
7405 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
7406 }
7407
7408 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
7409
7410 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
7411 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
7412
7413 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
7414 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
7415
7416 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
7417 {
7418 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
7419 {
7420 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
7421 if(limit != VK_WHOLE_SIZE)
7422 {
7423 m_HeapSizeLimit[heapIndex] = limit;
7424 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
7425 {
7426 m_MemProps.memoryHeaps[heapIndex].size = limit;
7427 }
7428 }
7429 }
7430 }
7431
7432 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7433 {
7434 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
7435
7436 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
7437 this,
7438 memTypeIndex,
7439 preferredBlockSize,
7440 0,
7441 SIZE_MAX,
7442 GetBufferImageGranularity(),
7443 pCreateInfo->frameInUseCount,
7444 false); // isCustomPool
7445 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
7446 // becase minBlockCount is 0.
7447 m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
7448 }
7449 }
7450
~VmaAllocator_T()7451 VmaAllocator_T::~VmaAllocator_T()
7452 {
7453 VMA_ASSERT(m_Pools.empty());
7454
7455 for(size_t i = GetMemoryTypeCount(); i--; )
7456 {
7457 vma_delete(this, m_pDedicatedAllocations[i]);
7458 vma_delete(this, m_pBlockVectors[i]);
7459 }
7460 }
7461
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)7462 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
7463 {
7464 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7465 m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
7466 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
7467 m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
7468 m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
7469 m_VulkanFunctions.vkMapMemory = &vkMapMemory;
7470 m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
7471 m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
7472 m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
7473 m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
7474 m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
7475 m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
7476 m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
7477 m_VulkanFunctions.vkCreateImage = &vkCreateImage;
7478 m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
7479 if(m_UseKhrDedicatedAllocation)
7480 {
7481 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
7482 (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
7483 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
7484 (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
7485 }
7486 #endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
7487
7488 #define VMA_COPY_IF_NOT_NULL(funcName) \
7489 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
7490
7491 if(pVulkanFunctions != VMA_NULL)
7492 {
7493 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
7494 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
7495 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
7496 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
7497 VMA_COPY_IF_NOT_NULL(vkMapMemory);
7498 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
7499 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
7500 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
7501 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
7502 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
7503 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
7504 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
7505 VMA_COPY_IF_NOT_NULL(vkCreateImage);
7506 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
7507 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
7508 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
7509 }
7510
7511 #undef VMA_COPY_IF_NOT_NULL
7512
7513 // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
7514 // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
7515 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
7516 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
7517 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
7518 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
7519 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
7520 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
7521 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
7522 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
7523 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
7524 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
7525 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
7526 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
7527 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
7528 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
7529 if(m_UseKhrDedicatedAllocation)
7530 {
7531 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
7532 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
7533 }
7534 }
7535
CalcPreferredBlockSize(uint32_t memTypeIndex)7536 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
7537 {
7538 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
7539 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
7540 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
7541 return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
7542 }
7543
AllocateMemoryOfType(const VkMemoryRequirements & vkMemReq,bool dedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,VmaAllocation * pAllocation)7544 VkResult VmaAllocator_T::AllocateMemoryOfType(
7545 const VkMemoryRequirements& vkMemReq,
7546 bool dedicatedAllocation,
7547 VkBuffer dedicatedBuffer,
7548 VkImage dedicatedImage,
7549 const VmaAllocationCreateInfo& createInfo,
7550 uint32_t memTypeIndex,
7551 VmaSuballocationType suballocType,
7552 VmaAllocation* pAllocation)
7553 {
7554 VMA_ASSERT(pAllocation != VMA_NULL);
7555 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size);
7556
7557 VmaAllocationCreateInfo finalCreateInfo = createInfo;
7558
7559 // If memory type is not HOST_VISIBLE, disable MAPPED.
7560 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
7561 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
7562 {
7563 finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
7564 }
7565
7566 VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
7567 VMA_ASSERT(blockVector);
7568
7569 const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
7570 bool preferDedicatedMemory =
7571 VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
7572 dedicatedAllocation ||
7573 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
7574 vkMemReq.size > preferredBlockSize / 2;
7575
7576 if(preferDedicatedMemory &&
7577 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
7578 finalCreateInfo.pool == VK_NULL_HANDLE)
7579 {
7580 finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
7581 }
7582
7583 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
7584 {
7585 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7586 {
7587 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7588 }
7589 else
7590 {
7591 return AllocateDedicatedMemory(
7592 vkMemReq.size,
7593 suballocType,
7594 memTypeIndex,
7595 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
7596 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
7597 finalCreateInfo.pUserData,
7598 dedicatedBuffer,
7599 dedicatedImage,
7600 pAllocation);
7601 }
7602 }
7603 else
7604 {
7605 VkResult res = blockVector->Allocate(
7606 VK_NULL_HANDLE, // hCurrentPool
7607 m_CurrentFrameIndex.load(),
7608 vkMemReq,
7609 finalCreateInfo,
7610 suballocType,
7611 pAllocation);
7612 if(res == VK_SUCCESS)
7613 {
7614 return res;
7615 }
7616
7617 // 5. Try dedicated memory.
7618 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7619 {
7620 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7621 }
7622 else
7623 {
7624 res = AllocateDedicatedMemory(
7625 vkMemReq.size,
7626 suballocType,
7627 memTypeIndex,
7628 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
7629 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
7630 finalCreateInfo.pUserData,
7631 dedicatedBuffer,
7632 dedicatedImage,
7633 pAllocation);
7634 if(res == VK_SUCCESS)
7635 {
7636 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
7637 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
7638 return VK_SUCCESS;
7639 }
7640 else
7641 {
7642 // Everything failed: Return error code.
7643 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
7644 return res;
7645 }
7646 }
7647 }
7648 }
7649
AllocateDedicatedMemory(VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,bool map,bool isUserDataString,void * pUserData,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VmaAllocation * pAllocation)7650 VkResult VmaAllocator_T::AllocateDedicatedMemory(
7651 VkDeviceSize size,
7652 VmaSuballocationType suballocType,
7653 uint32_t memTypeIndex,
7654 bool map,
7655 bool isUserDataString,
7656 void* pUserData,
7657 VkBuffer dedicatedBuffer,
7658 VkImage dedicatedImage,
7659 VmaAllocation* pAllocation)
7660 {
7661 VMA_ASSERT(pAllocation);
7662
7663 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
7664 allocInfo.memoryTypeIndex = memTypeIndex;
7665 allocInfo.allocationSize = size;
7666
7667 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
7668 if(m_UseKhrDedicatedAllocation)
7669 {
7670 if(dedicatedBuffer != VK_NULL_HANDLE)
7671 {
7672 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
7673 dedicatedAllocInfo.buffer = dedicatedBuffer;
7674 allocInfo.pNext = &dedicatedAllocInfo;
7675 }
7676 else if(dedicatedImage != VK_NULL_HANDLE)
7677 {
7678 dedicatedAllocInfo.image = dedicatedImage;
7679 allocInfo.pNext = &dedicatedAllocInfo;
7680 }
7681 }
7682
7683 // Allocate VkDeviceMemory.
7684 VkDeviceMemory hMemory = VK_NULL_HANDLE;
7685 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
7686 if(res < 0)
7687 {
7688 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
7689 return res;
7690 }
7691
7692 void* pMappedData = VMA_NULL;
7693 if(map)
7694 {
7695 res = (*m_VulkanFunctions.vkMapMemory)(
7696 m_hDevice,
7697 hMemory,
7698 0,
7699 VK_WHOLE_SIZE,
7700 0,
7701 &pMappedData);
7702 if(res < 0)
7703 {
7704 VMA_DEBUG_LOG(" vkMapMemory FAILED");
7705 FreeVulkanMemory(memTypeIndex, size, hMemory);
7706 return res;
7707 }
7708 }
7709
7710 *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
7711 (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
7712 (*pAllocation)->SetUserData(this, pUserData);
7713
7714 // Register it in m_pDedicatedAllocations.
7715 {
7716 VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7717 AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
7718 VMA_ASSERT(pDedicatedAllocations);
7719 VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, *pAllocation);
7720 }
7721
7722 VMA_DEBUG_LOG(" Allocated DedicatedMemory MemoryTypeIndex=#%u", memTypeIndex);
7723
7724 return VK_SUCCESS;
7725 }
7726
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)7727 void VmaAllocator_T::GetBufferMemoryRequirements(
7728 VkBuffer hBuffer,
7729 VkMemoryRequirements& memReq,
7730 bool& requiresDedicatedAllocation,
7731 bool& prefersDedicatedAllocation) const
7732 {
7733 if(m_UseKhrDedicatedAllocation)
7734 {
7735 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
7736 memReqInfo.buffer = hBuffer;
7737
7738 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
7739
7740 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
7741 memReq2.pNext = &memDedicatedReq;
7742
7743 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
7744
7745 memReq = memReq2.memoryRequirements;
7746 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
7747 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
7748 }
7749 else
7750 {
7751 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
7752 requiresDedicatedAllocation = false;
7753 prefersDedicatedAllocation = false;
7754 }
7755 }
7756
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)7757 void VmaAllocator_T::GetImageMemoryRequirements(
7758 VkImage hImage,
7759 VkMemoryRequirements& memReq,
7760 bool& requiresDedicatedAllocation,
7761 bool& prefersDedicatedAllocation) const
7762 {
7763 if(m_UseKhrDedicatedAllocation)
7764 {
7765 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
7766 memReqInfo.image = hImage;
7767
7768 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
7769
7770 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
7771 memReq2.pNext = &memDedicatedReq;
7772
7773 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
7774
7775 memReq = memReq2.memoryRequirements;
7776 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
7777 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
7778 }
7779 else
7780 {
7781 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
7782 requiresDedicatedAllocation = false;
7783 prefersDedicatedAllocation = false;
7784 }
7785 }
7786
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)7787 VkResult VmaAllocator_T::AllocateMemory(
7788 const VkMemoryRequirements& vkMemReq,
7789 bool requiresDedicatedAllocation,
7790 bool prefersDedicatedAllocation,
7791 VkBuffer dedicatedBuffer,
7792 VkImage dedicatedImage,
7793 const VmaAllocationCreateInfo& createInfo,
7794 VmaSuballocationType suballocType,
7795 VmaAllocation* pAllocation)
7796 {
7797 if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
7798 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7799 {
7800 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
7801 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7802 }
7803 if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
7804 (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
7805 {
7806 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
7807 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7808 }
7809 if(requiresDedicatedAllocation)
7810 {
7811 if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
7812 {
7813 VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
7814 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7815 }
7816 if(createInfo.pool != VK_NULL_HANDLE)
7817 {
7818 VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
7819 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7820 }
7821 }
7822 if((createInfo.pool != VK_NULL_HANDLE) &&
7823 ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
7824 {
7825 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
7826 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7827 }
7828
7829 if(createInfo.pool != VK_NULL_HANDLE)
7830 {
7831 return createInfo.pool->m_BlockVector.Allocate(
7832 createInfo.pool,
7833 m_CurrentFrameIndex.load(),
7834 vkMemReq,
7835 createInfo,
7836 suballocType,
7837 pAllocation);
7838 }
7839 else
7840 {
7841 // Bit mask of memory Vulkan types acceptable for this allocation.
7842 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
7843 uint32_t memTypeIndex = UINT32_MAX;
7844 VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
7845 if(res == VK_SUCCESS)
7846 {
7847 res = AllocateMemoryOfType(
7848 vkMemReq,
7849 requiresDedicatedAllocation || prefersDedicatedAllocation,
7850 dedicatedBuffer,
7851 dedicatedImage,
7852 createInfo,
7853 memTypeIndex,
7854 suballocType,
7855 pAllocation);
7856 // Succeeded on first try.
7857 if(res == VK_SUCCESS)
7858 {
7859 return res;
7860 }
7861 // Allocation from this memory type failed. Try other compatible memory types.
7862 else
7863 {
7864 for(;;)
7865 {
7866 // Remove old memTypeIndex from list of possibilities.
7867 memoryTypeBits &= ~(1u << memTypeIndex);
7868 // Find alternative memTypeIndex.
7869 res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
7870 if(res == VK_SUCCESS)
7871 {
7872 res = AllocateMemoryOfType(
7873 vkMemReq,
7874 requiresDedicatedAllocation || prefersDedicatedAllocation,
7875 dedicatedBuffer,
7876 dedicatedImage,
7877 createInfo,
7878 memTypeIndex,
7879 suballocType,
7880 pAllocation);
7881 // Allocation from this alternative memory type succeeded.
7882 if(res == VK_SUCCESS)
7883 {
7884 return res;
7885 }
7886 // else: Allocation from this memory type failed. Try next one - next loop iteration.
7887 }
7888 // No other matching memory type index could be found.
7889 else
7890 {
7891 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
7892 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
7893 }
7894 }
7895 }
7896 }
7897 // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
7898 else
7899 return res;
7900 }
7901 }
7902
FreeMemory(const VmaAllocation allocation)7903 void VmaAllocator_T::FreeMemory(const VmaAllocation allocation)
7904 {
7905 VMA_ASSERT(allocation);
7906
7907 if(allocation->CanBecomeLost() == false ||
7908 allocation->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
7909 {
7910 switch(allocation->GetType())
7911 {
7912 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
7913 {
7914 VmaBlockVector* pBlockVector = VMA_NULL;
7915 VmaPool hPool = allocation->GetPool();
7916 if(hPool != VK_NULL_HANDLE)
7917 {
7918 pBlockVector = &hPool->m_BlockVector;
7919 }
7920 else
7921 {
7922 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
7923 pBlockVector = m_pBlockVectors[memTypeIndex];
7924 }
7925 pBlockVector->Free(allocation);
7926 }
7927 break;
7928 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
7929 FreeDedicatedMemory(allocation);
7930 break;
7931 default:
7932 VMA_ASSERT(0);
7933 }
7934 }
7935
7936 allocation->SetUserData(this, VMA_NULL);
7937 vma_delete(this, allocation);
7938 }
7939
CalculateStats(VmaStats * pStats)7940 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
7941 {
7942 // Initialize.
7943 InitStatInfo(pStats->total);
7944 for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
7945 InitStatInfo(pStats->memoryType[i]);
7946 for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
7947 InitStatInfo(pStats->memoryHeap[i]);
7948
7949 // Process default pools.
7950 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7951 {
7952 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
7953 VMA_ASSERT(pBlockVector);
7954 pBlockVector->AddStats(pStats);
7955 }
7956
7957 // Process custom pools.
7958 {
7959 VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
7960 for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
7961 {
7962 m_Pools[poolIndex]->GetBlockVector().AddStats(pStats);
7963 }
7964 }
7965
7966 // Process dedicated allocations.
7967 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
7968 {
7969 const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
7970 VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
7971 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
7972 VMA_ASSERT(pDedicatedAllocVector);
7973 for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
7974 {
7975 VmaStatInfo allocationStatInfo;
7976 (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
7977 VmaAddStatInfo(pStats->total, allocationStatInfo);
7978 VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
7979 VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
7980 }
7981 }
7982
7983 // Postprocess.
7984 VmaPostprocessCalcStatInfo(pStats->total);
7985 for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
7986 VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
7987 for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
7988 VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
7989 }
7990
7991 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
7992
Defragment(VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)7993 VkResult VmaAllocator_T::Defragment(
7994 VmaAllocation* pAllocations,
7995 size_t allocationCount,
7996 VkBool32* pAllocationsChanged,
7997 const VmaDefragmentationInfo* pDefragmentationInfo,
7998 VmaDefragmentationStats* pDefragmentationStats)
7999 {
8000 if(pAllocationsChanged != VMA_NULL)
8001 {
8002 memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged));
8003 }
8004 if(pDefragmentationStats != VMA_NULL)
8005 {
8006 memset(pDefragmentationStats, 0, sizeof(*pDefragmentationStats));
8007 }
8008
8009 const uint32_t currentFrameIndex = m_CurrentFrameIndex.load();
8010
8011 VmaMutexLock poolsLock(m_PoolsMutex, m_UseMutex);
8012
8013 const size_t poolCount = m_Pools.size();
8014
8015 // Dispatch pAllocations among defragmentators. Create them in BlockVectors when necessary.
8016 for(size_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
8017 {
8018 VmaAllocation hAlloc = pAllocations[allocIndex];
8019 VMA_ASSERT(hAlloc);
8020 const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
8021 // DedicatedAlloc cannot be defragmented.
8022 if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
8023 // Only HOST_VISIBLE memory types can be defragmented.
8024 ((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) &&
8025 // Lost allocation cannot be defragmented.
8026 (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
8027 {
8028 VmaBlockVector* pAllocBlockVector = VMA_NULL;
8029
8030 const VmaPool hAllocPool = hAlloc->GetPool();
8031 // This allocation belongs to custom pool.
8032 if(hAllocPool != VK_NULL_HANDLE)
8033 {
8034 pAllocBlockVector = &hAllocPool->GetBlockVector();
8035 }
8036 // This allocation belongs to general pool.
8037 else
8038 {
8039 pAllocBlockVector = m_pBlockVectors[memTypeIndex];
8040 }
8041
8042 VmaDefragmentator* const pDefragmentator = pAllocBlockVector->EnsureDefragmentator(this, currentFrameIndex);
8043
8044 VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
8045 &pAllocationsChanged[allocIndex] : VMA_NULL;
8046 pDefragmentator->AddAllocation(hAlloc, pChanged);
8047 }
8048 }
8049
8050 VkResult result = VK_SUCCESS;
8051
8052 // ======== Main processing.
8053
8054 VkDeviceSize maxBytesToMove = SIZE_MAX;
8055 uint32_t maxAllocationsToMove = UINT32_MAX;
8056 if(pDefragmentationInfo != VMA_NULL)
8057 {
8058 maxBytesToMove = pDefragmentationInfo->maxBytesToMove;
8059 maxAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
8060 }
8061
8062 // Process standard memory.
8063 for(uint32_t memTypeIndex = 0;
8064 (memTypeIndex < GetMemoryTypeCount()) && (result == VK_SUCCESS);
8065 ++memTypeIndex)
8066 {
8067 // Only HOST_VISIBLE memory types can be defragmented.
8068 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8069 {
8070 result = m_pBlockVectors[memTypeIndex]->Defragment(
8071 pDefragmentationStats,
8072 maxBytesToMove,
8073 maxAllocationsToMove);
8074 }
8075 }
8076
8077 // Process custom pools.
8078 for(size_t poolIndex = 0; (poolIndex < poolCount) && (result == VK_SUCCESS); ++poolIndex)
8079 {
8080 result = m_Pools[poolIndex]->GetBlockVector().Defragment(
8081 pDefragmentationStats,
8082 maxBytesToMove,
8083 maxAllocationsToMove);
8084 }
8085
8086 // ======== Destroy defragmentators.
8087
8088 // Process custom pools.
8089 for(size_t poolIndex = poolCount; poolIndex--; )
8090 {
8091 m_Pools[poolIndex]->GetBlockVector().DestroyDefragmentator();
8092 }
8093
8094 // Process standard memory.
8095 for(uint32_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
8096 {
8097 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8098 {
8099 m_pBlockVectors[memTypeIndex]->DestroyDefragmentator();
8100 }
8101 }
8102
8103 return result;
8104 }
8105
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)8106 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
8107 {
8108 if(hAllocation->CanBecomeLost())
8109 {
8110 /*
8111 Warning: This is a carefully designed algorithm.
8112 Do not modify unless you really know what you're doing :)
8113 */
8114 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
8115 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
8116 for(;;)
8117 {
8118 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8119 {
8120 pAllocationInfo->memoryType = UINT32_MAX;
8121 pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
8122 pAllocationInfo->offset = 0;
8123 pAllocationInfo->size = hAllocation->GetSize();
8124 pAllocationInfo->pMappedData = VMA_NULL;
8125 pAllocationInfo->pUserData = hAllocation->GetUserData();
8126 return;
8127 }
8128 else if(localLastUseFrameIndex == localCurrFrameIndex)
8129 {
8130 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
8131 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
8132 pAllocationInfo->offset = hAllocation->GetOffset();
8133 pAllocationInfo->size = hAllocation->GetSize();
8134 pAllocationInfo->pMappedData = VMA_NULL;
8135 pAllocationInfo->pUserData = hAllocation->GetUserData();
8136 return;
8137 }
8138 else // Last use time earlier than current time.
8139 {
8140 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
8141 {
8142 localLastUseFrameIndex = localCurrFrameIndex;
8143 }
8144 }
8145 }
8146 }
8147 else
8148 {
8149 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
8150 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
8151 pAllocationInfo->offset = hAllocation->GetOffset();
8152 pAllocationInfo->size = hAllocation->GetSize();
8153 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
8154 pAllocationInfo->pUserData = hAllocation->GetUserData();
8155 }
8156 }
8157
TouchAllocation(VmaAllocation hAllocation)8158 bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
8159 {
8160 // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
8161 if(hAllocation->CanBecomeLost())
8162 {
8163 uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
8164 uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
8165 for(;;)
8166 {
8167 if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
8168 {
8169 return false;
8170 }
8171 else if(localLastUseFrameIndex == localCurrFrameIndex)
8172 {
8173 return true;
8174 }
8175 else // Last use time earlier than current time.
8176 {
8177 if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
8178 {
8179 localLastUseFrameIndex = localCurrFrameIndex;
8180 }
8181 }
8182 }
8183 }
8184 else
8185 {
8186 return true;
8187 }
8188 }
8189
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)8190 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
8191 {
8192 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u", pCreateInfo->memoryTypeIndex);
8193
8194 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
8195
8196 if(newCreateInfo.maxBlockCount == 0)
8197 {
8198 newCreateInfo.maxBlockCount = SIZE_MAX;
8199 }
8200 if(newCreateInfo.blockSize == 0)
8201 {
8202 newCreateInfo.blockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
8203 }
8204
8205 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo);
8206
8207 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
8208 if(res != VK_SUCCESS)
8209 {
8210 vma_delete(this, *pPool);
8211 *pPool = VMA_NULL;
8212 return res;
8213 }
8214
8215 // Add to m_Pools.
8216 {
8217 VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
8218 VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
8219 }
8220
8221 return VK_SUCCESS;
8222 }
8223
DestroyPool(VmaPool pool)8224 void VmaAllocator_T::DestroyPool(VmaPool pool)
8225 {
8226 // Remove from m_Pools.
8227 {
8228 VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
8229 bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
8230 VMA_ASSERT(success && "Pool not found in Allocator.");
8231 }
8232
8233 vma_delete(this, pool);
8234 }
8235
GetPoolStats(VmaPool pool,VmaPoolStats * pPoolStats)8236 void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
8237 {
8238 pool->m_BlockVector.GetPoolStats(pPoolStats);
8239 }
8240
SetCurrentFrameIndex(uint32_t frameIndex)8241 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
8242 {
8243 m_CurrentFrameIndex.store(frameIndex);
8244 }
8245
MakePoolAllocationsLost(VmaPool hPool,size_t * pLostAllocationCount)8246 void VmaAllocator_T::MakePoolAllocationsLost(
8247 VmaPool hPool,
8248 size_t* pLostAllocationCount)
8249 {
8250 hPool->m_BlockVector.MakePoolAllocationsLost(
8251 m_CurrentFrameIndex.load(),
8252 pLostAllocationCount);
8253 }
8254
CreateLostAllocation(VmaAllocation * pAllocation)8255 void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
8256 {
8257 *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
8258 (*pAllocation)->InitLost();
8259 }
8260
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)8261 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
8262 {
8263 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
8264
8265 VkResult res;
8266 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
8267 {
8268 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
8269 if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
8270 {
8271 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
8272 if(res == VK_SUCCESS)
8273 {
8274 m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
8275 }
8276 }
8277 else
8278 {
8279 res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
8280 }
8281 }
8282 else
8283 {
8284 res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
8285 }
8286
8287 if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
8288 {
8289 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
8290 }
8291
8292 return res;
8293 }
8294
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)8295 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
8296 {
8297 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
8298 {
8299 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
8300 }
8301
8302 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
8303
8304 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
8305 if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
8306 {
8307 VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
8308 m_HeapSizeLimit[heapIndex] += size;
8309 }
8310 }
8311
Map(VmaAllocation hAllocation,void ** ppData)8312 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
8313 {
8314 if(hAllocation->CanBecomeLost())
8315 {
8316 return VK_ERROR_MEMORY_MAP_FAILED;
8317 }
8318
8319 switch(hAllocation->GetType())
8320 {
8321 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8322 {
8323 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
8324 char *pBytes = VMA_NULL;
8325 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
8326 if(res == VK_SUCCESS)
8327 {
8328 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
8329 hAllocation->BlockAllocMap();
8330 }
8331 return res;
8332 }
8333 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8334 return hAllocation->DedicatedAllocMap(this, ppData);
8335 default:
8336 VMA_ASSERT(0);
8337 return VK_ERROR_MEMORY_MAP_FAILED;
8338 }
8339 }
8340
Unmap(VmaAllocation hAllocation)8341 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
8342 {
8343 switch(hAllocation->GetType())
8344 {
8345 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8346 {
8347 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
8348 hAllocation->BlockAllocUnmap();
8349 pBlock->Unmap(this, 1);
8350 }
8351 break;
8352 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8353 hAllocation->DedicatedAllocUnmap(this);
8354 break;
8355 default:
8356 VMA_ASSERT(0);
8357 }
8358 }
8359
BindBufferMemory(VmaAllocation hAllocation,VkBuffer hBuffer)8360 VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
8361 {
8362 VkResult res = VK_SUCCESS;
8363 switch(hAllocation->GetType())
8364 {
8365 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8366 res = GetVulkanFunctions().vkBindBufferMemory(
8367 m_hDevice,
8368 hBuffer,
8369 hAllocation->GetMemory(),
8370 0); //memoryOffset
8371 break;
8372 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8373 {
8374 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
8375 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
8376 res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
8377 break;
8378 }
8379 default:
8380 VMA_ASSERT(0);
8381 }
8382 return res;
8383 }
8384
BindImageMemory(VmaAllocation hAllocation,VkImage hImage)8385 VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
8386 {
8387 VkResult res = VK_SUCCESS;
8388 switch(hAllocation->GetType())
8389 {
8390 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
8391 res = GetVulkanFunctions().vkBindImageMemory(
8392 m_hDevice,
8393 hImage,
8394 hAllocation->GetMemory(),
8395 0); //memoryOffset
8396 break;
8397 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
8398 {
8399 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
8400 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
8401 res = pBlock->BindImageMemory(this, hAllocation, hImage);
8402 break;
8403 }
8404 default:
8405 VMA_ASSERT(0);
8406 }
8407 return res;
8408 }
8409
FreeDedicatedMemory(VmaAllocation allocation)8410 void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
8411 {
8412 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
8413
8414 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
8415 {
8416 VmaMutexLock lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
8417 AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
8418 VMA_ASSERT(pDedicatedAllocations);
8419 bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
8420 VMA_ASSERT(success);
8421 }
8422
8423 VkDeviceMemory hMemory = allocation->GetMemory();
8424
8425 if(allocation->GetMappedData() != VMA_NULL)
8426 {
8427 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
8428 }
8429
8430 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
8431
8432 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
8433 }
8434
8435 #if VMA_STATS_STRING_ENABLED
8436
PrintDetailedMap(VmaJsonWriter & json)8437 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
8438 {
8439 bool dedicatedAllocationsStarted = false;
8440 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
8441 {
8442 VmaMutexLock dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
8443 AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
8444 VMA_ASSERT(pDedicatedAllocVector);
8445 if(pDedicatedAllocVector->empty() == false)
8446 {
8447 if(dedicatedAllocationsStarted == false)
8448 {
8449 dedicatedAllocationsStarted = true;
8450 json.WriteString("DedicatedAllocations");
8451 json.BeginObject();
8452 }
8453
8454 json.BeginString("Type ");
8455 json.ContinueString(memTypeIndex);
8456 json.EndString();
8457
8458 json.BeginArray();
8459
8460 for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
8461 {
8462 const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
8463 json.BeginObject(true);
8464
8465 json.WriteString("Type");
8466 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[hAlloc->GetSuballocationType()]);
8467
8468 json.WriteString("Size");
8469 json.WriteNumber(hAlloc->GetSize());
8470
8471 const void* pUserData = hAlloc->GetUserData();
8472 if(pUserData != VMA_NULL)
8473 {
8474 json.WriteString("UserData");
8475 if(hAlloc->IsUserDataString())
8476 {
8477 json.WriteString((const char*)pUserData);
8478 }
8479 else
8480 {
8481 json.BeginString();
8482 json.ContinueString_Pointer(pUserData);
8483 json.EndString();
8484 }
8485 }
8486
8487 json.EndObject();
8488 }
8489
8490 json.EndArray();
8491 }
8492 }
8493 if(dedicatedAllocationsStarted)
8494 {
8495 json.EndObject();
8496 }
8497
8498 {
8499 bool allocationsStarted = false;
8500 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
8501 {
8502 if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
8503 {
8504 if(allocationsStarted == false)
8505 {
8506 allocationsStarted = true;
8507 json.WriteString("DefaultPools");
8508 json.BeginObject();
8509 }
8510
8511 json.BeginString("Type ");
8512 json.ContinueString(memTypeIndex);
8513 json.EndString();
8514
8515 m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
8516 }
8517 }
8518 if(allocationsStarted)
8519 {
8520 json.EndObject();
8521 }
8522 }
8523
8524 {
8525 VmaMutexLock lock(m_PoolsMutex, m_UseMutex);
8526 const size_t poolCount = m_Pools.size();
8527 if(poolCount > 0)
8528 {
8529 json.WriteString("Pools");
8530 json.BeginArray();
8531 for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
8532 {
8533 m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
8534 }
8535 json.EndArray();
8536 }
8537 }
8538 }
8539
8540 #endif // #if VMA_STATS_STRING_ENABLED
8541
AllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pAllocationCreateInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)8542 static VkResult AllocateMemoryForImage(
8543 VmaAllocator allocator,
8544 VkImage image,
8545 const VmaAllocationCreateInfo* pAllocationCreateInfo,
8546 VmaSuballocationType suballocType,
8547 VmaAllocation* pAllocation)
8548 {
8549 VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pAllocationCreateInfo && pAllocation);
8550
8551 VkMemoryRequirements vkMemReq = {};
8552 bool requiresDedicatedAllocation = false;
8553 bool prefersDedicatedAllocation = false;
8554 allocator->GetImageMemoryRequirements(image, vkMemReq,
8555 requiresDedicatedAllocation, prefersDedicatedAllocation);
8556
8557 return allocator->AllocateMemory(
8558 vkMemReq,
8559 requiresDedicatedAllocation,
8560 prefersDedicatedAllocation,
8561 VK_NULL_HANDLE, // dedicatedBuffer
8562 image, // dedicatedImage
8563 *pAllocationCreateInfo,
8564 suballocType,
8565 pAllocation);
8566 }
8567
8568 ////////////////////////////////////////////////////////////////////////////////
8569 // Public interface
8570
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)8571 VkResult vmaCreateAllocator(
8572 const VmaAllocatorCreateInfo* pCreateInfo,
8573 VmaAllocator* pAllocator)
8574 {
8575 VMA_ASSERT(pCreateInfo && pAllocator);
8576 VMA_DEBUG_LOG("vmaCreateAllocator");
8577 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
8578 return VK_SUCCESS;
8579 }
8580
vmaDestroyAllocator(VmaAllocator allocator)8581 void vmaDestroyAllocator(
8582 VmaAllocator allocator)
8583 {
8584 if(allocator != VK_NULL_HANDLE)
8585 {
8586 VMA_DEBUG_LOG("vmaDestroyAllocator");
8587 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
8588 vma_delete(&allocationCallbacks, allocator);
8589 }
8590 }
8591
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)8592 void vmaGetPhysicalDeviceProperties(
8593 VmaAllocator allocator,
8594 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
8595 {
8596 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
8597 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
8598 }
8599
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)8600 void vmaGetMemoryProperties(
8601 VmaAllocator allocator,
8602 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
8603 {
8604 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
8605 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
8606 }
8607
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)8608 void vmaGetMemoryTypeProperties(
8609 VmaAllocator allocator,
8610 uint32_t memoryTypeIndex,
8611 VkMemoryPropertyFlags* pFlags)
8612 {
8613 VMA_ASSERT(allocator && pFlags);
8614 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
8615 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
8616 }
8617
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)8618 void vmaSetCurrentFrameIndex(
8619 VmaAllocator allocator,
8620 uint32_t frameIndex)
8621 {
8622 VMA_ASSERT(allocator);
8623 VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
8624
8625 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8626
8627 allocator->SetCurrentFrameIndex(frameIndex);
8628 }
8629
vmaCalculateStats(VmaAllocator allocator,VmaStats * pStats)8630 void vmaCalculateStats(
8631 VmaAllocator allocator,
8632 VmaStats* pStats)
8633 {
8634 VMA_ASSERT(allocator && pStats);
8635 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8636 allocator->CalculateStats(pStats);
8637 }
8638
8639 #if VMA_STATS_STRING_ENABLED
8640
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)8641 void vmaBuildStatsString(
8642 VmaAllocator allocator,
8643 char** ppStatsString,
8644 VkBool32 detailedMap)
8645 {
8646 VMA_ASSERT(allocator && ppStatsString);
8647 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8648
8649 VmaStringBuilder sb(allocator);
8650 {
8651 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
8652 json.BeginObject();
8653
8654 VmaStats stats;
8655 allocator->CalculateStats(&stats);
8656
8657 json.WriteString("Total");
8658 VmaPrintStatInfo(json, stats.total);
8659
8660 for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
8661 {
8662 json.BeginString("Heap ");
8663 json.ContinueString(heapIndex);
8664 json.EndString();
8665 json.BeginObject();
8666
8667 json.WriteString("Size");
8668 json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
8669
8670 json.WriteString("Flags");
8671 json.BeginArray(true);
8672 if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
8673 {
8674 json.WriteString("DEVICE_LOCAL");
8675 }
8676 json.EndArray();
8677
8678 if(stats.memoryHeap[heapIndex].blockCount > 0)
8679 {
8680 json.WriteString("Stats");
8681 VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
8682 }
8683
8684 for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
8685 {
8686 if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
8687 {
8688 json.BeginString("Type ");
8689 json.ContinueString(typeIndex);
8690 json.EndString();
8691
8692 json.BeginObject();
8693
8694 json.WriteString("Flags");
8695 json.BeginArray(true);
8696 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
8697 if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
8698 {
8699 json.WriteString("DEVICE_LOCAL");
8700 }
8701 if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
8702 {
8703 json.WriteString("HOST_VISIBLE");
8704 }
8705 if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
8706 {
8707 json.WriteString("HOST_COHERENT");
8708 }
8709 if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
8710 {
8711 json.WriteString("HOST_CACHED");
8712 }
8713 if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
8714 {
8715 json.WriteString("LAZILY_ALLOCATED");
8716 }
8717 json.EndArray();
8718
8719 if(stats.memoryType[typeIndex].blockCount > 0)
8720 {
8721 json.WriteString("Stats");
8722 VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
8723 }
8724
8725 json.EndObject();
8726 }
8727 }
8728
8729 json.EndObject();
8730 }
8731 if(detailedMap == VK_TRUE)
8732 {
8733 allocator->PrintDetailedMap(json);
8734 }
8735
8736 json.EndObject();
8737 }
8738
8739 const size_t len = sb.GetLength();
8740 char* const pChars = vma_new_array(allocator, char, len + 1);
8741 if(len > 0)
8742 {
8743 memcpy(pChars, sb.GetData(), len);
8744 }
8745 pChars[len] = '\0';
8746 *ppStatsString = pChars;
8747 }
8748
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)8749 void vmaFreeStatsString(
8750 VmaAllocator allocator,
8751 char* pStatsString)
8752 {
8753 if(pStatsString != VMA_NULL)
8754 {
8755 VMA_ASSERT(allocator);
8756 size_t len = strlen(pStatsString);
8757 vma_delete_array(allocator, pStatsString, len + 1);
8758 }
8759 }
8760
8761 #endif // #if VMA_STATS_STRING_ENABLED
8762
8763 /*
8764 This function is not protected by any mutex because it just reads immutable data.
8765 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)8766 VkResult vmaFindMemoryTypeIndex(
8767 VmaAllocator allocator,
8768 uint32_t memoryTypeBits,
8769 const VmaAllocationCreateInfo* pAllocationCreateInfo,
8770 uint32_t* pMemoryTypeIndex)
8771 {
8772 VMA_ASSERT(allocator != VK_NULL_HANDLE);
8773 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8774 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8775
8776 if(pAllocationCreateInfo->memoryTypeBits != 0)
8777 {
8778 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
8779 }
8780
8781 uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
8782 uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
8783
8784 // Convert usage to requiredFlags and preferredFlags.
8785 switch(pAllocationCreateInfo->usage)
8786 {
8787 case VMA_MEMORY_USAGE_UNKNOWN:
8788 break;
8789 case VMA_MEMORY_USAGE_GPU_ONLY:
8790 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
8791 break;
8792 case VMA_MEMORY_USAGE_CPU_ONLY:
8793 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
8794 break;
8795 case VMA_MEMORY_USAGE_CPU_TO_GPU:
8796 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8797 preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
8798 break;
8799 case VMA_MEMORY_USAGE_GPU_TO_CPU:
8800 requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
8801 preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
8802 break;
8803 default:
8804 break;
8805 }
8806
8807 *pMemoryTypeIndex = UINT32_MAX;
8808 uint32_t minCost = UINT32_MAX;
8809 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
8810 memTypeIndex < allocator->GetMemoryTypeCount();
8811 ++memTypeIndex, memTypeBit <<= 1)
8812 {
8813 // This memory type is acceptable according to memoryTypeBits bitmask.
8814 if((memTypeBit & memoryTypeBits) != 0)
8815 {
8816 const VkMemoryPropertyFlags currFlags =
8817 allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
8818 // This memory type contains requiredFlags.
8819 if((requiredFlags & ~currFlags) == 0)
8820 {
8821 // Calculate cost as number of bits from preferredFlags not present in this memory type.
8822 uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
8823 // Remember memory type with lowest cost.
8824 if(currCost < minCost)
8825 {
8826 *pMemoryTypeIndex = memTypeIndex;
8827 if(currCost == 0)
8828 {
8829 return VK_SUCCESS;
8830 }
8831 minCost = currCost;
8832 }
8833 }
8834 }
8835 }
8836 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
8837 }
8838
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)8839 VkResult vmaFindMemoryTypeIndexForBufferInfo(
8840 VmaAllocator allocator,
8841 const VkBufferCreateInfo* pBufferCreateInfo,
8842 const VmaAllocationCreateInfo* pAllocationCreateInfo,
8843 uint32_t* pMemoryTypeIndex)
8844 {
8845 VMA_ASSERT(allocator != VK_NULL_HANDLE);
8846 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
8847 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8848 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8849
8850 const VkDevice hDev = allocator->m_hDevice;
8851 VkBuffer hBuffer = VK_NULL_HANDLE;
8852 VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
8853 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
8854 if(res == VK_SUCCESS)
8855 {
8856 VkMemoryRequirements memReq = {};
8857 allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
8858 hDev, hBuffer, &memReq);
8859
8860 res = vmaFindMemoryTypeIndex(
8861 allocator,
8862 memReq.memoryTypeBits,
8863 pAllocationCreateInfo,
8864 pMemoryTypeIndex);
8865
8866 allocator->GetVulkanFunctions().vkDestroyBuffer(
8867 hDev, hBuffer, allocator->GetAllocationCallbacks());
8868 }
8869 return res;
8870 }
8871
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)8872 VkResult vmaFindMemoryTypeIndexForImageInfo(
8873 VmaAllocator allocator,
8874 const VkImageCreateInfo* pImageCreateInfo,
8875 const VmaAllocationCreateInfo* pAllocationCreateInfo,
8876 uint32_t* pMemoryTypeIndex)
8877 {
8878 VMA_ASSERT(allocator != VK_NULL_HANDLE);
8879 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
8880 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
8881 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
8882
8883 const VkDevice hDev = allocator->m_hDevice;
8884 VkImage hImage = VK_NULL_HANDLE;
8885 VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
8886 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
8887 if(res == VK_SUCCESS)
8888 {
8889 VkMemoryRequirements memReq = {};
8890 allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
8891 hDev, hImage, &memReq);
8892
8893 res = vmaFindMemoryTypeIndex(
8894 allocator,
8895 memReq.memoryTypeBits,
8896 pAllocationCreateInfo,
8897 pMemoryTypeIndex);
8898
8899 allocator->GetVulkanFunctions().vkDestroyImage(
8900 hDev, hImage, allocator->GetAllocationCallbacks());
8901 }
8902 return res;
8903 }
8904
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)8905 VkResult vmaCreatePool(
8906 VmaAllocator allocator,
8907 const VmaPoolCreateInfo* pCreateInfo,
8908 VmaPool* pPool)
8909 {
8910 VMA_ASSERT(allocator && pCreateInfo && pPool);
8911
8912 VMA_DEBUG_LOG("vmaCreatePool");
8913
8914 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8915
8916 return allocator->CreatePool(pCreateInfo, pPool);
8917 }
8918
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)8919 void vmaDestroyPool(
8920 VmaAllocator allocator,
8921 VmaPool pool)
8922 {
8923 VMA_ASSERT(allocator);
8924
8925 if(pool == VK_NULL_HANDLE)
8926 {
8927 return;
8928 }
8929
8930 VMA_DEBUG_LOG("vmaDestroyPool");
8931
8932 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8933
8934 allocator->DestroyPool(pool);
8935 }
8936
vmaGetPoolStats(VmaAllocator allocator,VmaPool pool,VmaPoolStats * pPoolStats)8937 void vmaGetPoolStats(
8938 VmaAllocator allocator,
8939 VmaPool pool,
8940 VmaPoolStats* pPoolStats)
8941 {
8942 VMA_ASSERT(allocator && pool && pPoolStats);
8943
8944 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8945
8946 allocator->GetPoolStats(pool, pPoolStats);
8947 }
8948
vmaMakePoolAllocationsLost(VmaAllocator allocator,VmaPool pool,size_t * pLostAllocationCount)8949 void vmaMakePoolAllocationsLost(
8950 VmaAllocator allocator,
8951 VmaPool pool,
8952 size_t* pLostAllocationCount)
8953 {
8954 VMA_ASSERT(allocator && pool);
8955
8956 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8957
8958 allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
8959 }
8960
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)8961 VkResult vmaAllocateMemory(
8962 VmaAllocator allocator,
8963 const VkMemoryRequirements* pVkMemoryRequirements,
8964 const VmaAllocationCreateInfo* pCreateInfo,
8965 VmaAllocation* pAllocation,
8966 VmaAllocationInfo* pAllocationInfo)
8967 {
8968 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
8969
8970 VMA_DEBUG_LOG("vmaAllocateMemory");
8971
8972 VMA_DEBUG_GLOBAL_MUTEX_LOCK
8973
8974 VkResult result = allocator->AllocateMemory(
8975 *pVkMemoryRequirements,
8976 false, // requiresDedicatedAllocation
8977 false, // prefersDedicatedAllocation
8978 VK_NULL_HANDLE, // dedicatedBuffer
8979 VK_NULL_HANDLE, // dedicatedImage
8980 *pCreateInfo,
8981 VMA_SUBALLOCATION_TYPE_UNKNOWN,
8982 pAllocation);
8983
8984 if(pAllocationInfo && result == VK_SUCCESS)
8985 {
8986 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
8987 }
8988
8989 return result;
8990 }
8991
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)8992 VkResult vmaAllocateMemoryForBuffer(
8993 VmaAllocator allocator,
8994 VkBuffer buffer,
8995 const VmaAllocationCreateInfo* pCreateInfo,
8996 VmaAllocation* pAllocation,
8997 VmaAllocationInfo* pAllocationInfo)
8998 {
8999 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
9000
9001 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
9002
9003 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9004
9005 VkMemoryRequirements vkMemReq = {};
9006 bool requiresDedicatedAllocation = false;
9007 bool prefersDedicatedAllocation = false;
9008 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
9009 requiresDedicatedAllocation,
9010 prefersDedicatedAllocation);
9011
9012 VkResult result = allocator->AllocateMemory(
9013 vkMemReq,
9014 requiresDedicatedAllocation,
9015 prefersDedicatedAllocation,
9016 buffer, // dedicatedBuffer
9017 VK_NULL_HANDLE, // dedicatedImage
9018 *pCreateInfo,
9019 VMA_SUBALLOCATION_TYPE_BUFFER,
9020 pAllocation);
9021
9022 if(pAllocationInfo && result == VK_SUCCESS)
9023 {
9024 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9025 }
9026
9027 return result;
9028 }
9029
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)9030 VkResult vmaAllocateMemoryForImage(
9031 VmaAllocator allocator,
9032 VkImage image,
9033 const VmaAllocationCreateInfo* pCreateInfo,
9034 VmaAllocation* pAllocation,
9035 VmaAllocationInfo* pAllocationInfo)
9036 {
9037 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
9038
9039 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
9040
9041 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9042
9043 VkResult result = AllocateMemoryForImage(
9044 allocator,
9045 image,
9046 pCreateInfo,
9047 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
9048 pAllocation);
9049
9050 if(pAllocationInfo && result == VK_SUCCESS)
9051 {
9052 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9053 }
9054
9055 return result;
9056 }
9057
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)9058 void vmaFreeMemory(
9059 VmaAllocator allocator,
9060 VmaAllocation allocation)
9061 {
9062 VMA_ASSERT(allocator && allocation);
9063
9064 VMA_DEBUG_LOG("vmaFreeMemory");
9065
9066 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9067
9068 allocator->FreeMemory(allocation);
9069 }
9070
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)9071 void vmaGetAllocationInfo(
9072 VmaAllocator allocator,
9073 VmaAllocation allocation,
9074 VmaAllocationInfo* pAllocationInfo)
9075 {
9076 VMA_ASSERT(allocator && allocation && pAllocationInfo);
9077
9078 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9079
9080 allocator->GetAllocationInfo(allocation, pAllocationInfo);
9081 }
9082
vmaTouchAllocation(VmaAllocator allocator,VmaAllocation allocation)9083 VkBool32 vmaTouchAllocation(
9084 VmaAllocator allocator,
9085 VmaAllocation allocation)
9086 {
9087 VMA_ASSERT(allocator && allocation);
9088
9089 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9090
9091 return allocator->TouchAllocation(allocation);
9092 }
9093
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)9094 void vmaSetAllocationUserData(
9095 VmaAllocator allocator,
9096 VmaAllocation allocation,
9097 void* pUserData)
9098 {
9099 VMA_ASSERT(allocator && allocation);
9100
9101 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9102
9103 allocation->SetUserData(allocator, pUserData);
9104 }
9105
vmaCreateLostAllocation(VmaAllocator allocator,VmaAllocation * pAllocation)9106 void vmaCreateLostAllocation(
9107 VmaAllocator allocator,
9108 VmaAllocation* pAllocation)
9109 {
9110 VMA_ASSERT(allocator && pAllocation);
9111
9112 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
9113
9114 allocator->CreateLostAllocation(pAllocation);
9115 }
9116
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)9117 VkResult vmaMapMemory(
9118 VmaAllocator allocator,
9119 VmaAllocation allocation,
9120 void** ppData)
9121 {
9122 VMA_ASSERT(allocator && allocation && ppData);
9123
9124 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9125
9126 return allocator->Map(allocation, ppData);
9127 }
9128
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)9129 void vmaUnmapMemory(
9130 VmaAllocator allocator,
9131 VmaAllocation allocation)
9132 {
9133 VMA_ASSERT(allocator && allocation);
9134
9135 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9136
9137 allocator->Unmap(allocation);
9138 }
9139
vmaDefragment(VmaAllocator allocator,VmaAllocation * pAllocations,size_t allocationCount,VkBool32 * pAllocationsChanged,const VmaDefragmentationInfo * pDefragmentationInfo,VmaDefragmentationStats * pDefragmentationStats)9140 VkResult vmaDefragment(
9141 VmaAllocator allocator,
9142 VmaAllocation* pAllocations,
9143 size_t allocationCount,
9144 VkBool32* pAllocationsChanged,
9145 const VmaDefragmentationInfo *pDefragmentationInfo,
9146 VmaDefragmentationStats* pDefragmentationStats)
9147 {
9148 VMA_ASSERT(allocator && pAllocations);
9149
9150 VMA_DEBUG_LOG("vmaDefragment");
9151
9152 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9153
9154 return allocator->Defragment(pAllocations, allocationCount, pAllocationsChanged, pDefragmentationInfo, pDefragmentationStats);
9155 }
9156
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)9157 VkResult vmaBindBufferMemory(
9158 VmaAllocator allocator,
9159 VmaAllocation allocation,
9160 VkBuffer buffer)
9161 {
9162 VMA_ASSERT(allocator && allocation && buffer);
9163
9164 VMA_DEBUG_LOG("vmaBindBufferMemory");
9165
9166 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9167
9168 return allocator->BindBufferMemory(allocation, buffer);
9169 }
9170
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)9171 VkResult vmaBindImageMemory(
9172 VmaAllocator allocator,
9173 VmaAllocation allocation,
9174 VkImage image)
9175 {
9176 VMA_ASSERT(allocator && allocation && image);
9177
9178 VMA_DEBUG_LOG("vmaBindImageMemory");
9179
9180 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9181
9182 return allocator->BindImageMemory(allocation, image);
9183 }
9184
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)9185 VkResult vmaCreateBuffer(
9186 VmaAllocator allocator,
9187 const VkBufferCreateInfo* pBufferCreateInfo,
9188 const VmaAllocationCreateInfo* pAllocationCreateInfo,
9189 VkBuffer* pBuffer,
9190 VmaAllocation* pAllocation,
9191 VmaAllocationInfo* pAllocationInfo)
9192 {
9193 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
9194
9195 VMA_DEBUG_LOG("vmaCreateBuffer");
9196
9197 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9198
9199 *pBuffer = VK_NULL_HANDLE;
9200 *pAllocation = VK_NULL_HANDLE;
9201
9202 // 1. Create VkBuffer.
9203 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
9204 allocator->m_hDevice,
9205 pBufferCreateInfo,
9206 allocator->GetAllocationCallbacks(),
9207 pBuffer);
9208 if(res >= 0)
9209 {
9210 // 2. vkGetBufferMemoryRequirements.
9211 VkMemoryRequirements vkMemReq = {};
9212 bool requiresDedicatedAllocation = false;
9213 bool prefersDedicatedAllocation = false;
9214 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
9215 requiresDedicatedAllocation, prefersDedicatedAllocation);
9216
9217 // Make sure alignment requirements for specific buffer usages reported
9218 // in Physical Device Properties are included in alignment reported by memory requirements.
9219 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
9220 {
9221 VMA_ASSERT(vkMemReq.alignment %
9222 allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
9223 }
9224 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
9225 {
9226 VMA_ASSERT(vkMemReq.alignment %
9227 allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
9228 }
9229 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
9230 {
9231 VMA_ASSERT(vkMemReq.alignment %
9232 allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
9233 }
9234
9235 // 3. Allocate memory using allocator.
9236 res = allocator->AllocateMemory(
9237 vkMemReq,
9238 requiresDedicatedAllocation,
9239 prefersDedicatedAllocation,
9240 *pBuffer, // dedicatedBuffer
9241 VK_NULL_HANDLE, // dedicatedImage
9242 *pAllocationCreateInfo,
9243 VMA_SUBALLOCATION_TYPE_BUFFER,
9244 pAllocation);
9245 if(res >= 0)
9246 {
9247 // 3. Bind buffer with memory.
9248 res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
9249 if(res >= 0)
9250 {
9251 // All steps succeeded.
9252 if(pAllocationInfo != VMA_NULL)
9253 {
9254 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9255 }
9256 return VK_SUCCESS;
9257 }
9258 allocator->FreeMemory(*pAllocation);
9259 *pAllocation = VK_NULL_HANDLE;
9260 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
9261 *pBuffer = VK_NULL_HANDLE;
9262 return res;
9263 }
9264 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
9265 *pBuffer = VK_NULL_HANDLE;
9266 return res;
9267 }
9268 return res;
9269 }
9270
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)9271 void vmaDestroyBuffer(
9272 VmaAllocator allocator,
9273 VkBuffer buffer,
9274 VmaAllocation allocation)
9275 {
9276 if(buffer != VK_NULL_HANDLE)
9277 {
9278 VMA_ASSERT(allocator);
9279
9280 VMA_DEBUG_LOG("vmaDestroyBuffer");
9281
9282 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9283
9284 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
9285
9286 allocator->FreeMemory(allocation);
9287 }
9288 }
9289
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)9290 VkResult vmaCreateImage(
9291 VmaAllocator allocator,
9292 const VkImageCreateInfo* pImageCreateInfo,
9293 const VmaAllocationCreateInfo* pAllocationCreateInfo,
9294 VkImage* pImage,
9295 VmaAllocation* pAllocation,
9296 VmaAllocationInfo* pAllocationInfo)
9297 {
9298 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
9299
9300 VMA_DEBUG_LOG("vmaCreateImage");
9301
9302 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9303
9304 *pImage = VK_NULL_HANDLE;
9305 *pAllocation = VK_NULL_HANDLE;
9306
9307 // 1. Create VkImage.
9308 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
9309 allocator->m_hDevice,
9310 pImageCreateInfo,
9311 allocator->GetAllocationCallbacks(),
9312 pImage);
9313 if(res >= 0)
9314 {
9315 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
9316 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
9317 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
9318
9319 // 2. Allocate memory using allocator.
9320 res = AllocateMemoryForImage(allocator, *pImage, pAllocationCreateInfo, suballocType, pAllocation);
9321 if(res >= 0)
9322 {
9323 // 3. Bind image with memory.
9324 res = allocator->BindImageMemory(*pAllocation, *pImage);
9325 if(res >= 0)
9326 {
9327 // All steps succeeded.
9328 if(pAllocationInfo != VMA_NULL)
9329 {
9330 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
9331 }
9332 return VK_SUCCESS;
9333 }
9334 allocator->FreeMemory(*pAllocation);
9335 *pAllocation = VK_NULL_HANDLE;
9336 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
9337 *pImage = VK_NULL_HANDLE;
9338 return res;
9339 }
9340 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
9341 *pImage = VK_NULL_HANDLE;
9342 return res;
9343 }
9344 return res;
9345 }
9346
vmaDestroyImage(VmaAllocator allocator,VkImage image,VmaAllocation allocation)9347 void vmaDestroyImage(
9348 VmaAllocator allocator,
9349 VkImage image,
9350 VmaAllocation allocation)
9351 {
9352 if(image != VK_NULL_HANDLE)
9353 {
9354 VMA_ASSERT(allocator);
9355
9356 VMA_DEBUG_LOG("vmaDestroyImage");
9357
9358 VMA_DEBUG_GLOBAL_MUTEX_LOCK
9359
9360 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
9361
9362 allocator->FreeMemory(allocation);
9363 }
9364 }
9365
9366 #endif // #ifdef VMA_IMPLEMENTATION
9367