1 //
2 // Copyright (c) 2017-2022 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 /** \mainpage Vulkan Memory Allocator
27
28 <b>Version 3.0.1 (2022-05-26)</b>
29
30 Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. \n
31 License: MIT
32
33 <b>API documentation divided into groups:</b> [Modules](modules.html)
34
35 \section main_table_of_contents Table of contents
36
37 - <b>User guide</b>
38 - \subpage quick_start
39 - [Project setup](@ref quick_start_project_setup)
40 - [Initialization](@ref quick_start_initialization)
41 - [Resource allocation](@ref quick_start_resource_allocation)
42 - \subpage choosing_memory_type
43 - [Usage](@ref choosing_memory_type_usage)
44 - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
45 - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
46 - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
47 - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
48 - \subpage memory_mapping
49 - [Mapping functions](@ref memory_mapping_mapping_functions)
50 - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
51 - [Cache flush and invalidate](@ref memory_mapping_cache_control)
52 - \subpage staying_within_budget
53 - [Querying for budget](@ref staying_within_budget_querying_for_budget)
54 - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)
55 - \subpage resource_aliasing
56 - \subpage custom_memory_pools
57 - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
58 - [Linear allocation algorithm](@ref linear_algorithm)
59 - [Free-at-once](@ref linear_algorithm_free_at_once)
60 - [Stack](@ref linear_algorithm_stack)
61 - [Double stack](@ref linear_algorithm_double_stack)
62 - [Ring buffer](@ref linear_algorithm_ring_buffer)
63 - \subpage defragmentation
64 - \subpage statistics
65 - [Numeric statistics](@ref statistics_numeric_statistics)
66 - [JSON dump](@ref statistics_json_dump)
67 - \subpage allocation_annotation
68 - [Allocation user data](@ref allocation_user_data)
69 - [Allocation names](@ref allocation_names)
70 - \subpage virtual_allocator
71 - \subpage debugging_memory_usage
72 - [Memory initialization](@ref debugging_memory_usage_initialization)
73 - [Margins](@ref debugging_memory_usage_margins)
74 - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
75 - \subpage opengl_interop
76 - \subpage usage_patterns
77 - [GPU-only resource](@ref usage_patterns_gpu_only)
78 - [Staging copy for upload](@ref usage_patterns_staging_copy_upload)
79 - [Readback](@ref usage_patterns_readback)
80 - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading)
81 - [Other use cases](@ref usage_patterns_other_use_cases)
82 - \subpage configuration
83 - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
84 - [Custom host memory allocator](@ref custom_memory_allocator)
85 - [Device memory allocation callbacks](@ref allocation_callbacks)
86 - [Device heap memory limit](@ref heap_memory_limit)
87 - <b>Extension support</b>
88 - \subpage vk_khr_dedicated_allocation
89 - \subpage enabling_buffer_device_address
90 - \subpage vk_ext_memory_priority
91 - \subpage vk_amd_device_coherent_memory
92 - \subpage general_considerations
93 - [Thread safety](@ref general_considerations_thread_safety)
94 - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)
95 - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
96 - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
97 - [Features not supported](@ref general_considerations_features_not_supported)
98
99 \section main_see_also See also
100
101 - [**Product page on GPUOpen**](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
102 - [**Source repository on GitHub**](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
103
104 \defgroup group_init Library initialization
105
106 \brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object.
107
108 \defgroup group_alloc Memory allocation
109
110 \brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images.
111 Most basic ones being: vmaCreateBuffer(), vmaCreateImage().
112
113 \defgroup group_virtual Virtual allocator
114
115 \brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm
116 for user-defined purpose without allocating any real GPU memory.
117
118 \defgroup group_stats Statistics
119
120 \brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format.
121 See documentation chapter: \ref statistics.
122 */
123
124
125 #ifdef __cplusplus
126 extern "C" {
127 #endif
128
129 #ifndef VULKAN_H_
130 #include <vulkan/vulkan.h>
131 #endif
132
133 // Define this macro to declare maximum supported Vulkan version in format AAABBBCCC,
134 // where AAA = major, BBB = minor, CCC = patch.
135 // If you want to use version > 1.0, it still needs to be enabled via VmaAllocatorCreateInfo::vulkanApiVersion.
136 #if !defined(VMA_VULKAN_VERSION)
137 #if defined(VK_VERSION_1_3)
138 #define VMA_VULKAN_VERSION 1003000
139 #elif defined(VK_VERSION_1_2)
140 #define VMA_VULKAN_VERSION 1002000
141 #elif defined(VK_VERSION_1_1)
142 #define VMA_VULKAN_VERSION 1001000
143 #else
144 #define VMA_VULKAN_VERSION 1000000
145 #endif
146 #endif
147
148 #if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS
149 extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
150 extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
151 extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
152 extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
153 extern PFN_vkAllocateMemory vkAllocateMemory;
154 extern PFN_vkFreeMemory vkFreeMemory;
155 extern PFN_vkMapMemory vkMapMemory;
156 extern PFN_vkUnmapMemory vkUnmapMemory;
157 extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
158 extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
159 extern PFN_vkBindBufferMemory vkBindBufferMemory;
160 extern PFN_vkBindImageMemory vkBindImageMemory;
161 extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
162 extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
163 extern PFN_vkCreateBuffer vkCreateBuffer;
164 extern PFN_vkDestroyBuffer vkDestroyBuffer;
165 extern PFN_vkCreateImage vkCreateImage;
166 extern PFN_vkDestroyImage vkDestroyImage;
167 extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
168 #if VMA_VULKAN_VERSION >= 1001000
169 extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
170 extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
171 extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;
172 extern PFN_vkBindImageMemory2 vkBindImageMemory2;
173 extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;
174 #endif // #if VMA_VULKAN_VERSION >= 1001000
175 #endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES
176
177 #if !defined(VMA_DEDICATED_ALLOCATION)
178 #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
179 #define VMA_DEDICATED_ALLOCATION 1
180 #else
181 #define VMA_DEDICATED_ALLOCATION 0
182 #endif
183 #endif
184
185 #if !defined(VMA_BIND_MEMORY2)
186 #if VK_KHR_bind_memory2
187 #define VMA_BIND_MEMORY2 1
188 #else
189 #define VMA_BIND_MEMORY2 0
190 #endif
191 #endif
192
193 #if !defined(VMA_MEMORY_BUDGET)
194 #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)
195 #define VMA_MEMORY_BUDGET 1
196 #else
197 #define VMA_MEMORY_BUDGET 0
198 #endif
199 #endif
200
201 // Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.
202 #if !defined(VMA_BUFFER_DEVICE_ADDRESS)
203 #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000
204 #define VMA_BUFFER_DEVICE_ADDRESS 1
205 #else
206 #define VMA_BUFFER_DEVICE_ADDRESS 0
207 #endif
208 #endif
209
210 // Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.
211 #if !defined(VMA_MEMORY_PRIORITY)
212 #if VK_EXT_memory_priority
213 #define VMA_MEMORY_PRIORITY 1
214 #else
215 #define VMA_MEMORY_PRIORITY 0
216 #endif
217 #endif
218
219 // Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.
220 #if !defined(VMA_EXTERNAL_MEMORY)
221 #if VK_KHR_external_memory
222 #define VMA_EXTERNAL_MEMORY 1
223 #else
224 #define VMA_EXTERNAL_MEMORY 0
225 #endif
226 #endif
227
228 // Define these macros to decorate all public functions with additional code,
229 // before and after returned type, appropriately. This may be useful for
230 // exporting the functions when compiling VMA as a separate library. Example:
231 // #define VMA_CALL_PRE __declspec(dllexport)
232 // #define VMA_CALL_POST __cdecl
233 #ifndef VMA_CALL_PRE
234 #define VMA_CALL_PRE
235 #endif
236 #ifndef VMA_CALL_POST
237 #define VMA_CALL_POST
238 #endif
239
240 // Define this macro to decorate pointers with an attribute specifying the
241 // length of the array they point to if they are not null.
242 //
243 // The length may be one of
244 // - The name of another parameter in the argument list where the pointer is declared
245 // - The name of another member in the struct where the pointer is declared
246 // - The name of a member of a struct type, meaning the value of that member in
247 // the context of the call. For example
248 // VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),
249 // this means the number of memory heaps available in the device associated
250 // with the VmaAllocator being dealt with.
251 #ifndef VMA_LEN_IF_NOT_NULL
252 #define VMA_LEN_IF_NOT_NULL(len)
253 #endif
254
255 // The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.
256 // see: https://clang.llvm.org/docs/AttributeReference.html#nullable
257 #ifndef VMA_NULLABLE
258 #ifdef __clang__
259 #define VMA_NULLABLE _Nullable
260 #else
261 #define VMA_NULLABLE
262 #endif
263 #endif
264
265 // The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.
266 // see: https://clang.llvm.org/docs/AttributeReference.html#nonnull
267 #ifndef VMA_NOT_NULL
268 #ifdef __clang__
269 #define VMA_NOT_NULL _Nonnull
270 #else
271 #define VMA_NOT_NULL
272 #endif
273 #endif
274
275 // If non-dispatchable handles are represented as pointers then we can give
276 // then nullability annotations
277 #ifndef VMA_NOT_NULL_NON_DISPATCHABLE
278 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
279 #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL
280 #else
281 #define VMA_NOT_NULL_NON_DISPATCHABLE
282 #endif
283 #endif
284
285 #ifndef VMA_NULLABLE_NON_DISPATCHABLE
286 #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
287 #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE
288 #else
289 #define VMA_NULLABLE_NON_DISPATCHABLE
290 #endif
291 #endif
292
293 #ifndef VMA_STATS_STRING_ENABLED
294 #define VMA_STATS_STRING_ENABLED 1
295 #endif
296
297 ////////////////////////////////////////////////////////////////////////////////
298 ////////////////////////////////////////////////////////////////////////////////
299 //
300 // INTERFACE
301 //
302 ////////////////////////////////////////////////////////////////////////////////
303 ////////////////////////////////////////////////////////////////////////////////
304
305 // Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE.
306 #ifndef _VMA_ENUM_DECLARATIONS
307
308 /**
309 \addtogroup group_init
310 @{
311 */
312
313 /// Flags for created #VmaAllocator.
314 typedef enum VmaAllocatorCreateFlagBits
315 {
316 /** \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.
317
318 Using this flag may increase performance because internal mutexes are not used.
319 */
320 VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
321 /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
322
323 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
324 When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
325
326 Using this extension will automatically allocate dedicated blocks of memory for
327 some buffers and images instead of suballocating place for them out of bigger
328 memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
329 flag) when it is recommended by the driver. It may improve performance on some
330 GPUs.
331
332 You may set this flag only if you found out that following device extensions are
333 supported, you enabled them while creating Vulkan device passed as
334 VmaAllocatorCreateInfo::device, and you want them to be used internally by this
335 library:
336
337 - VK_KHR_get_memory_requirements2 (device extension)
338 - VK_KHR_dedicated_allocation (device extension)
339
340 When this flag is set, you can experience following warnings reported by Vulkan
341 validation layer. You can ignore them.
342
343 > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
344 */
345 VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
346 /**
347 Enables usage of VK_KHR_bind_memory2 extension.
348
349 The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.
350 When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
351
352 You may set this flag only if you found out that this device extension is supported,
353 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
354 and you want it to be used internally by this library.
355
356 The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
357 which allow to pass a chain of `pNext` structures while binding.
358 This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2().
359 */
360 VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,
361 /**
362 Enables usage of VK_EXT_memory_budget extension.
363
364 You may set this flag only if you found out that this device extension is supported,
365 you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
366 and you want it to be used internally by this library, along with another instance extension
367 VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
368
369 The extension provides query for current memory usage and budget, which will probably
370 be more accurate than an estimation used by the library otherwise.
371 */
372 VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,
373 /**
374 Enables usage of VK_AMD_device_coherent_memory extension.
375
376 You may set this flag only if you:
377
378 - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,
379 - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,
380 - want it to be used internally by this library.
381
382 The extension and accompanying device feature provide access to memory types with
383 `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.
384 They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.
385
386 When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.
387 To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,
388 returning `VK_ERROR_FEATURE_NOT_PRESENT`.
389 */
390 VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,
391 /**
392 Enables usage of "buffer device address" feature, which allows you to use function
393 `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.
394
395 You may set this flag only if you:
396
397 1. (For Vulkan version < 1.2) Found as available and enabled device extension
398 VK_KHR_buffer_device_address.
399 This extension is promoted to core Vulkan 1.2.
400 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.
401
402 When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.
403 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to
404 allocated memory blocks wherever it might be needed.
405
406 For more information, see documentation chapter \ref enabling_buffer_device_address.
407 */
408 VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,
409 /**
410 Enables usage of VK_EXT_memory_priority extension in the library.
411
412 You may set this flag only if you found available and enabled this device extension,
413 along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,
414 while creating Vulkan device passed as VmaAllocatorCreateInfo::device.
415
416 When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority
417 are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.
418
419 A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
420 Larger values are higher priority. The granularity of the priorities is implementation-dependent.
421 It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.
422 The value to be used for default priority is 0.5.
423 For more details, see the documentation of the VK_EXT_memory_priority extension.
424 */
425 VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,
426
427 VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
428 } VmaAllocatorCreateFlagBits;
429 /// See #VmaAllocatorCreateFlagBits.
430 typedef VkFlags VmaAllocatorCreateFlags;
431
432 /** @} */
433
434 /**
435 \addtogroup group_alloc
436 @{
437 */
438
439 /// \brief Intended usage of the allocated memory.
440 typedef enum VmaMemoryUsage
441 {
442 /** No intended memory usage specified.
443 Use other members of VmaAllocationCreateInfo to specify your requirements.
444 */
445 VMA_MEMORY_USAGE_UNKNOWN = 0,
446 /**
447 \deprecated Obsolete, preserved for backward compatibility.
448 Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
449 */
450 VMA_MEMORY_USAGE_GPU_ONLY = 1,
451 /**
452 \deprecated Obsolete, preserved for backward compatibility.
453 Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`.
454 */
455 VMA_MEMORY_USAGE_CPU_ONLY = 2,
456 /**
457 \deprecated Obsolete, preserved for backward compatibility.
458 Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
459 */
460 VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
461 /**
462 \deprecated Obsolete, preserved for backward compatibility.
463 Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.
464 */
465 VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
466 /**
467 \deprecated Obsolete, preserved for backward compatibility.
468 Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
469 */
470 VMA_MEMORY_USAGE_CPU_COPY = 5,
471 /**
472 Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
473 Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
474
475 Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
476
477 Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
478 */
479 VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,
480 /**
481 Selects best memory type automatically.
482 This flag is recommended for most common use cases.
483
484 When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
485 you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
486 in VmaAllocationCreateInfo::flags.
487
488 It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
489 vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
490 and not with generic memory allocation functions.
491 */
492 VMA_MEMORY_USAGE_AUTO = 7,
493 /**
494 Selects best memory type automatically with preference for GPU (device) memory.
495
496 When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
497 you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
498 in VmaAllocationCreateInfo::flags.
499
500 It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
501 vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
502 and not with generic memory allocation functions.
503 */
504 VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8,
505 /**
506 Selects best memory type automatically with preference for CPU (host) memory.
507
508 When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),
509 you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
510 in VmaAllocationCreateInfo::flags.
511
512 It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.
513 vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()
514 and not with generic memory allocation functions.
515 */
516 VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9,
517
518 VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
519 } VmaMemoryUsage;
520
521 /// Flags to be passed as VmaAllocationCreateInfo::flags.
522 typedef enum VmaAllocationCreateFlagBits
523 {
524 /** \brief Set this flag if the allocation should have its own memory block.
525
526 Use it for special, big resources, like fullscreen images used as attachments.
527 */
528 VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
529
530 /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
531
532 If new allocation cannot be placed in any of the existing blocks, allocation
533 fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
534
535 You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
536 #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
537 */
538 VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
539 /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
540
541 Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
542
543 It is valid to use this flag for allocation made from memory type that is not
544 `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
545 useful if you need an allocation that is efficient to use on GPU
546 (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
547 support it (e.g. Intel GPU).
548 */
549 VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
550 /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead.
551
552 Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
553 null-terminated string. Instead of copying pointer value, a local copy of the
554 string is made and stored in allocation's `pName`. The string is automatically
555 freed together with the allocation. It is also used in vmaBuildStatsString().
556 */
557 VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
558 /** Allocation will be created from upper stack in a double stack pool.
559
560 This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
561 */
562 VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
563 /** Create both buffer/image and allocation, but don't bind them together.
564 It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
565 The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage().
566 Otherwise it is ignored.
567
568 If you want to make sure the new buffer/image is not tied to the new memory allocation
569 through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block,
570 use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT.
571 */
572 VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,
573 /** Create allocation only if additional device memory required for it, if any, won't exceed
574 memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
575 */
576 VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,
577 /** \brief Set this flag if the allocated memory will have aliasing resources.
578
579 Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified.
580 Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors.
581 */
582 VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200,
583 /**
584 Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).
585
586 - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,
587 you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.
588 - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.
589 This includes allocations created in \ref custom_memory_pools.
590
591 Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number,
592 never read or accessed randomly, so a memory type can be selected that is uncached and write-combined.
593
594 \warning Violating this declaration may work correctly, but will likely be very slow.
595 Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;`
596 Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once.
597 */
598 VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400,
599 /**
600 Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).
601
602 - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,
603 you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.
604 - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.
605 This includes allocations created in \ref custom_memory_pools.
606
607 Declares that mapped memory can be read, written, and accessed in random order,
608 so a `HOST_CACHED` memory type is required.
609 */
610 VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800,
611 /**
612 Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
613 it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected
614 if it may improve performance.
615
616 By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type
617 (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and
618 issue an explicit transfer to write/read your data.
619 To prepare for this possibility, don't forget to add appropriate flags like
620 `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image.
621 */
622 VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000,
623 /** Allocation strategy that chooses smallest possible free range for the allocation
624 to minimize memory usage and fragmentation, possibly at the expense of allocation time.
625 */
626 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000,
627 /** Allocation strategy that chooses first suitable free range for the allocation -
628 not necessarily in terms of the smallest offset but the one that is easiest and fastest to find
629 to minimize allocation time, possibly at the expense of allocation quality.
630 */
631 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000,
632 /** Allocation strategy that chooses always the lowest offset in available space.
633 This is not the most efficient strategy but achieves highly packed data.
634 Used internally by defragmentation, not recomended in typical usage.
635 */
636 VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = 0x00040000,
637 /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT.
638 */
639 VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
640 /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT.
641 */
642 VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
643 /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
644 */
645 VMA_ALLOCATION_CREATE_STRATEGY_MASK =
646 VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT |
647 VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT |
648 VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
649
650 VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
651 } VmaAllocationCreateFlagBits;
652 /// See #VmaAllocationCreateFlagBits.
653 typedef VkFlags VmaAllocationCreateFlags;
654
655 /// Flags to be passed as VmaPoolCreateInfo::flags.
656 typedef enum VmaPoolCreateFlagBits
657 {
658 /** \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.
659
660 This is an optional optimization flag.
661
662 If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
663 vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
664 knows exact type of your allocations so it can handle Buffer-Image Granularity
665 in the optimal way.
666
667 If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
668 exact type of such allocations is not known, so allocator must be conservative
669 in handling Buffer-Image Granularity, which can lead to suboptimal allocation
670 (wasted memory). In that case, if you can make sure you always allocate only
671 buffers and linear images or only optimal images out of this pool, use this flag
672 to make allocator disregard Buffer-Image Granularity and so make allocations
673 faster and more optimal.
674 */
675 VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
676
677 /** \brief Enables alternative, linear allocation algorithm in this pool.
678
679 Specify this flag to enable linear allocation algorithm, which always creates
680 new allocations after last one and doesn't reuse space from allocations freed in
681 between. It trades memory consumption for simplified algorithm and data
682 structure, which has better performance and uses less memory for metadata.
683
684 By using this flag, you can achieve behavior of free-at-once, stack,
685 ring buffer, and double stack.
686 For details, see documentation chapter \ref linear_algorithm.
687 */
688 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
689
690 /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
691 */
692 VMA_POOL_CREATE_ALGORITHM_MASK =
693 VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT,
694
695 VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
696 } VmaPoolCreateFlagBits;
697 /// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.
698 typedef VkFlags VmaPoolCreateFlags;
699
700 /// Flags to be passed as VmaDefragmentationInfo::flags.
701 typedef enum VmaDefragmentationFlagBits
702 {
703 /* \brief Use simple but fast algorithm for defragmentation.
704 May not achieve best results but will require least time to compute and least allocations to copy.
705 */
706 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1,
707 /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified.
708 Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved.
709 */
710 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2,
711 /* \brief Perform full defragmentation of memory.
712 Can result in notably more time to compute and allocations to copy, but will achieve best memory packing.
713 */
714 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4,
715 /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make.
716 Only available when bufferImageGranularity is greater than 1, since it aims to reduce
717 alignment issues between different types of resources.
718 Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT.
719 */
720 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8,
721
722 /// A bit mask to extract only `ALGORITHM` bits from entire set of flags.
723 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK =
724 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT |
725 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT |
726 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT |
727 VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT,
728
729 VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
730 } VmaDefragmentationFlagBits;
731 /// See #VmaDefragmentationFlagBits.
732 typedef VkFlags VmaDefragmentationFlags;
733
734 /// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove.
735 typedef enum VmaDefragmentationMoveOperation
736 {
737 /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass().
738 VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0,
739 /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged.
740 VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1,
741 /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed.
742 VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2,
743 } VmaDefragmentationMoveOperation;
744
745 /** @} */
746
747 /**
748 \addtogroup group_virtual
749 @{
750 */
751
752 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags.
753 typedef enum VmaVirtualBlockCreateFlagBits
754 {
755 /** \brief Enables alternative, linear allocation algorithm in this virtual block.
756
757 Specify this flag to enable linear allocation algorithm, which always creates
758 new allocations after last one and doesn't reuse space from allocations freed in
759 between. It trades memory consumption for simplified algorithm and data
760 structure, which has better performance and uses less memory for metadata.
761
762 By using this flag, you can achieve behavior of free-at-once, stack,
763 ring buffer, and double stack.
764 For details, see documentation chapter \ref linear_algorithm.
765 */
766 VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,
767
768 /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.
769 */
770 VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =
771 VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT,
772
773 VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
774 } VmaVirtualBlockCreateFlagBits;
775 /// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.
776 typedef VkFlags VmaVirtualBlockCreateFlags;
777
778 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.
779 typedef enum VmaVirtualAllocationCreateFlagBits
780 {
781 /** \brief Allocation will be created from upper stack in a double stack pool.
782
783 This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.
784 */
785 VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,
786 /** \brief Allocation strategy that tries to minimize memory usage.
787 */
788 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,
789 /** \brief Allocation strategy that tries to minimize allocation time.
790 */
791 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,
792 /** Allocation strategy that chooses always the lowest offset in available space.
793 This is not the most efficient strategy but achieves highly packed data.
794 */
795 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
796 /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.
797
798 These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.
799 */
800 VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,
801
802 VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
803 } VmaVirtualAllocationCreateFlagBits;
804 /// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.
805 typedef VkFlags VmaVirtualAllocationCreateFlags;
806
807 /** @} */
808
809 #endif // _VMA_ENUM_DECLARATIONS
810
811 #ifndef _VMA_DATA_TYPES_DECLARATIONS
812
813 /**
814 \addtogroup group_init
815 @{ */
816
817 /** \struct VmaAllocator
818 \brief Represents main object of this library initialized.
819
820 Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
821 Call function vmaDestroyAllocator() to destroy it.
822
823 It is recommended to create just one object of this type per `VkDevice` object,
824 right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
825 */
826 VK_DEFINE_HANDLE(VmaAllocator)
827
828 /** @} */
829
830 /**
831 \addtogroup group_alloc
832 @{
833 */
834
835 /** \struct VmaPool
836 \brief Represents custom memory pool
837
838 Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
839 Call function vmaDestroyPool() to destroy it.
840
841 For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
842 */
843 VK_DEFINE_HANDLE(VmaPool)
844
845 /** \struct VmaAllocation
846 \brief Represents single memory allocation.
847
848 It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
849 plus unique offset.
850
851 There are multiple ways to create such object.
852 You need to fill structure VmaAllocationCreateInfo.
853 For more information see [Choosing memory type](@ref choosing_memory_type).
854
855 Although the library provides convenience functions that create Vulkan buffer or image,
856 allocate memory for it and bind them together,
857 binding of the allocation to a buffer or an image is out of scope of the allocation itself.
858 Allocation object can exist without buffer/image bound,
859 binding can be done manually by the user, and destruction of it can be done
860 independently of destruction of the allocation.
861
862 The object also remembers its size and some other information.
863 To retrieve this information, use function vmaGetAllocationInfo() and inspect
864 returned structure VmaAllocationInfo.
865 */
866 VK_DEFINE_HANDLE(VmaAllocation)
867
868 /** \struct VmaDefragmentationContext
869 \brief An opaque object that represents started defragmentation process.
870
871 Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it.
872 Call function vmaEndDefragmentation() to destroy it.
873 */
874 VK_DEFINE_HANDLE(VmaDefragmentationContext)
875
876 /** @} */
877
878 /**
879 \addtogroup group_virtual
880 @{
881 */
882
883 /** \struct VmaVirtualAllocation
884 \brief Represents single memory allocation done inside VmaVirtualBlock.
885
886 Use it as a unique identifier to virtual allocation within the single block.
887
888 Use value `VK_NULL_HANDLE` to represent a null/invalid allocation.
889 */
890 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation);
891
892 /** @} */
893
894 /**
895 \addtogroup group_virtual
896 @{
897 */
898
899 /** \struct VmaVirtualBlock
900 \brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.
901
902 Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.
903 For more information, see documentation chapter \ref virtual_allocator.
904
905 This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally.
906 */
907 VK_DEFINE_HANDLE(VmaVirtualBlock)
908
909 /** @} */
910
911 /**
912 \addtogroup group_init
913 @{
914 */
915
916 /// Callback function called after successful vkAllocateMemory.
917 typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)(
918 VmaAllocator VMA_NOT_NULL allocator,
919 uint32_t memoryType,
920 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
921 VkDeviceSize size,
922 void* VMA_NULLABLE pUserData);
923
924 /// Callback function called before vkFreeMemory.
925 typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)(
926 VmaAllocator VMA_NOT_NULL allocator,
927 uint32_t memoryType,
928 VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,
929 VkDeviceSize size,
930 void* VMA_NULLABLE pUserData);
931
932 /** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
933
934 Provided for informative purpose, e.g. to gather statistics about number of
935 allocations or total amount of memory allocated in Vulkan.
936
937 Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
938 */
939 typedef struct VmaDeviceMemoryCallbacks
940 {
941 /// Optional, can be null.
942 PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;
943 /// Optional, can be null.
944 PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;
945 /// Optional, can be null.
946 void* VMA_NULLABLE pUserData;
947 } VmaDeviceMemoryCallbacks;
948
949 /** \brief Pointers to some Vulkan functions - a subset used by the library.
950
951 Used in VmaAllocatorCreateInfo::pVulkanFunctions.
952 */
953 typedef struct VmaVulkanFunctions
954 {
955 /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
956 PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr;
957 /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.
958 PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr;
959 PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;
960 PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;
961 PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;
962 PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;
963 PFN_vkMapMemory VMA_NULLABLE vkMapMemory;
964 PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;
965 PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;
966 PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;
967 PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;
968 PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;
969 PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;
970 PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;
971 PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;
972 PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;
973 PFN_vkCreateImage VMA_NULLABLE vkCreateImage;
974 PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;
975 PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;
976 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
977 /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
978 PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;
979 /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.
980 PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;
981 #endif
982 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
983 /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.
984 PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;
985 /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.
986 PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;
987 #endif
988 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
989 PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;
990 #endif
991 #if VMA_VULKAN_VERSION >= 1003000
992 /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
993 PFN_vkGetDeviceBufferMemoryRequirements VMA_NULLABLE vkGetDeviceBufferMemoryRequirements;
994 /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.
995 PFN_vkGetDeviceImageMemoryRequirements VMA_NULLABLE vkGetDeviceImageMemoryRequirements;
996 #endif
997 } VmaVulkanFunctions;
998
999 /// Description of a Allocator to be created.
1000 typedef struct VmaAllocatorCreateInfo
1001 {
1002 /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
1003 VmaAllocatorCreateFlags flags;
1004 /// Vulkan physical device.
1005 /** It must be valid throughout whole lifetime of created allocator. */
1006 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
1007 /// Vulkan device.
1008 /** It must be valid throughout whole lifetime of created allocator. */
1009 VkDevice VMA_NOT_NULL device;
1010 /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
1011 /** Set to 0 to use default, which is currently 256 MiB. */
1012 VkDeviceSize preferredLargeHeapBlockSize;
1013 /// Custom CPU memory allocation callbacks. Optional.
1014 /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
1015 const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
1016 /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
1017 /** Optional, can be null. */
1018 const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;
1019 /** \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.
1020
1021 If not NULL, it must be a pointer to an array of
1022 `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
1023 maximum number of bytes that can be allocated out of particular Vulkan memory
1024 heap.
1025
1026 Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
1027 heap. This is also the default in case of `pHeapSizeLimit` = NULL.
1028
1029 If there is a limit defined for a heap:
1030
1031 - If user tries to allocate more memory from that heap using this allocator,
1032 the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
1033 - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
1034 value of this limit will be reported instead when using vmaGetMemoryProperties().
1035
1036 Warning! Using this feature may not be equivalent to installing a GPU with
1037 smaller amount of memory, because graphics driver doesn't necessary fail new
1038 allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
1039 exceeded. It may return success and just silently migrate some device memory
1040 blocks to system RAM. This driver behavior can also be controlled using
1041 VK_AMD_memory_overallocation_behavior extension.
1042 */
1043 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;
1044
1045 /** \brief Pointers to Vulkan functions. Can be null.
1046
1047 For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).
1048 */
1049 const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;
1050 /** \brief Handle to Vulkan instance object.
1051
1052 Starting from version 3.0.0 this member is no longer optional, it must be set!
1053 */
1054 VkInstance VMA_NOT_NULL instance;
1055 /** \brief Optional. The highest version of Vulkan that the application is designed to use.
1056
1057 It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`.
1058 The patch version number specified is ignored. Only the major and minor versions are considered.
1059 It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.
1060 Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation.
1061 Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.
1062 */
1063 uint32_t vulkanApiVersion;
1064 size_t maxBlockCount = SIZE_MAX;
1065 #if VMA_EXTERNAL_MEMORY
1066 /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.
1067
1068 If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`
1069 elements, defining external memory handle types of particular Vulkan memory type,
1070 to be passed using `VkExportMemoryAllocateInfoKHR`.
1071
1072 Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.
1073 This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.
1074 */
1075 const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;
1076 #endif // #if VMA_EXTERNAL_MEMORY
1077 } VmaAllocatorCreateInfo;
1078
1079 /// Information about existing #VmaAllocator object.
1080 typedef struct VmaAllocatorInfo
1081 {
1082 /** \brief Handle to Vulkan instance object.
1083
1084 This is the same value as has been passed through VmaAllocatorCreateInfo::instance.
1085 */
1086 VkInstance VMA_NOT_NULL instance;
1087 /** \brief Handle to Vulkan physical device object.
1088
1089 This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.
1090 */
1091 VkPhysicalDevice VMA_NOT_NULL physicalDevice;
1092 /** \brief Handle to Vulkan device object.
1093
1094 This is the same value as has been passed through VmaAllocatorCreateInfo::device.
1095 */
1096 VkDevice VMA_NOT_NULL device;
1097 } VmaAllocatorInfo;
1098
1099 /** @} */
1100
1101 /**
1102 \addtogroup group_stats
1103 @{
1104 */
1105
1106 /** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total.
1107
1108 These are fast to calculate.
1109 See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics().
1110 */
1111 typedef struct VmaStatistics
1112 {
1113 /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated.
1114 */
1115 uint32_t blockCount;
1116 /** \brief Number of #VmaAllocation objects allocated.
1117
1118 Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`.
1119 */
1120 uint32_t allocationCount;
1121 /** \brief Number of bytes allocated in `VkDeviceMemory` blocks.
1122
1123 \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object
1124 (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls
1125 "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image.
1126 */
1127 VkDeviceSize blockBytes;
1128 /** \brief Total number of bytes occupied by all #VmaAllocation objects.
1129
1130 Always less or equal than `blockBytes`.
1131 Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan
1132 but unused by any #VmaAllocation.
1133 */
1134 VkDeviceSize allocationBytes;
1135 } VmaStatistics;
1136
1137 /** \brief More detailed statistics than #VmaStatistics.
1138
1139 These are slower to calculate. Use for debugging purposes.
1140 See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics().
1141
1142 Previous version of the statistics API provided averages, but they have been removed
1143 because they can be easily calculated as:
1144
1145 \code
1146 VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount;
1147 VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes;
1148 VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount;
1149 \endcode
1150 */
1151 typedef struct VmaDetailedStatistics
1152 {
1153 /// Basic statistics.
1154 VmaStatistics statistics;
1155 /// Number of free ranges of memory between allocations.
1156 uint32_t unusedRangeCount;
1157 /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations.
1158 VkDeviceSize allocationSizeMin;
1159 /// Largest allocation size. 0 if there are 0 allocations.
1160 VkDeviceSize allocationSizeMax;
1161 /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges.
1162 VkDeviceSize unusedRangeSizeMin;
1163 /// Largest empty range size. 0 if there are 0 empty ranges.
1164 VkDeviceSize unusedRangeSizeMax;
1165 } VmaDetailedStatistics;
1166
1167 /** \brief General statistics from current state of the Allocator -
1168 total memory usage across all memory heaps and types.
1169
1170 These are slower to calculate. Use for debugging purposes.
1171 See function vmaCalculateStatistics().
1172 */
1173 typedef struct VmaTotalStatistics
1174 {
1175 VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES];
1176 VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS];
1177 VmaDetailedStatistics total;
1178 } VmaTotalStatistics;
1179
1180 /** \brief Statistics of current memory usage and available budget for a specific memory heap.
1181
1182 These are fast to calculate.
1183 See function vmaGetHeapBudgets().
1184 */
1185 typedef struct VmaBudget
1186 {
1187 /** \brief Statistics fetched from the library.
1188 */
1189 VmaStatistics statistics;
1190 /** \brief Estimated current memory usage of the program, in bytes.
1191
1192 Fetched from system using VK_EXT_memory_budget extension if enabled.
1193
1194 It might be different than `statistics.blockBytes` (usually higher) due to additional implicit objects
1195 also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
1196 `VkDeviceMemory` blocks allocated outside of this library, if any.
1197 */
1198 VkDeviceSize usage;
1199 /** \brief Estimated amount of memory available to the program, in bytes.
1200
1201 Fetched from system using VK_EXT_memory_budget extension if enabled.
1202
1203 It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors
1204 external to the program, decided by the operating system.
1205 Difference `budget - usage` is the amount of additional memory that can probably
1206 be allocated without problems. Exceeding the budget may result in various problems.
1207 */
1208 VkDeviceSize budget;
1209 } VmaBudget;
1210
1211 /** @} */
1212
1213 /**
1214 \addtogroup group_alloc
1215 @{
1216 */
1217
1218 /** \brief Parameters of new #VmaAllocation.
1219
1220 To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others.
1221 */
1222 typedef struct VmaAllocationCreateInfo
1223 {
1224 /// Use #VmaAllocationCreateFlagBits enum.
1225 VmaAllocationCreateFlags flags;
1226 /** \brief Intended usage of memory.
1227
1228 You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
1229 If `pool` is not null, this member is ignored.
1230 */
1231 VmaMemoryUsage usage;
1232 /** \brief Flags that must be set in a Memory Type chosen for an allocation.
1233
1234 Leave 0 if you specify memory requirements in other way. \n
1235 If `pool` is not null, this member is ignored.*/
1236 VkMemoryPropertyFlags requiredFlags;
1237 /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
1238
1239 Set to 0 if no additional flags are preferred. \n
1240 If `pool` is not null, this member is ignored. */
1241 VkMemoryPropertyFlags preferredFlags;
1242 /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
1243
1244 Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
1245 it meets other requirements specified by this structure, with no further
1246 restrictions on memory type index. \n
1247 If `pool` is not null, this member is ignored.
1248 */
1249 uint32_t memoryTypeBits;
1250 /** \brief Pool that this allocation should be created in.
1251
1252 Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
1253 `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
1254 */
1255 VmaPool VMA_NULLABLE pool;
1256 /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
1257
1258 If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
1259 null or pointer to a null-terminated string. The string will be then copied to
1260 internal buffer, so it doesn't need to be valid after allocation call.
1261 */
1262 void* VMA_NULLABLE pUserData;
1263 /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.
1264
1265 It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object
1266 and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
1267 Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.
1268 */
1269 float priority;
1270 } VmaAllocationCreateInfo;
1271
1272 /// Describes parameter of created #VmaPool.
1273 typedef struct VmaPoolCreateInfo
1274 {
1275 /** \brief Vulkan memory type index to allocate this pool from.
1276 */
1277 uint32_t memoryTypeIndex;
1278 /** \brief Use combination of #VmaPoolCreateFlagBits.
1279 */
1280 VmaPoolCreateFlags flags;
1281 /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
1282
1283 Specify nonzero to set explicit, constant size of memory blocks used by this
1284 pool.
1285
1286 Leave 0 to use default and let the library manage block sizes automatically.
1287 Sizes of particular blocks may vary.
1288 In this case, the pool will also support dedicated allocations.
1289 */
1290 VkDeviceSize blockSize;
1291 /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
1292
1293 Set to 0 to have no preallocated blocks and allow the pool be completely empty.
1294 */
1295 size_t minBlockCount;
1296 /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
1297
1298 Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
1299
1300 Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
1301 throughout whole lifetime of this pool.
1302 */
1303 size_t maxBlockCount;
1304 /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.
1305
1306 It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.
1307 Otherwise, this variable is ignored.
1308 */
1309 float priority;
1310 /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.
1311
1312 Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.
1313 It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,
1314 e.g. when doing interop with OpenGL.
1315 */
1316 VkDeviceSize minAllocationAlignment;
1317 /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.
1318
1319 Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.
1320 It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.
1321 Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.
1322
1323 Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,
1324 can be attached automatically by this library when using other, more convenient of its features.
1325 */
1326 void* VMA_NULLABLE pMemoryAllocateNext;
1327 } VmaPoolCreateInfo;
1328
1329 /** @} */
1330
1331 /**
1332 \addtogroup group_alloc
1333 @{
1334 */
1335
1336 /// Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
1337 typedef struct VmaAllocationInfo
1338 {
1339 /** \brief Memory type index that this allocation was allocated from.
1340
1341 It never changes.
1342 */
1343 uint32_t memoryType;
1344 /** \brief Handle to Vulkan memory object.
1345
1346 Same memory object can be shared by multiple allocations.
1347
1348 It can change after the allocation is moved during \ref defragmentation.
1349 */
1350 VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;
1351 /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.
1352
1353 You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function
1354 vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,
1355 not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation
1356 and apply this offset automatically.
1357
1358 It can change after the allocation is moved during \ref defragmentation.
1359 */
1360 VkDeviceSize offset;
1361 /** \brief Size of this allocation, in bytes.
1362
1363 It never changes.
1364
1365 \note Allocation size returned in this variable may be greater than the size
1366 requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the
1367 allocation is accessible for operations on memory e.g. using a pointer after
1368 mapping with vmaMapMemory(), but operations on the resource e.g. using
1369 `vkCmdCopyBuffer` must be limited to the size of the resource.
1370 */
1371 VkDeviceSize size;
1372 /** \brief Pointer to the beginning of this allocation as mapped data.
1373
1374 If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
1375 created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.
1376
1377 It can change after call to vmaMapMemory(), vmaUnmapMemory().
1378 It can also change after the allocation is moved during \ref defragmentation.
1379 */
1380 void* VMA_NULLABLE pMappedData;
1381 /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
1382
1383 It can change after call to vmaSetAllocationUserData() for this allocation.
1384 */
1385 void* VMA_NULLABLE pUserData;
1386 /** \brief Custom allocation name that was set with vmaSetAllocationName().
1387
1388 It can change after call to vmaSetAllocationName() for this allocation.
1389
1390 Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with
1391 additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED].
1392 */
1393 const char* VMA_NULLABLE pName;
1394 } VmaAllocationInfo;
1395
1396 /** \brief Parameters for defragmentation.
1397
1398 To be used with function vmaBeginDefragmentation().
1399 */
1400 typedef struct VmaDefragmentationInfo
1401 {
1402 /// \brief Use combination of #VmaDefragmentationFlagBits.
1403 VmaDefragmentationFlags flags;
1404 /** \brief Custom pool to be defragmented.
1405
1406 If null then default pools will undergo defragmentation process.
1407 */
1408 VmaPool VMA_NULLABLE pool;
1409 /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places.
1410
1411 `0` means no limit.
1412 */
1413 VkDeviceSize maxBytesPerPass;
1414 /** \brief Maximum number of allocations that can be moved during single pass to a different place.
1415
1416 `0` means no limit.
1417 */
1418 uint32_t maxAllocationsPerPass;
1419 } VmaDefragmentationInfo;
1420
1421 /// Single move of an allocation to be done for defragmentation.
1422 typedef struct VmaDefragmentationMove
1423 {
1424 /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it.
1425 VmaDefragmentationMoveOperation operation;
1426 /// Allocation that should be moved.
1427 VmaAllocation VMA_NOT_NULL srcAllocation;
1428 /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`.
1429
1430 \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass,
1431 to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory().
1432 vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory.
1433 */
1434 VmaAllocation VMA_NOT_NULL dstTmpAllocation;
1435 } VmaDefragmentationMove;
1436
1437 /** \brief Parameters for incremental defragmentation steps.
1438
1439 To be used with function vmaBeginDefragmentationPass().
1440 */
1441 typedef struct VmaDefragmentationPassMoveInfo
1442 {
1443 /// Number of elements in the `pMoves` array.
1444 uint32_t moveCount;
1445 /** \brief Array of moves to be performed by the user in the current defragmentation pass.
1446
1447 Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass().
1448
1449 For each element, you should:
1450
1451 1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset.
1452 2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`.
1453 3. Make sure these commands finished executing on the GPU.
1454 4. Destroy the old buffer/image.
1455
1456 Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass().
1457 After this call, the allocation will point to the new place in memory.
1458
1459 Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
1460
1461 Alternatively, if you decide you want to completely remove the allocation:
1462
1463 1. Destroy its buffer/image.
1464 2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
1465
1466 Then, after vmaEndDefragmentationPass() the allocation will be freed.
1467 */
1468 VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves;
1469 } VmaDefragmentationPassMoveInfo;
1470
1471 /// Statistics returned for defragmentation process in function vmaEndDefragmentation().
1472 typedef struct VmaDefragmentationStats
1473 {
1474 /// Total number of bytes that have been copied while moving allocations to different places.
1475 VkDeviceSize bytesMoved;
1476 /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
1477 VkDeviceSize bytesFreed;
1478 /// Number of allocations that have been moved to different places.
1479 uint32_t allocationsMoved;
1480 /// Number of empty `VkDeviceMemory` objects that have been released to the system.
1481 uint32_t deviceMemoryBlocksFreed;
1482 } VmaDefragmentationStats;
1483
1484 /** @} */
1485
1486 /**
1487 \addtogroup group_virtual
1488 @{
1489 */
1490
1491 /// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().
1492 typedef struct VmaVirtualBlockCreateInfo
1493 {
1494 /** \brief Total size of the virtual block.
1495
1496 Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
1497 For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
1498 */
1499 VkDeviceSize size;
1500
1501 /** \brief Use combination of #VmaVirtualBlockCreateFlagBits.
1502 */
1503 VmaVirtualBlockCreateFlags flags;
1504
1505 /** \brief Custom CPU memory allocation callbacks. Optional.
1506
1507 Optional, can be null. When specified, they will be used for all CPU-side memory allocations.
1508 */
1509 const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;
1510 } VmaVirtualBlockCreateInfo;
1511
1512 /// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().
1513 typedef struct VmaVirtualAllocationCreateInfo
1514 {
1515 /** \brief Size of the allocation.
1516
1517 Cannot be zero.
1518 */
1519 VkDeviceSize size;
1520 /** \brief Required alignment of the allocation. Optional.
1521
1522 Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.
1523 */
1524 VkDeviceSize alignment;
1525 /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.
1526 */
1527 VmaVirtualAllocationCreateFlags flags;
1528 /** \brief Custom pointer to be associated with the allocation. Optional.
1529
1530 It can be any value and can be used for user-defined purposes. It can be fetched or changed later.
1531 */
1532 void* VMA_NULLABLE pUserData;
1533 } VmaVirtualAllocationCreateInfo;
1534
1535 /// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().
1536 typedef struct VmaVirtualAllocationInfo
1537 {
1538 /** \brief Offset of the allocation.
1539
1540 Offset at which the allocation was made.
1541 */
1542 VkDeviceSize offset;
1543 /** \brief Size of the allocation.
1544
1545 Same value as passed in VmaVirtualAllocationCreateInfo::size.
1546 */
1547 VkDeviceSize size;
1548 /** \brief Custom pointer associated with the allocation.
1549
1550 Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().
1551 */
1552 void* VMA_NULLABLE pUserData;
1553 } VmaVirtualAllocationInfo;
1554
1555 /** @} */
1556
1557 #endif // _VMA_DATA_TYPES_DECLARATIONS
1558
1559 #ifndef _VMA_FUNCTION_HEADERS
1560
1561 /**
1562 \addtogroup group_init
1563 @{
1564 */
1565
1566 /// Creates #VmaAllocator object.
1567 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
1568 const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,
1569 VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator);
1570
1571 /// Destroys allocator object.
1572 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
1573 VmaAllocator VMA_NULLABLE allocator);
1574
1575 /** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.
1576
1577 It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to
1578 `VkPhysicalDevice`, `VkDevice` etc. every time using this function.
1579 */
1580 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(
1581 VmaAllocator VMA_NOT_NULL allocator,
1582 VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);
1583
1584 /**
1585 PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
1586 You can access it here, without fetching it again on your own.
1587 */
1588 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
1589 VmaAllocator VMA_NOT_NULL allocator,
1590 const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties);
1591
1592 /**
1593 PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
1594 You can access it here, without fetching it again on your own.
1595 */
1596 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
1597 VmaAllocator VMA_NOT_NULL allocator,
1598 const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);
1599
1600 /**
1601 \brief Given Memory Type Index, returns Property Flags of this memory type.
1602
1603 This is just a convenience function. Same information can be obtained using
1604 vmaGetMemoryProperties().
1605 */
1606 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
1607 VmaAllocator VMA_NOT_NULL allocator,
1608 uint32_t memoryTypeIndex,
1609 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
1610
1611 /** \brief Sets index of the current frame.
1612 */
1613 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
1614 VmaAllocator VMA_NOT_NULL allocator,
1615 uint32_t frameIndex);
1616
1617 /** @} */
1618
1619 /**
1620 \addtogroup group_stats
1621 @{
1622 */
1623
1624 /** \brief Retrieves statistics from current state of the Allocator.
1625
1626 This function is called "calculate" not "get" because it has to traverse all
1627 internal data structures, so it may be quite slow. Use it for debugging purposes.
1628 For faster but more brief statistics suitable to be called every frame or every allocation,
1629 use vmaGetHeapBudgets().
1630
1631 Note that when using allocator from multiple threads, returned information may immediately
1632 become outdated.
1633 */
1634 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
1635 VmaAllocator VMA_NOT_NULL allocator,
1636 VmaTotalStatistics* VMA_NOT_NULL pStats);
1637
1638 /** \brief Free empty block of the Allocator.
1639 */
1640 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
1641 VmaAllocator VMA_NOT_NULL allocator);
1642
1643 /** \brief Retrieves information about current memory usage and budget for all memory heaps.
1644
1645 \param allocator
1646 \param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used.
1647
1648 This function is called "get" not "calculate" because it is very fast, suitable to be called
1649 every frame or every allocation. For more detailed statistics use vmaCalculateStatistics().
1650
1651 Note that when using allocator from multiple threads, returned information may immediately
1652 become outdated.
1653 */
1654 VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
1655 VmaAllocator VMA_NOT_NULL allocator,
1656 VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets);
1657
1658 /** @} */
1659
1660 /**
1661 \addtogroup group_alloc
1662 @{
1663 */
1664
1665 /**
1666 \brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
1667
1668 This algorithm tries to find a memory type that:
1669
1670 - Is allowed by memoryTypeBits.
1671 - Contains all the flags from pAllocationCreateInfo->requiredFlags.
1672 - Matches intended usage.
1673 - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
1674
1675 \return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
1676 from this function or any other allocating function probably means that your
1677 device doesn't support any memory type with requested features for the specific
1678 type of resource you want to use it for. Please check parameters of your
1679 resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
1680 */
1681 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
1682 VmaAllocator VMA_NOT_NULL allocator,
1683 uint32_t memoryTypeBits,
1684 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1685 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1686
1687 /**
1688 \brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
1689
1690 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1691 It internally creates a temporary, dummy buffer that never has memory bound.
1692 */
1693 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
1694 VmaAllocator VMA_NOT_NULL allocator,
1695 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
1696 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1697 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1698
1699 /**
1700 \brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
1701
1702 It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
1703 It internally creates a temporary, dummy image that never has memory bound.
1704 */
1705 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
1706 VmaAllocator VMA_NOT_NULL allocator,
1707 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
1708 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
1709 uint32_t* VMA_NOT_NULL pMemoryTypeIndex);
1710
1711 /** \brief Allocates Vulkan device memory and creates #VmaPool object.
1712
1713 \param allocator Allocator object.
1714 \param pCreateInfo Parameters of pool to create.
1715 \param[out] pPool Handle to created pool.
1716 */
1717 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
1718 VmaAllocator VMA_NOT_NULL allocator,
1719 const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,
1720 VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool);
1721
1722 /** \brief Destroys #VmaPool object and frees Vulkan device memory.
1723 */
1724 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
1725 VmaAllocator VMA_NOT_NULL allocator,
1726 VmaPool VMA_NULLABLE pool);
1727
1728 /** @} */
1729
1730 /**
1731 \addtogroup group_stats
1732 @{
1733 */
1734
1735 /** \brief Retrieves statistics of existing #VmaPool object.
1736
1737 \param allocator Allocator object.
1738 \param pool Pool object.
1739 \param[out] pPoolStats Statistics of specified pool.
1740 */
1741 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
1742 VmaAllocator VMA_NOT_NULL allocator,
1743 VmaPool VMA_NOT_NULL pool,
1744 VmaStatistics* VMA_NOT_NULL pPoolStats);
1745
1746 /** \brief Retrieves detailed statistics of existing #VmaPool object.
1747
1748 \param allocator Allocator object.
1749 \param pool Pool object.
1750 \param[out] pPoolStats Statistics of specified pool.
1751 */
1752 VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
1753 VmaAllocator VMA_NOT_NULL allocator,
1754 VmaPool VMA_NOT_NULL pool,
1755 VmaDetailedStatistics* VMA_NOT_NULL pPoolStats);
1756
1757 /** @} */
1758
1759 /**
1760 \addtogroup group_alloc
1761 @{
1762 */
1763
1764 /** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
1765
1766 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
1767 `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
1768 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
1769
1770 Possible return values:
1771
1772 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
1773 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
1774 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
1775 `VMA_ASSERT` is also fired in that case.
1776 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
1777 */
1778 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(
1779 VmaAllocator VMA_NOT_NULL allocator,
1780 VmaPool VMA_NOT_NULL pool);
1781
1782 /** \brief Retrieves name of a custom pool.
1783
1784 After the call `ppName` is either null or points to an internally-owned null-terminated string
1785 containing name of the pool that was previously set. The pointer becomes invalid when the pool is
1786 destroyed or its name is changed using vmaSetPoolName().
1787 */
1788 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
1789 VmaAllocator VMA_NOT_NULL allocator,
1790 VmaPool VMA_NOT_NULL pool,
1791 const char* VMA_NULLABLE* VMA_NOT_NULL ppName);
1792
1793 /** \brief Sets name of a custom pool.
1794
1795 `pName` can be either null or pointer to a null-terminated string with new name for the pool.
1796 Function makes internal copy of the string, so it can be changed or freed immediately after this call.
1797 */
1798 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
1799 VmaAllocator VMA_NOT_NULL allocator,
1800 VmaPool VMA_NOT_NULL pool,
1801 const char* VMA_NULLABLE pName);
1802
1803 /** \brief General purpose memory allocation.
1804
1805 \param allocator
1806 \param pVkMemoryRequirements
1807 \param pCreateInfo
1808 \param[out] pAllocation Handle to allocated memory.
1809 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1810
1811 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1812
1813 It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
1814 vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
1815 */
1816 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
1817 VmaAllocator VMA_NOT_NULL allocator,
1818 const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,
1819 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1820 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1821 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1822
1823 /** \brief General purpose memory allocation for multiple allocation objects at once.
1824
1825 \param allocator Allocator object.
1826 \param pVkMemoryRequirements Memory requirements for each allocation.
1827 \param pCreateInfo Creation parameters for each allocation.
1828 \param allocationCount Number of allocations to make.
1829 \param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
1830 \param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
1831
1832 You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
1833
1834 Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
1835 It is just a general purpose allocation function able to make multiple allocations at once.
1836 It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
1837
1838 All allocations are made using same parameters. All of them are created out of the same memory pool and type.
1839 If any allocation fails, all allocations already made within this function call are also freed, so that when
1840 returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
1841 */
1842 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
1843 VmaAllocator VMA_NOT_NULL allocator,
1844 const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,
1845 const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,
1846 size_t allocationCount,
1847 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,
1848 VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);
1849
1850 /** \brief Allocates memory suitable for given `VkBuffer`.
1851
1852 \param allocator
1853 \param buffer
1854 \param pCreateInfo
1855 \param[out] pAllocation Handle to allocated memory.
1856 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1857
1858 It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory().
1859
1860 This is a special-purpose function. In most cases you should use vmaCreateBuffer().
1861
1862 You must free the allocation using vmaFreeMemory() when no longer needed.
1863 */
1864 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
1865 VmaAllocator VMA_NOT_NULL allocator,
1866 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
1867 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1868 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1869 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1870
1871 /** \brief Allocates memory suitable for given `VkImage`.
1872
1873 \param allocator
1874 \param image
1875 \param pCreateInfo
1876 \param[out] pAllocation Handle to allocated memory.
1877 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
1878
1879 It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory().
1880
1881 This is a special-purpose function. In most cases you should use vmaCreateImage().
1882
1883 You must free the allocation using vmaFreeMemory() when no longer needed.
1884 */
1885 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
1886 VmaAllocator VMA_NOT_NULL allocator,
1887 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
1888 const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
1889 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
1890 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
1891
1892 /** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
1893
1894 Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
1895 */
1896 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
1897 VmaAllocator VMA_NOT_NULL allocator,
1898 const VmaAllocation VMA_NULLABLE allocation);
1899
1900 /** \brief Frees memory and destroys multiple allocations.
1901
1902 Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
1903 It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
1904 vmaAllocateMemoryPages() and other functions.
1905 It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
1906
1907 Allocations in `pAllocations` array can come from any memory pools and types.
1908 Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
1909 */
1910 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
1911 VmaAllocator VMA_NOT_NULL allocator,
1912 size_t allocationCount,
1913 const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);
1914
1915 /** \brief Returns current information about specified allocation.
1916
1917 Current paramteres of given allocation are returned in `pAllocationInfo`.
1918
1919 Although this function doesn't lock any mutex, so it should be quite efficient,
1920 you should avoid calling it too often.
1921 You can retrieve same VmaAllocationInfo structure while creating your resource, from function
1922 vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
1923 (e.g. due to defragmentation).
1924 */
1925 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
1926 VmaAllocator VMA_NOT_NULL allocator,
1927 VmaAllocation VMA_NOT_NULL allocation,
1928 VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);
1929
1930 /** \brief Sets pUserData in given allocation to new value.
1931
1932 The value of pointer `pUserData` is copied to allocation's `pUserData`.
1933 It is opaque, so you can use it however you want - e.g.
1934 as a pointer, ordinal number or some handle to you own data.
1935 */
1936 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
1937 VmaAllocator VMA_NOT_NULL allocator,
1938 VmaAllocation VMA_NOT_NULL allocation,
1939 void* VMA_NULLABLE pUserData);
1940
1941 /** \brief Sets pName in given allocation to new value.
1942
1943 `pName` must be either null, or pointer to a null-terminated string. The function
1944 makes local copy of the string and sets it as allocation's `pName`. String
1945 passed as pName doesn't need to be valid for whole lifetime of the allocation -
1946 you can free it after this call. String previously pointed by allocation's
1947 `pName` is freed from memory.
1948 */
1949 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
1950 VmaAllocator VMA_NOT_NULL allocator,
1951 VmaAllocation VMA_NOT_NULL allocation,
1952 const char* VMA_NULLABLE pName);
1953
1954 /**
1955 \brief Given an allocation, returns Property Flags of its memory type.
1956
1957 This is just a convenience function. Same information can be obtained using
1958 vmaGetAllocationInfo() + vmaGetMemoryProperties().
1959 */
1960 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
1961 VmaAllocator VMA_NOT_NULL allocator,
1962 VmaAllocation VMA_NOT_NULL allocation,
1963 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);
1964
1965 /** \brief Maps memory represented by given allocation and returns pointer to it.
1966
1967 Maps memory represented by given allocation to make it accessible to CPU code.
1968 When succeeded, `*ppData` contains pointer to first byte of this memory.
1969
1970 \warning
1971 If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is
1972 correctly offsetted to the beginning of region assigned to this particular allocation.
1973 Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block.
1974 You should not add VmaAllocationInfo::offset to it!
1975
1976 Mapping is internally reference-counted and synchronized, so despite raw Vulkan
1977 function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
1978 multiple times simultaneously, it is safe to call this function on allocations
1979 assigned to the same memory block. Actual Vulkan memory will be mapped on first
1980 mapping and unmapped on last unmapping.
1981
1982 If the function succeeded, you must call vmaUnmapMemory() to unmap the
1983 allocation when mapping is no longer needed or before freeing the allocation, at
1984 the latest.
1985
1986 It also safe to call this function multiple times on the same allocation. You
1987 must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
1988
1989 It is also safe to call this function on allocation created with
1990 #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
1991 You must still call vmaUnmapMemory() same number of times as you called
1992 vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
1993 "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
1994
1995 This function fails when used on allocation made in memory type that is not
1996 `HOST_VISIBLE`.
1997
1998 This function doesn't automatically flush or invalidate caches.
1999 If the allocation is made from a memory types that is not `HOST_COHERENT`,
2000 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
2001 */
2002 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
2003 VmaAllocator VMA_NOT_NULL allocator,
2004 VmaAllocation VMA_NOT_NULL allocation,
2005 void* VMA_NULLABLE* VMA_NOT_NULL ppData);
2006
2007 /** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
2008
2009 For details, see description of vmaMapMemory().
2010
2011 This function doesn't automatically flush or invalidate caches.
2012 If the allocation is made from a memory types that is not `HOST_COHERENT`,
2013 you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.
2014 */
2015 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
2016 VmaAllocator VMA_NOT_NULL allocator,
2017 VmaAllocation VMA_NOT_NULL allocation);
2018
2019 /** \brief Flushes memory of given allocation.
2020
2021 Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
2022 It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
2023 Unmap operation doesn't do that automatically.
2024
2025 - `offset` must be relative to the beginning of allocation.
2026 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2027 - `offset` and `size` don't have to be aligned.
2028 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2029 - If `size` is 0, this call is ignored.
2030 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2031 this call is ignored.
2032
2033 Warning! `offset` and `size` are relative to the contents of given `allocation`.
2034 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
2035 Do not pass allocation's offset as `offset`!!!
2036
2037 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
2038 called, otherwise `VK_SUCCESS`.
2039 */
2040 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
2041 VmaAllocator VMA_NOT_NULL allocator,
2042 VmaAllocation VMA_NOT_NULL allocation,
2043 VkDeviceSize offset,
2044 VkDeviceSize size);
2045
2046 /** \brief Invalidates memory of given allocation.
2047
2048 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
2049 It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
2050 Map operation doesn't do that automatically.
2051
2052 - `offset` must be relative to the beginning of allocation.
2053 - `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
2054 - `offset` and `size` don't have to be aligned.
2055 They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
2056 - If `size` is 0, this call is ignored.
2057 - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
2058 this call is ignored.
2059
2060 Warning! `offset` and `size` are relative to the contents of given `allocation`.
2061 If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively.
2062 Do not pass allocation's offset as `offset`!!!
2063
2064 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if
2065 it is called, otherwise `VK_SUCCESS`.
2066 */
2067 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
2068 VmaAllocator VMA_NOT_NULL allocator,
2069 VmaAllocation VMA_NOT_NULL allocation,
2070 VkDeviceSize offset,
2071 VkDeviceSize size);
2072
2073 /** \brief Flushes memory of given set of allocations.
2074
2075 Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.
2076 For more information, see documentation of vmaFlushAllocation().
2077
2078 \param allocator
2079 \param allocationCount
2080 \param allocations
2081 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
2082 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
2083
2084 This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is
2085 called, otherwise `VK_SUCCESS`.
2086 */
2087 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
2088 VmaAllocator VMA_NOT_NULL allocator,
2089 uint32_t allocationCount,
2090 const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
2091 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
2092 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
2093
2094 /** \brief Invalidates memory of given set of allocations.
2095
2096 Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.
2097 For more information, see documentation of vmaInvalidateAllocation().
2098
2099 \param allocator
2100 \param allocationCount
2101 \param allocations
2102 \param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.
2103 \param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.
2104
2105 This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is
2106 called, otherwise `VK_SUCCESS`.
2107 */
2108 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
2109 VmaAllocator VMA_NOT_NULL allocator,
2110 uint32_t allocationCount,
2111 const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,
2112 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
2113 const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
2114
2115 /** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
2116
2117 \param allocator
2118 \param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
2119
2120 Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
2121 `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
2122 `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
2123
2124 Possible return values:
2125
2126 - `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
2127 - `VK_SUCCESS` - corruption detection has been performed and succeeded.
2128 - `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations.
2129 `VMA_ASSERT` is also fired in that case.
2130 - Other value: Error returned by Vulkan, e.g. memory mapping failure.
2131 */
2132 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
2133 VmaAllocator VMA_NOT_NULL allocator,
2134 uint32_t memoryTypeBits);
2135
2136 /** \brief Begins defragmentation process.
2137
2138 \param allocator Allocator object.
2139 \param pInfo Structure filled with parameters of defragmentation.
2140 \param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation.
2141 \returns
2142 - `VK_SUCCESS` if defragmentation can begin.
2143 - `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported.
2144
2145 For more information about defragmentation, see documentation chapter:
2146 [Defragmentation](@ref defragmentation).
2147 */
2148 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
2149 VmaAllocator VMA_NOT_NULL allocator,
2150 const VmaDefragmentationInfo* VMA_NOT_NULL pInfo,
2151 VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext);
2152
2153 /** \brief Ends defragmentation process.
2154
2155 \param allocator Allocator object.
2156 \param context Context object that has been created by vmaBeginDefragmentation().
2157 \param[out] pStats Optional stats for the defragmentation. Can be null.
2158
2159 Use this function to finish defragmentation started by vmaBeginDefragmentation().
2160 */
2161 VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
2162 VmaAllocator VMA_NOT_NULL allocator,
2163 VmaDefragmentationContext VMA_NOT_NULL context,
2164 VmaDefragmentationStats* VMA_NULLABLE pStats);
2165
2166 /** \brief Starts single defragmentation pass.
2167
2168 \param allocator Allocator object.
2169 \param context Context object that has been created by vmaBeginDefragmentation().
2170 \param[out] pPassInfo Computed informations for current pass.
2171 \returns
2172 - `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation.
2173 - `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(),
2174 and then preferably try another pass with vmaBeginDefragmentationPass().
2175 */
2176 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
2177 VmaAllocator VMA_NOT_NULL allocator,
2178 VmaDefragmentationContext VMA_NOT_NULL context,
2179 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);
2180
2181 /** \brief Ends single defragmentation pass.
2182
2183 \param allocator Allocator object.
2184 \param context Context object that has been created by vmaBeginDefragmentation().
2185 \param pPassInfo Computed informations for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you.
2186
2187 Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible.
2188
2189 Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`.
2190 After this call:
2191
2192 - Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY
2193 (which is the default) will be pointing to the new destination place.
2194 - Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY
2195 will be freed.
2196
2197 If no more moves are possible you can end whole defragmentation.
2198 */
2199 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
2200 VmaAllocator VMA_NOT_NULL allocator,
2201 VmaDefragmentationContext VMA_NOT_NULL context,
2202 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);
2203
2204 /** \brief Binds buffer to allocation.
2205
2206 Binds specified buffer to region of memory represented by specified allocation.
2207 Gets `VkDeviceMemory` handle and offset from the allocation.
2208 If you want to create a buffer, allocate memory for it and bind them together separately,
2209 you should use this function for binding instead of standard `vkBindBufferMemory()`,
2210 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2211 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2212 (which is illegal in Vulkan).
2213
2214 It is recommended to use function vmaCreateBuffer() instead of this one.
2215 */
2216 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
2217 VmaAllocator VMA_NOT_NULL allocator,
2218 VmaAllocation VMA_NOT_NULL allocation,
2219 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);
2220
2221 /** \brief Binds buffer to allocation with additional parameters.
2222
2223 \param allocator
2224 \param allocation
2225 \param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2226 \param buffer
2227 \param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
2228
2229 This function is similar to vmaBindBufferMemory(), but it provides additional parameters.
2230
2231 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2232 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2233 */
2234 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
2235 VmaAllocator VMA_NOT_NULL allocator,
2236 VmaAllocation VMA_NOT_NULL allocation,
2237 VkDeviceSize allocationLocalOffset,
2238 VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,
2239 const void* VMA_NULLABLE pNext);
2240
2241 /** \brief Binds image to allocation.
2242
2243 Binds specified image to region of memory represented by specified allocation.
2244 Gets `VkDeviceMemory` handle and offset from the allocation.
2245 If you want to create an image, allocate memory for it and bind them together separately,
2246 you should use this function for binding instead of standard `vkBindImageMemory()`,
2247 because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
2248 allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
2249 (which is illegal in Vulkan).
2250
2251 It is recommended to use function vmaCreateImage() instead of this one.
2252 */
2253 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
2254 VmaAllocator VMA_NOT_NULL allocator,
2255 VmaAllocation VMA_NOT_NULL allocation,
2256 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);
2257
2258 /** \brief Binds image to allocation with additional parameters.
2259
2260 \param allocator
2261 \param allocation
2262 \param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
2263 \param image
2264 \param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
2265
2266 This function is similar to vmaBindImageMemory(), but it provides additional parameters.
2267
2268 If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
2269 or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
2270 */
2271 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
2272 VmaAllocator VMA_NOT_NULL allocator,
2273 VmaAllocation VMA_NOT_NULL allocation,
2274 VkDeviceSize allocationLocalOffset,
2275 VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,
2276 const void* VMA_NULLABLE pNext);
2277
2278 /** \brief Creates a new `VkBuffer`, allocates and binds memory for it.
2279
2280 \param allocator
2281 \param pBufferCreateInfo
2282 \param pAllocationCreateInfo
2283 \param[out] pBuffer Buffer that was created.
2284 \param[out] pAllocation Allocation that was created.
2285 \param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
2286
2287 This function automatically:
2288
2289 -# Creates buffer.
2290 -# Allocates appropriate memory for it.
2291 -# Binds the buffer with the memory.
2292
2293 If any of these operations fail, buffer and allocation are not created,
2294 returned value is negative error code, `*pBuffer` and `*pAllocation` are null.
2295
2296 If the function succeeded, you must destroy both buffer and allocation when you
2297 no longer need them using either convenience function vmaDestroyBuffer() or
2298 separately, using `vkDestroyBuffer()` and vmaFreeMemory().
2299
2300 If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
2301 VK_KHR_dedicated_allocation extension is used internally to query driver whether
2302 it requires or prefers the new buffer to have dedicated allocation. If yes,
2303 and if dedicated allocation is possible
2304 (#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
2305 allocation for this buffer, just like when using
2306 #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
2307
2308 \note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,
2309 although recommended as a good practice, is out of scope of this library and could be implemented
2310 by the user as a higher-level logic on top of VMA.
2311 */
2312 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
2313 VmaAllocator VMA_NOT_NULL allocator,
2314 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2315 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2316 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
2317 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2318 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2319
2320 /** \brief Creates a buffer with additional minimum alignment.
2321
2322 Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,
2323 minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.
2324 for interop with OpenGL.
2325 */
2326 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
2327 VmaAllocator VMA_NOT_NULL allocator,
2328 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2329 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2330 VkDeviceSize minAlignment,
2331 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,
2332 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2333 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2334
2335 /** \brief Creates a new `VkBuffer`, binds already created memory for it.
2336
2337 \param allocator
2338 \param allocation Allocation that provides memory to be used for binding new buffer to it.
2339 \param pBufferCreateInfo
2340 \param[out] pBuffer Buffer that was created.
2341
2342 This function automatically:
2343
2344 -# Creates buffer.
2345 -# Binds the buffer with the supplied memory.
2346
2347 If any of these operations fail, buffer is not created,
2348 returned value is negative error code and `*pBuffer` is null.
2349
2350 If the function succeeded, you must destroy the buffer when you
2351 no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding
2352 allocation you can use convenience function vmaDestroyBuffer().
2353 */
2354 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
2355 VmaAllocator VMA_NOT_NULL allocator,
2356 VmaAllocation VMA_NOT_NULL allocation,
2357 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
2358 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);
2359
2360 /** \brief Destroys Vulkan buffer and frees allocated memory.
2361
2362 This is just a convenience function equivalent to:
2363
2364 \code
2365 vkDestroyBuffer(device, buffer, allocationCallbacks);
2366 vmaFreeMemory(allocator, allocation);
2367 \endcode
2368
2369 It it safe to pass null as buffer and/or allocation.
2370 */
2371 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
2372 VmaAllocator VMA_NOT_NULL allocator,
2373 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,
2374 VmaAllocation VMA_NULLABLE allocation);
2375
2376 /// Function similar to vmaCreateBuffer().
2377 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
2378 VmaAllocator VMA_NOT_NULL allocator,
2379 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2380 const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,
2381 VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage,
2382 VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,
2383 VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);
2384
2385 /// Function similar to vmaCreateAliasingBuffer().
2386 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
2387 VmaAllocator VMA_NOT_NULL allocator,
2388 VmaAllocation VMA_NOT_NULL allocation,
2389 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
2390 VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);
2391
2392 /** \brief Destroys Vulkan image and frees allocated memory.
2393
2394 This is just a convenience function equivalent to:
2395
2396 \code
2397 vkDestroyImage(device, image, allocationCallbacks);
2398 vmaFreeMemory(allocator, allocation);
2399 \endcode
2400
2401 It it safe to pass null as image and/or allocation.
2402 */
2403 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
2404 VmaAllocator VMA_NOT_NULL allocator,
2405 VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
2406 VmaAllocation VMA_NULLABLE allocation);
2407
2408 /** @} */
2409
2410 /**
2411 \addtogroup group_virtual
2412 @{
2413 */
2414
2415 /** \brief Creates new #VmaVirtualBlock object.
2416
2417 \param pCreateInfo Parameters for creation.
2418 \param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.
2419 */
2420 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
2421 const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
2422 VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock);
2423
2424 /** \brief Destroys #VmaVirtualBlock object.
2425
2426 Please note that you should consciously handle virtual allocations that could remain unfreed in the block.
2427 You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()
2428 if you are sure this is what you want. If you do neither, an assert is called.
2429
2430 If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,
2431 don't forget to free them.
2432 */
2433 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(
2434 VmaVirtualBlock VMA_NULLABLE virtualBlock);
2435
2436 /** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.
2437 */
2438 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(
2439 VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2440
2441 /** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.
2442 */
2443 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(
2444 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2445 VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);
2446
2447 /** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.
2448
2449 If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned
2450 (despite the function doesn't ever allocate actual GPU memory).
2451 `pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`.
2452
2453 \param virtualBlock Virtual block
2454 \param pCreateInfo Parameters for the allocation
2455 \param[out] pAllocation Returned handle of the new allocation
2456 \param[out] pOffset Returned offset of the new allocation. Optional, can be null.
2457 */
2458 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(
2459 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2460 const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,
2461 VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
2462 VkDeviceSize* VMA_NULLABLE pOffset);
2463
2464 /** \brief Frees virtual allocation inside given #VmaVirtualBlock.
2465
2466 It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing.
2467 */
2468 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(
2469 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2470 VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation);
2471
2472 /** \brief Frees all virtual allocations inside given #VmaVirtualBlock.
2473
2474 You must either call this function or free each virtual allocation individually with vmaVirtualFree()
2475 before destroying a virtual block. Otherwise, an assert is called.
2476
2477 If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,
2478 don't forget to free it as well.
2479 */
2480 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(
2481 VmaVirtualBlock VMA_NOT_NULL virtualBlock);
2482
2483 /** \brief Changes custom pointer associated with given virtual allocation.
2484 */
2485 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(
2486 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2487 VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,
2488 void* VMA_NULLABLE pUserData);
2489
2490 /** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2491
2492 This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics().
2493 */
2494 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(
2495 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2496 VmaStatistics* VMA_NOT_NULL pStats);
2497
2498 /** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock.
2499
2500 This function is slow to call. Use for debugging purposes.
2501 For less detailed statistics, see vmaGetVirtualBlockStatistics().
2502 */
2503 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(
2504 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2505 VmaDetailedStatistics* VMA_NOT_NULL pStats);
2506
2507 /** @} */
2508
2509 #if VMA_STATS_STRING_ENABLED
2510 /**
2511 \addtogroup group_stats
2512 @{
2513 */
2514
2515 /** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.
2516 \param virtualBlock Virtual block.
2517 \param[out] ppStatsString Returned string.
2518 \param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.
2519
2520 Returned string must be freed using vmaFreeVirtualBlockStatsString().
2521 */
2522 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(
2523 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2524 char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2525 VkBool32 detailedMap);
2526
2527 /// Frees a string returned by vmaBuildVirtualBlockStatsString().
2528 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(
2529 VmaVirtualBlock VMA_NOT_NULL virtualBlock,
2530 char* VMA_NULLABLE pStatsString);
2531
2532 /** \brief Builds and returns statistics as a null-terminated string in JSON format.
2533 \param allocator
2534 \param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
2535 \param detailedMap
2536 */
2537 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
2538 VmaAllocator VMA_NOT_NULL allocator,
2539 char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,
2540 VkBool32 detailedMap);
2541
2542 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
2543 VmaAllocator VMA_NOT_NULL allocator,
2544 char* VMA_NULLABLE pStatsString);
2545
2546 /** @} */
2547
2548 #endif // VMA_STATS_STRING_ENABLED
2549
2550 #endif // _VMA_FUNCTION_HEADERS
2551
2552 #ifdef __cplusplus
2553 }
2554 #endif
2555
2556 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
2557
2558 ////////////////////////////////////////////////////////////////////////////////
2559 ////////////////////////////////////////////////////////////////////////////////
2560 //
2561 // IMPLEMENTATION
2562 //
2563 ////////////////////////////////////////////////////////////////////////////////
2564 ////////////////////////////////////////////////////////////////////////////////
2565
2566 // For Visual Studio IntelliSense.
2567 #if defined(__cplusplus) && defined(__INTELLISENSE__)
2568 #define VMA_IMPLEMENTATION
2569 #endif
2570
2571 #ifdef VMA_IMPLEMENTATION
2572 #undef VMA_IMPLEMENTATION
2573
2574 #include <cstdint>
2575 #include <cstdlib>
2576 #include <cstring>
2577 #include <utility>
2578 #include <type_traits>
2579
2580 #ifdef _MSC_VER
2581 #include <intrin.h> // For functions like __popcnt, _BitScanForward etc.
2582 #endif
2583 #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20
2584 #include <bit> // For std::popcount
2585 #endif
2586
2587 /*******************************************************************************
2588 CONFIGURATION SECTION
2589
2590 Define some of these macros before each #include of this header or change them
2591 here if you need other then default behavior depending on your environment.
2592 */
2593 #ifndef _VMA_CONFIGURATION
2594
2595 /*
2596 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2597 internally, like:
2598
2599 vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
2600 */
2601 #if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
2602 #define VMA_STATIC_VULKAN_FUNCTIONS 1
2603 #endif
2604
2605 /*
2606 Define this macro to 1 to make the library fetch pointers to Vulkan functions
2607 internally, like:
2608
2609 vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory");
2610
2611 To use this feature in new versions of VMA you now have to pass
2612 VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as
2613 VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null.
2614 */
2615 #if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)
2616 #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
2617 #endif
2618
2619 #ifndef VMA_USE_STL_SHARED_MUTEX
2620 // Compiler conforms to C++17.
2621 #if __cplusplus >= 201703L
2622 #define VMA_USE_STL_SHARED_MUTEX 1
2623 // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus
2624 // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.
2625 #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L
2626 #define VMA_USE_STL_SHARED_MUTEX 1
2627 #else
2628 #define VMA_USE_STL_SHARED_MUTEX 0
2629 #endif
2630 #endif
2631
2632 /*
2633 Define this macro to include custom header files without having to edit this file directly, e.g.:
2634
2635 // Inside of "my_vma_configuration_user_includes.h":
2636
2637 #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT
2638 #include "my_custom_min.h" // for my_custom_min
2639 #include <algorithm>
2640 #include <mutex>
2641
2642 // Inside a different file, which includes "vk_mem_alloc.h":
2643
2644 #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h"
2645 #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr)
2646 #define VMA_MIN(v1, v2) (my_custom_min(v1, v2))
2647 #include "vk_mem_alloc.h"
2648 ...
2649
2650 The following headers are used in this CONFIGURATION section only, so feel free to
2651 remove them if not needed.
2652 */
2653 #if !defined(VMA_CONFIGURATION_USER_INCLUDES_H)
2654 #include <cassert> // for assert
2655 #include <algorithm> // for min, max
2656 #include <mutex>
2657 #else
2658 #include VMA_CONFIGURATION_USER_INCLUDES_H
2659 #endif
2660
2661 #ifndef VMA_NULL
2662 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
2663 #define VMA_NULL nullptr
2664 #endif
2665
2666 #if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
2667 #include <cstdlib>
vma_aligned_alloc(size_t alignment,size_t size)2668 static void* vma_aligned_alloc(size_t alignment, size_t size)
2669 {
2670 // alignment must be >= sizeof(void*)
2671 if(alignment < sizeof(void*))
2672 {
2673 alignment = sizeof(void*);
2674 }
2675
2676 return memalign(alignment, size);
2677 }
2678 #elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))
2679 #include <cstdlib>
2680
2681 #if defined(__APPLE__)
2682 #include <AvailabilityMacros.h>
2683 #endif
2684
vma_aligned_alloc(size_t alignment,size_t size)2685 static void* vma_aligned_alloc(size_t alignment, size_t size)
2686 {
2687 // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4)
2688 // Therefore, for now disable this specific exception until a proper solution is found.
2689 //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))
2690 //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
2691 // // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only
2692 // // with the MacOSX11.0 SDK in Xcode 12 (which is what adds
2693 // // MAC_OS_X_VERSION_10_16), even though the function is marked
2694 // // availabe for 10.15. That is why the preprocessor checks for 10.16 but
2695 // // the __builtin_available checks for 10.15.
2696 // // People who use C++17 could call aligned_alloc with the 10.15 SDK already.
2697 // if (__builtin_available(macOS 10.15, iOS 13, *))
2698 // return aligned_alloc(alignment, size);
2699 //#endif
2700 //#endif
2701
2702 // alignment must be >= sizeof(void*)
2703 if(alignment < sizeof(void*))
2704 {
2705 alignment = sizeof(void*);
2706 }
2707
2708 void *pointer;
2709 if(posix_memalign(&pointer, alignment, size) == 0)
2710 return pointer;
2711 return VMA_NULL;
2712 }
2713 #elif defined(_WIN32)
vma_aligned_alloc(size_t alignment,size_t size)2714 static void* vma_aligned_alloc(size_t alignment, size_t size)
2715 {
2716 return _aligned_malloc(size, alignment);
2717 }
2718 #else
vma_aligned_alloc(size_t alignment,size_t size)2719 static void* vma_aligned_alloc(size_t alignment, size_t size)
2720 {
2721 return aligned_alloc(alignment, size);
2722 }
2723 #endif
2724
2725 #if defined(_WIN32)
vma_aligned_free(void * ptr)2726 static void vma_aligned_free(void* ptr)
2727 {
2728 _aligned_free(ptr);
2729 }
2730 #else
vma_aligned_free(void * VMA_NULLABLE ptr)2731 static void vma_aligned_free(void* VMA_NULLABLE ptr)
2732 {
2733 free(ptr);
2734 }
2735 #endif
2736
2737 // If your compiler is not compatible with C++11 and definition of
2738 // aligned_alloc() function is missing, uncommeting following line may help:
2739
2740 //#include <malloc.h>
2741
2742 // Normal assert to check for programmer's errors, especially in Debug configuration.
2743 #ifndef VMA_ASSERT
2744 #ifdef NDEBUG
2745 #define VMA_ASSERT(expr)
2746 #else
2747 #define VMA_ASSERT(expr) assert(expr)
2748 #endif
2749 #endif
2750
2751 // Assert that will be called very often, like inside data structures e.g. operator[].
2752 // Making it non-empty can make program slow.
2753 #ifndef VMA_HEAVY_ASSERT
2754 #ifdef NDEBUG
2755 #define VMA_HEAVY_ASSERT(expr)
2756 #else
2757 #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
2758 #endif
2759 #endif
2760
2761 #ifndef VMA_ALIGN_OF
2762 #define VMA_ALIGN_OF(type) (__alignof(type))
2763 #endif
2764
2765 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
2766 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))
2767 #endif
2768
2769 #ifndef VMA_SYSTEM_ALIGNED_FREE
2770 // VMA_SYSTEM_FREE is the old name, but might have been defined by the user
2771 #if defined(VMA_SYSTEM_FREE)
2772 #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr)
2773 #else
2774 #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr)
2775 #endif
2776 #endif
2777
2778 #ifndef VMA_COUNT_BITS_SET
2779 // Returns number of bits set to 1 in (v)
2780 #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v)
2781 #endif
2782
2783 #ifndef VMA_BITSCAN_LSB
2784 // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX
2785 #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask)
2786 #endif
2787
2788 #ifndef VMA_BITSCAN_MSB
2789 // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX
2790 #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask)
2791 #endif
2792
2793 #ifndef VMA_MIN
2794 #define VMA_MIN(v1, v2) ((std::min)((v1), (v2)))
2795 #endif
2796
2797 #ifndef VMA_MAX
2798 #define VMA_MAX(v1, v2) ((std::max)((v1), (v2)))
2799 #endif
2800
2801 #ifndef VMA_SWAP
2802 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
2803 #endif
2804
2805 #ifndef VMA_SORT
2806 #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
2807 #endif
2808
2809 #ifndef VMA_DEBUG_LOG
2810 #define VMA_DEBUG_LOG(format, ...)
2811 /*
2812 #define VMA_DEBUG_LOG(format, ...) do { \
2813 printf(format, __VA_ARGS__); \
2814 printf("\n"); \
2815 } while(false)
2816 */
2817 #endif
2818
2819 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
2820 #if VMA_STATS_STRING_ENABLED
VmaUint32ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint32_t num)2821 static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)
2822 {
2823 snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
2824 }
VmaUint64ToStr(char * VMA_NOT_NULL outStr,size_t strLen,uint64_t num)2825 static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)
2826 {
2827 snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
2828 }
VmaPtrToStr(char * VMA_NOT_NULL outStr,size_t strLen,const void * ptr)2829 static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)
2830 {
2831 snprintf(outStr, strLen, "%p", ptr);
2832 }
2833 #endif
2834
2835 #ifndef VMA_MUTEX
2836 class VmaMutex
2837 {
2838 public:
Lock()2839 void Lock() { m_Mutex.lock(); }
Unlock()2840 void Unlock() { m_Mutex.unlock(); }
TryLock()2841 bool TryLock() { return m_Mutex.try_lock(); }
2842 private:
2843 std::mutex m_Mutex;
2844 };
2845 #define VMA_MUTEX VmaMutex
2846 #endif
2847
2848 // Read-write mutex, where "read" is shared access, "write" is exclusive access.
2849 #ifndef VMA_RW_MUTEX
2850 #if VMA_USE_STL_SHARED_MUTEX
2851 // Use std::shared_mutex from C++17.
2852 #include <shared_mutex>
2853 class VmaRWMutex
2854 {
2855 public:
LockRead()2856 void LockRead() { m_Mutex.lock_shared(); }
UnlockRead()2857 void UnlockRead() { m_Mutex.unlock_shared(); }
TryLockRead()2858 bool TryLockRead() { return m_Mutex.try_lock_shared(); }
LockWrite()2859 void LockWrite() { m_Mutex.lock(); }
UnlockWrite()2860 void UnlockWrite() { m_Mutex.unlock(); }
TryLockWrite()2861 bool TryLockWrite() { return m_Mutex.try_lock(); }
2862 private:
2863 std::shared_mutex m_Mutex;
2864 };
2865 #define VMA_RW_MUTEX VmaRWMutex
2866 #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600
2867 // Use SRWLOCK from WinAPI.
2868 // Minimum supported client = Windows Vista, server = Windows Server 2008.
2869 class VmaRWMutex
2870 {
2871 public:
VmaRWMutex()2872 VmaRWMutex() { InitializeSRWLock(&m_Lock); }
LockRead()2873 void LockRead() { AcquireSRWLockShared(&m_Lock); }
UnlockRead()2874 void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
TryLockRead()2875 bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }
LockWrite()2876 void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
UnlockWrite()2877 void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
TryLockWrite()2878 bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }
2879 private:
2880 SRWLOCK m_Lock;
2881 };
2882 #define VMA_RW_MUTEX VmaRWMutex
2883 #else
2884 // Less efficient fallback: Use normal mutex.
2885 class VmaRWMutex
2886 {
2887 public:
LockRead()2888 void LockRead() { m_Mutex.Lock(); }
UnlockRead()2889 void UnlockRead() { m_Mutex.Unlock(); }
TryLockRead()2890 bool TryLockRead() { return m_Mutex.TryLock(); }
LockWrite()2891 void LockWrite() { m_Mutex.Lock(); }
UnlockWrite()2892 void UnlockWrite() { m_Mutex.Unlock(); }
TryLockWrite()2893 bool TryLockWrite() { return m_Mutex.TryLock(); }
2894 private:
2895 VMA_MUTEX m_Mutex;
2896 };
2897 #define VMA_RW_MUTEX VmaRWMutex
2898 #endif // #if VMA_USE_STL_SHARED_MUTEX
2899 #endif // #ifndef VMA_RW_MUTEX
2900
2901 /*
2902 If providing your own implementation, you need to implement a subset of std::atomic.
2903 */
2904 #ifndef VMA_ATOMIC_UINT32
2905 #include <atomic>
2906 #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
2907 #endif
2908
2909 #ifndef VMA_ATOMIC_UINT64
2910 #include <atomic>
2911 #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>
2912 #endif
2913
2914 #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
2915 /**
2916 Every allocation will have its own memory block.
2917 Define to 1 for debugging purposes only.
2918 */
2919 #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
2920 #endif
2921
2922 #ifndef VMA_MIN_ALIGNMENT
2923 /**
2924 Minimum alignment of all allocations, in bytes.
2925 Set to more than 1 for debugging purposes. Must be power of two.
2926 */
2927 #ifdef VMA_DEBUG_ALIGNMENT // Old name
2928 #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT
2929 #else
2930 #define VMA_MIN_ALIGNMENT (1)
2931 #endif
2932 #endif
2933
2934 #ifndef VMA_DEBUG_MARGIN
2935 /**
2936 Minimum margin after every allocation, in bytes.
2937 Set nonzero for debugging purposes only.
2938 */
2939 #define VMA_DEBUG_MARGIN (0)
2940 #endif
2941
2942 #ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
2943 /**
2944 Define this macro to 1 to automatically fill new allocations and destroyed
2945 allocations with some bit pattern.
2946 */
2947 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
2948 #endif
2949
2950 #ifndef VMA_DEBUG_DETECT_CORRUPTION
2951 /**
2952 Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
2953 enable writing magic value to the margin after every allocation and
2954 validating it, so that memory corruptions (out-of-bounds writes) are detected.
2955 */
2956 #define VMA_DEBUG_DETECT_CORRUPTION (0)
2957 #endif
2958
2959 #ifndef VMA_DEBUG_GLOBAL_MUTEX
2960 /**
2961 Set this to 1 for debugging purposes only, to enable single mutex protecting all
2962 entry calls to the library. Can be useful for debugging multithreading issues.
2963 */
2964 #define VMA_DEBUG_GLOBAL_MUTEX (0)
2965 #endif
2966
2967 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
2968 /**
2969 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
2970 Set to more than 1 for debugging purposes only. Must be power of two.
2971 */
2972 #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
2973 #endif
2974
2975 #ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
2976 /*
2977 Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount
2978 and return error instead of leaving up to Vulkan implementation what to do in such cases.
2979 */
2980 #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)
2981 #endif
2982
2983 #ifndef VMA_SMALL_HEAP_MAX_SIZE
2984 /// Maximum size of a memory heap in Vulkan to consider it "small".
2985 #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
2986 #endif
2987
2988 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
2989 /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
2990 #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
2991 #endif
2992
2993 /*
2994 Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called
2995 or a persistently mapped allocation is created and destroyed several times in a row.
2996 It keeps additional +1 mapping of a device memory block to prevent calling actual
2997 vkMapMemory/vkUnmapMemory too many times, which may improve performance and help
2998 tools like RenderDOc.
2999 */
3000 #ifndef VMA_MAPPING_HYSTERESIS_ENABLED
3001 #define VMA_MAPPING_HYSTERESIS_ENABLED 1
3002 #endif
3003
3004 #ifndef VMA_CLASS_NO_COPY
3005 #define VMA_CLASS_NO_COPY(className) \
3006 private: \
3007 className(const className&) = delete; \
3008 className& operator=(const className&) = delete;
3009 #endif
3010
3011 #define VMA_VALIDATE(cond) do { if(!(cond)) { \
3012 VMA_ASSERT(0 && "Validation failed: " #cond); \
3013 return false; \
3014 } } while(false)
3015
3016 /*******************************************************************************
3017 END OF CONFIGURATION
3018 */
3019 #endif // _VMA_CONFIGURATION
3020
3021
3022 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
3023 static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
3024 // Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
3025 static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
3026
3027 // Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.
3028 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;
3029 static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;
3030 static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;
3031 static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200;
3032 static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000;
3033 static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
3034 static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
3035 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
3036
3037 // This one is tricky. Vulkan specification defines this code as available since
3038 // Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131.
3039 // See pull request #207.
3040 #define VK_ERROR_UNKNOWN_COPY ((VkResult)-13)
3041
3042
3043 #if VMA_STATS_STRING_ENABLED
3044 // Correspond to values of enum VmaSuballocationType.
3045 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] =
3046 {
3047 "FREE",
3048 "UNKNOWN",
3049 "BUFFER",
3050 "IMAGE_UNKNOWN",
3051 "IMAGE_LINEAR",
3052 "IMAGE_OPTIMAL",
3053 };
3054 #endif
3055
3056 static VkAllocationCallbacks VmaEmptyAllocationCallbacks =
3057 { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
3058
3059
3060 #ifndef _VMA_ENUM_DECLARATIONS
3061
3062 enum VmaSuballocationType
3063 {
3064 VMA_SUBALLOCATION_TYPE_FREE = 0,
3065 VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
3066 VMA_SUBALLOCATION_TYPE_BUFFER = 2,
3067 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
3068 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
3069 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
3070 VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
3071 };
3072
3073 enum VMA_CACHE_OPERATION
3074 {
3075 VMA_CACHE_FLUSH,
3076 VMA_CACHE_INVALIDATE
3077 };
3078
3079 enum class VmaAllocationRequestType
3080 {
3081 Normal,
3082 TLSF,
3083 // Used by "Linear" algorithm.
3084 UpperAddress,
3085 EndOf1st,
3086 EndOf2nd,
3087 };
3088
3089 #endif // _VMA_ENUM_DECLARATIONS
3090
3091 #ifndef _VMA_FORWARD_DECLARATIONS
3092 // Opaque handle used by allocation algorithms to identify single allocation in any conforming way.
3093 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle);
3094
3095 struct VmaMutexLock;
3096 struct VmaMutexLockRead;
3097 struct VmaMutexLockWrite;
3098
3099 template<typename T>
3100 struct AtomicTransactionalIncrement;
3101
3102 template<typename T>
3103 struct VmaStlAllocator;
3104
3105 template<typename T, typename AllocatorT>
3106 class VmaVector;
3107
3108 template<typename T, typename AllocatorT, size_t N>
3109 class VmaSmallVector;
3110
3111 template<typename T>
3112 class VmaPoolAllocator;
3113
3114 template<typename T>
3115 struct VmaListItem;
3116
3117 template<typename T>
3118 class VmaRawList;
3119
3120 template<typename T, typename AllocatorT>
3121 class VmaList;
3122
3123 template<typename ItemTypeTraits>
3124 class VmaIntrusiveLinkedList;
3125
3126 // Unused in this version
3127 #if 0
3128 template<typename T1, typename T2>
3129 struct VmaPair;
3130 template<typename FirstT, typename SecondT>
3131 struct VmaPairFirstLess;
3132
3133 template<typename KeyT, typename ValueT>
3134 class VmaMap;
3135 #endif
3136
3137 #if VMA_STATS_STRING_ENABLED
3138 class VmaStringBuilder;
3139 class VmaJsonWriter;
3140 #endif
3141
3142 class VmaDeviceMemoryBlock;
3143
3144 struct VmaDedicatedAllocationListItemTraits;
3145 class VmaDedicatedAllocationList;
3146
3147 struct VmaSuballocation;
3148 struct VmaSuballocationOffsetLess;
3149 struct VmaSuballocationOffsetGreater;
3150 struct VmaSuballocationItemSizeLess;
3151
3152 typedef VmaList<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> VmaSuballocationList;
3153
3154 struct VmaAllocationRequest;
3155
3156 class VmaBlockMetadata;
3157 class VmaBlockMetadata_Linear;
3158 class VmaBlockMetadata_TLSF;
3159
3160 class VmaBlockVector;
3161
3162 struct VmaPoolListItemTraits;
3163
3164 struct VmaCurrentBudgetData;
3165
3166 class VmaAllocationObjectAllocator;
3167
3168 #endif // _VMA_FORWARD_DECLARATIONS
3169
3170
3171 #ifndef _VMA_FUNCTIONS
3172
3173 /*
3174 Returns number of bits set to 1 in (v).
3175
3176 On specific platforms and compilers you can use instrinsics like:
3177
3178 Visual Studio:
3179 return __popcnt(v);
3180 GCC, Clang:
3181 return static_cast<uint32_t>(__builtin_popcount(v));
3182
3183 Define macro VMA_COUNT_BITS_SET to provide your optimized implementation.
3184 But you need to check in runtime whether user's CPU supports these, as some old processors don't.
3185 */
VmaCountBitsSet(uint32_t v)3186 static inline uint32_t VmaCountBitsSet(uint32_t v)
3187 {
3188 #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20
3189 return std::popcount(v);
3190 #else
3191 uint32_t c = v - ((v >> 1) & 0x55555555);
3192 c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
3193 c = ((c >> 4) + c) & 0x0F0F0F0F;
3194 c = ((c >> 8) + c) & 0x00FF00FF;
3195 c = ((c >> 16) + c) & 0x0000FFFF;
3196 return c;
3197 #endif
3198 }
3199
VmaBitScanLSB(uint64_t mask)3200 static inline uint8_t VmaBitScanLSB(uint64_t mask)
3201 {
3202 #if defined(_MSC_VER) && defined(_WIN64)
3203 unsigned long pos;
3204 if (_BitScanForward64(&pos, mask))
3205 return static_cast<uint8_t>(pos);
3206 return UINT8_MAX;
3207 #elif defined __GNUC__ || defined __clang__
3208 return static_cast<uint8_t>(__builtin_ffsll(mask)) - 1U;
3209 #else
3210 uint8_t pos = 0;
3211 uint64_t bit = 1;
3212 do
3213 {
3214 if (mask & bit)
3215 return pos;
3216 bit <<= 1;
3217 } while (pos++ < 63);
3218 return UINT8_MAX;
3219 #endif
3220 }
3221
VmaBitScanLSB(uint32_t mask)3222 static inline uint8_t VmaBitScanLSB(uint32_t mask)
3223 {
3224 #ifdef _MSC_VER
3225 unsigned long pos;
3226 if (_BitScanForward(&pos, mask))
3227 return static_cast<uint8_t>(pos);
3228 return UINT8_MAX;
3229 #elif defined __GNUC__ || defined __clang__
3230 return static_cast<uint8_t>(__builtin_ffs(mask)) - 1U;
3231 #else
3232 uint8_t pos = 0;
3233 uint32_t bit = 1;
3234 do
3235 {
3236 if (mask & bit)
3237 return pos;
3238 bit <<= 1;
3239 } while (pos++ < 31);
3240 return UINT8_MAX;
3241 #endif
3242 }
3243
VmaBitScanMSB(uint64_t mask)3244 static inline uint8_t VmaBitScanMSB(uint64_t mask)
3245 {
3246 #if defined(_MSC_VER) && defined(_WIN64)
3247 unsigned long pos;
3248 if (_BitScanReverse64(&pos, mask))
3249 return static_cast<uint8_t>(pos);
3250 #elif defined __GNUC__ || defined __clang__
3251 if (mask)
3252 return 63 - static_cast<uint8_t>(__builtin_clzll(mask));
3253 #else
3254 uint8_t pos = 63;
3255 uint64_t bit = 1ULL << 63;
3256 do
3257 {
3258 if (mask & bit)
3259 return pos;
3260 bit >>= 1;
3261 } while (pos-- > 0);
3262 #endif
3263 return UINT8_MAX;
3264 }
3265
VmaBitScanMSB(uint32_t mask)3266 static inline uint8_t VmaBitScanMSB(uint32_t mask)
3267 {
3268 #ifdef _MSC_VER
3269 unsigned long pos;
3270 if (_BitScanReverse(&pos, mask))
3271 return static_cast<uint8_t>(pos);
3272 #elif defined __GNUC__ || defined __clang__
3273 if (mask)
3274 return 31 - static_cast<uint8_t>(__builtin_clz(mask));
3275 #else
3276 uint8_t pos = 31;
3277 uint32_t bit = 1UL << 31;
3278 do
3279 {
3280 if (mask & bit)
3281 return pos;
3282 bit >>= 1;
3283 } while (pos-- > 0);
3284 #endif
3285 return UINT8_MAX;
3286 }
3287
3288 /*
3289 Returns true if given number is a power of two.
3290 T must be unsigned integer number or signed integer but always nonnegative.
3291 For 0 returns true.
3292 */
3293 template <typename T>
VmaIsPow2(T x)3294 inline bool VmaIsPow2(T x)
3295 {
3296 return (x & (x - 1)) == 0;
3297 }
3298
3299 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
3300 // Use types like uint32_t, uint64_t as T.
3301 template <typename T>
VmaAlignUp(T val,T alignment)3302 static inline T VmaAlignUp(T val, T alignment)
3303 {
3304 VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
3305 return (val + alignment - 1) & ~(alignment - 1);
3306 }
3307
3308 // Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
3309 // Use types like uint32_t, uint64_t as T.
3310 template <typename T>
VmaAlignDown(T val,T alignment)3311 static inline T VmaAlignDown(T val, T alignment)
3312 {
3313 VMA_HEAVY_ASSERT(VmaIsPow2(alignment));
3314 return val & ~(alignment - 1);
3315 }
3316
3317 // Division with mathematical rounding to nearest number.
3318 template <typename T>
VmaRoundDiv(T x,T y)3319 static inline T VmaRoundDiv(T x, T y)
3320 {
3321 return (x + (y / (T)2)) / y;
3322 }
3323
3324 // Divide by 'y' and round up to nearest integer.
3325 template <typename T>
VmaDivideRoundingUp(T x,T y)3326 static inline T VmaDivideRoundingUp(T x, T y)
3327 {
3328 return (x + y - (T)1) / y;
3329 }
3330
3331 // Returns smallest power of 2 greater or equal to v.
VmaNextPow2(uint32_t v)3332 static inline uint32_t VmaNextPow2(uint32_t v)
3333 {
3334 v--;
3335 v |= v >> 1;
3336 v |= v >> 2;
3337 v |= v >> 4;
3338 v |= v >> 8;
3339 v |= v >> 16;
3340 v++;
3341 return v;
3342 }
3343
VmaNextPow2(uint64_t v)3344 static inline uint64_t VmaNextPow2(uint64_t v)
3345 {
3346 v--;
3347 v |= v >> 1;
3348 v |= v >> 2;
3349 v |= v >> 4;
3350 v |= v >> 8;
3351 v |= v >> 16;
3352 v |= v >> 32;
3353 v++;
3354 return v;
3355 }
3356
3357 // Returns largest power of 2 less or equal to v.
VmaPrevPow2(uint32_t v)3358 static inline uint32_t VmaPrevPow2(uint32_t v)
3359 {
3360 v |= v >> 1;
3361 v |= v >> 2;
3362 v |= v >> 4;
3363 v |= v >> 8;
3364 v |= v >> 16;
3365 v = v ^ (v >> 1);
3366 return v;
3367 }
3368
VmaPrevPow2(uint64_t v)3369 static inline uint64_t VmaPrevPow2(uint64_t v)
3370 {
3371 v |= v >> 1;
3372 v |= v >> 2;
3373 v |= v >> 4;
3374 v |= v >> 8;
3375 v |= v >> 16;
3376 v |= v >> 32;
3377 v = v ^ (v >> 1);
3378 return v;
3379 }
3380
VmaStrIsEmpty(const char * pStr)3381 static inline bool VmaStrIsEmpty(const char* pStr)
3382 {
3383 return pStr == VMA_NULL || *pStr == '\0';
3384 }
3385
3386 /*
3387 Returns true if two memory blocks occupy overlapping pages.
3388 ResourceA must be in less memory offset than ResourceB.
3389
3390 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
3391 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
3392 */
VmaBlocksOnSamePage(VkDeviceSize resourceAOffset,VkDeviceSize resourceASize,VkDeviceSize resourceBOffset,VkDeviceSize pageSize)3393 static inline bool VmaBlocksOnSamePage(
3394 VkDeviceSize resourceAOffset,
3395 VkDeviceSize resourceASize,
3396 VkDeviceSize resourceBOffset,
3397 VkDeviceSize pageSize)
3398 {
3399 VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
3400 VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
3401 VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
3402 VkDeviceSize resourceBStart = resourceBOffset;
3403 VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
3404 return resourceAEndPage == resourceBStartPage;
3405 }
3406
3407 /*
3408 Returns true if given suballocation types could conflict and must respect
3409 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
3410 or linear image and another one is optimal image. If type is unknown, behave
3411 conservatively.
3412 */
VmaIsBufferImageGranularityConflict(VmaSuballocationType suballocType1,VmaSuballocationType suballocType2)3413 static inline bool VmaIsBufferImageGranularityConflict(
3414 VmaSuballocationType suballocType1,
3415 VmaSuballocationType suballocType2)
3416 {
3417 if (suballocType1 > suballocType2)
3418 {
3419 VMA_SWAP(suballocType1, suballocType2);
3420 }
3421
3422 switch (suballocType1)
3423 {
3424 case VMA_SUBALLOCATION_TYPE_FREE:
3425 return false;
3426 case VMA_SUBALLOCATION_TYPE_UNKNOWN:
3427 return true;
3428 case VMA_SUBALLOCATION_TYPE_BUFFER:
3429 return
3430 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3431 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3432 case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
3433 return
3434 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
3435 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
3436 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3437 case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
3438 return
3439 suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
3440 case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
3441 return false;
3442 default:
3443 VMA_ASSERT(0);
3444 return true;
3445 }
3446 }
3447
VmaWriteMagicValue(void * pData,VkDeviceSize offset)3448 static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
3449 {
3450 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3451 uint32_t* pDst = (uint32_t*)((char*)pData + offset);
3452 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3453 for (size_t i = 0; i < numberCount; ++i, ++pDst)
3454 {
3455 *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
3456 }
3457 #else
3458 // no-op
3459 #endif
3460 }
3461
VmaValidateMagicValue(const void * pData,VkDeviceSize offset)3462 static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
3463 {
3464 #if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION
3465 const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
3466 const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
3467 for (size_t i = 0; i < numberCount; ++i, ++pSrc)
3468 {
3469 if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
3470 {
3471 return false;
3472 }
3473 }
3474 #endif
3475 return true;
3476 }
3477
3478 /*
3479 Fills structure with parameters of an example buffer to be used for transfers
3480 during GPU memory defragmentation.
3481 */
VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo & outBufCreateInfo)3482 static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)
3483 {
3484 memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));
3485 outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
3486 outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
3487 outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.
3488 }
3489
3490
3491 /*
3492 Performs binary search and returns iterator to first element that is greater or
3493 equal to (key), according to comparison (cmp).
3494
3495 Cmp should return true if first argument is less than second argument.
3496
3497 Returned value is the found element, if present in the collection or place where
3498 new element with value (key) should be inserted.
3499 */
3500 template <typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindFirstNotLess(IterT beg,IterT end,const KeyT & key,const CmpLess & cmp)3501 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
3502 {
3503 size_t down = 0, up = (end - beg);
3504 while (down < up)
3505 {
3506 const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation
3507 if (cmp(*(beg + mid), key))
3508 {
3509 down = mid + 1;
3510 }
3511 else
3512 {
3513 up = mid;
3514 }
3515 }
3516 return beg + down;
3517 }
3518
3519 template<typename CmpLess, typename IterT, typename KeyT>
VmaBinaryFindSorted(const IterT & beg,const IterT & end,const KeyT & value,const CmpLess & cmp)3520 IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
3521 {
3522 IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
3523 beg, end, value, cmp);
3524 if (it == end ||
3525 (!cmp(*it, value) && !cmp(value, *it)))
3526 {
3527 return it;
3528 }
3529 return end;
3530 }
3531
3532 /*
3533 Returns true if all pointers in the array are not-null and unique.
3534 Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
3535 T must be pointer type, e.g. VmaAllocation, VmaPool.
3536 */
3537 template<typename T>
VmaValidatePointerArray(uint32_t count,const T * arr)3538 static bool VmaValidatePointerArray(uint32_t count, const T* arr)
3539 {
3540 for (uint32_t i = 0; i < count; ++i)
3541 {
3542 const T iPtr = arr[i];
3543 if (iPtr == VMA_NULL)
3544 {
3545 return false;
3546 }
3547 for (uint32_t j = i + 1; j < count; ++j)
3548 {
3549 if (iPtr == arr[j])
3550 {
3551 return false;
3552 }
3553 }
3554 }
3555 return true;
3556 }
3557
3558 template<typename MainT, typename NewT>
VmaPnextChainPushFront(MainT * mainStruct,NewT * newStruct)3559 static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)
3560 {
3561 newStruct->pNext = mainStruct->pNext;
3562 mainStruct->pNext = newStruct;
3563 }
3564
3565 // This is the main algorithm that guides the selection of a memory type best for an allocation -
3566 // converts usage to required/preferred/not preferred flags.
FindMemoryPreferences(bool isIntegratedGPU,const VmaAllocationCreateInfo & allocCreateInfo,VkFlags bufImgUsage,VkMemoryPropertyFlags & outRequiredFlags,VkMemoryPropertyFlags & outPreferredFlags,VkMemoryPropertyFlags & outNotPreferredFlags)3567 static bool FindMemoryPreferences(
3568 bool isIntegratedGPU,
3569 const VmaAllocationCreateInfo& allocCreateInfo,
3570 VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown.
3571 VkMemoryPropertyFlags& outRequiredFlags,
3572 VkMemoryPropertyFlags& outPreferredFlags,
3573 VkMemoryPropertyFlags& outNotPreferredFlags)
3574 {
3575 outRequiredFlags = allocCreateInfo.requiredFlags;
3576 outPreferredFlags = allocCreateInfo.preferredFlags;
3577 outNotPreferredFlags = 0;
3578
3579 switch(allocCreateInfo.usage)
3580 {
3581 case VMA_MEMORY_USAGE_UNKNOWN:
3582 break;
3583 case VMA_MEMORY_USAGE_GPU_ONLY:
3584 if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
3585 {
3586 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3587 }
3588 break;
3589 case VMA_MEMORY_USAGE_CPU_ONLY:
3590 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
3591 break;
3592 case VMA_MEMORY_USAGE_CPU_TO_GPU:
3593 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3594 if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
3595 {
3596 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3597 }
3598 break;
3599 case VMA_MEMORY_USAGE_GPU_TO_CPU:
3600 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3601 outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3602 break;
3603 case VMA_MEMORY_USAGE_CPU_COPY:
3604 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3605 break;
3606 case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:
3607 outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
3608 break;
3609 case VMA_MEMORY_USAGE_AUTO:
3610 case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:
3611 case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:
3612 {
3613 if(bufImgUsage == UINT32_MAX)
3614 {
3615 VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known.");
3616 return false;
3617 }
3618 // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same VK_BUFFER_IMAGE_TRANSFER*.
3619 const bool deviceAccess = (bufImgUsage & ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0;
3620 const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0;
3621 const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0;
3622 const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0;
3623 const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
3624 const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
3625
3626 // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU.
3627 if(hostAccessRandom)
3628 {
3629 if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
3630 {
3631 // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL.
3632 // Omitting HOST_VISIBLE here is intentional.
3633 // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one.
3634 // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list.
3635 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3636 }
3637 else
3638 {
3639 // Always CPU memory, cached.
3640 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3641 }
3642 }
3643 // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined.
3644 else if(hostAccessSequentialWrite)
3645 {
3646 // Want uncached and write-combined.
3647 outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
3648
3649 if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)
3650 {
3651 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3652 }
3653 else
3654 {
3655 outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
3656 // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame)
3657 if(deviceAccess)
3658 {
3659 // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory.
3660 if(preferHost)
3661 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3662 else
3663 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3664 }
3665 // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU)
3666 else
3667 {
3668 // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory.
3669 if(preferDevice)
3670 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3671 else
3672 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3673 }
3674 }
3675 }
3676 // No CPU access
3677 else
3678 {
3679 // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory
3680 if(deviceAccess)
3681 {
3682 // ...unless there is a clear preference from the user not to do so.
3683 if(preferHost)
3684 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3685 else
3686 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3687 }
3688 // No direct GPU access, no CPU access, just transfers.
3689 // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or
3690 // a "swap file" copy to free some GPU memory (then better CPU memory).
3691 // Up to the user to decide. If no preferece, assume the former and choose GPU memory.
3692 if(preferHost)
3693 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3694 else
3695 outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
3696 }
3697 break;
3698 }
3699 default:
3700 VMA_ASSERT(0);
3701 }
3702
3703 // Avoid DEVICE_COHERENT unless explicitly requested.
3704 if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) &
3705 (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)
3706 {
3707 outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY;
3708 }
3709
3710 return true;
3711 }
3712
3713 ////////////////////////////////////////////////////////////////////////////////
3714 // Memory allocation
3715
VmaMalloc(const VkAllocationCallbacks * pAllocationCallbacks,size_t size,size_t alignment)3716 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
3717 {
3718 void* result = VMA_NULL;
3719 if ((pAllocationCallbacks != VMA_NULL) &&
3720 (pAllocationCallbacks->pfnAllocation != VMA_NULL))
3721 {
3722 result = (*pAllocationCallbacks->pfnAllocation)(
3723 pAllocationCallbacks->pUserData,
3724 size,
3725 alignment,
3726 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
3727 }
3728 else
3729 {
3730 result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
3731 }
3732 VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");
3733 return result;
3734 }
3735
VmaFree(const VkAllocationCallbacks * pAllocationCallbacks,void * ptr)3736 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
3737 {
3738 if ((pAllocationCallbacks != VMA_NULL) &&
3739 (pAllocationCallbacks->pfnFree != VMA_NULL))
3740 {
3741 (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
3742 }
3743 else
3744 {
3745 VMA_SYSTEM_ALIGNED_FREE(ptr);
3746 }
3747 }
3748
3749 template<typename T>
VmaAllocate(const VkAllocationCallbacks * pAllocationCallbacks)3750 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
3751 {
3752 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
3753 }
3754
3755 template<typename T>
VmaAllocateArray(const VkAllocationCallbacks * pAllocationCallbacks,size_t count)3756 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
3757 {
3758 return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
3759 }
3760
3761 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
3762
3763 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
3764
3765 template<typename T>
vma_delete(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr)3766 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
3767 {
3768 ptr->~T();
3769 VmaFree(pAllocationCallbacks, ptr);
3770 }
3771
3772 template<typename T>
vma_delete_array(const VkAllocationCallbacks * pAllocationCallbacks,T * ptr,size_t count)3773 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
3774 {
3775 if (ptr != VMA_NULL)
3776 {
3777 for (size_t i = count; i--; )
3778 {
3779 ptr[i].~T();
3780 }
3781 VmaFree(pAllocationCallbacks, ptr);
3782 }
3783 }
3784
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr)3785 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)
3786 {
3787 if (srcStr != VMA_NULL)
3788 {
3789 const size_t len = strlen(srcStr);
3790 char* const result = vma_new_array(allocs, char, len + 1);
3791 memcpy(result, srcStr, len + 1);
3792 return result;
3793 }
3794 return VMA_NULL;
3795 }
3796
3797 #if VMA_STATS_STRING_ENABLED
VmaCreateStringCopy(const VkAllocationCallbacks * allocs,const char * srcStr,size_t strLen)3798 static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)
3799 {
3800 if (srcStr != VMA_NULL)
3801 {
3802 char* const result = vma_new_array(allocs, char, strLen + 1);
3803 memcpy(result, srcStr, strLen);
3804 result[strLen] = '\0';
3805 return result;
3806 }
3807 return VMA_NULL;
3808 }
3809 #endif // VMA_STATS_STRING_ENABLED
3810
VmaFreeString(const VkAllocationCallbacks * allocs,char * str)3811 static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)
3812 {
3813 if (str != VMA_NULL)
3814 {
3815 const size_t len = strlen(str);
3816 vma_delete_array(allocs, str, len + 1);
3817 }
3818 }
3819
3820 template<typename CmpLess, typename VectorT>
VmaVectorInsertSorted(VectorT & vector,const typename VectorT::value_type & value)3821 size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
3822 {
3823 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
3824 vector.data(),
3825 vector.data() + vector.size(),
3826 value,
3827 CmpLess()) - vector.data();
3828 VmaVectorInsert(vector, indexToInsert, value);
3829 return indexToInsert;
3830 }
3831
3832 template<typename CmpLess, typename VectorT>
VmaVectorRemoveSorted(VectorT & vector,const typename VectorT::value_type & value)3833 bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
3834 {
3835 CmpLess comparator;
3836 typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
3837 vector.begin(),
3838 vector.end(),
3839 value,
3840 comparator);
3841 if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
3842 {
3843 size_t indexToRemove = it - vector.begin();
3844 VmaVectorRemove(vector, indexToRemove);
3845 return true;
3846 }
3847 return false;
3848 }
3849 #endif // _VMA_FUNCTIONS
3850
3851 #ifndef _VMA_STATISTICS_FUNCTIONS
3852
VmaClearStatistics(VmaStatistics & outStats)3853 static void VmaClearStatistics(VmaStatistics& outStats)
3854 {
3855 outStats.blockCount = 0;
3856 outStats.allocationCount = 0;
3857 outStats.blockBytes = 0;
3858 outStats.allocationBytes = 0;
3859 }
3860
VmaAddStatistics(VmaStatistics & inoutStats,const VmaStatistics & src)3861 static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src)
3862 {
3863 inoutStats.blockCount += src.blockCount;
3864 inoutStats.allocationCount += src.allocationCount;
3865 inoutStats.blockBytes += src.blockBytes;
3866 inoutStats.allocationBytes += src.allocationBytes;
3867 }
3868
VmaClearDetailedStatistics(VmaDetailedStatistics & outStats)3869 static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats)
3870 {
3871 VmaClearStatistics(outStats.statistics);
3872 outStats.unusedRangeCount = 0;
3873 outStats.allocationSizeMin = VK_WHOLE_SIZE;
3874 outStats.allocationSizeMax = 0;
3875 outStats.unusedRangeSizeMin = VK_WHOLE_SIZE;
3876 outStats.unusedRangeSizeMax = 0;
3877 }
3878
VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics & inoutStats,VkDeviceSize size)3879 static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
3880 {
3881 inoutStats.statistics.allocationCount++;
3882 inoutStats.statistics.allocationBytes += size;
3883 inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size);
3884 inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size);
3885 }
3886
VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics & inoutStats,VkDeviceSize size)3887 static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size)
3888 {
3889 inoutStats.unusedRangeCount++;
3890 inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size);
3891 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size);
3892 }
3893
VmaAddDetailedStatistics(VmaDetailedStatistics & inoutStats,const VmaDetailedStatistics & src)3894 static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src)
3895 {
3896 VmaAddStatistics(inoutStats.statistics, src.statistics);
3897 inoutStats.unusedRangeCount += src.unusedRangeCount;
3898 inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin);
3899 inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax);
3900 inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin);
3901 inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax);
3902 }
3903
3904 #endif // _VMA_STATISTICS_FUNCTIONS
3905
3906 #ifndef _VMA_MUTEX_LOCK
3907 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
3908 struct VmaMutexLock
3909 {
VMA_CLASS_NO_COPYVmaMutexLock3910 VMA_CLASS_NO_COPY(VmaMutexLock)
3911 public:
3912 VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :
3913 m_pMutex(useMutex ? &mutex : VMA_NULL)
3914 {
3915 if (m_pMutex) { m_pMutex->Lock(); }
3916 }
~VmaMutexLockVmaMutexLock3917 ~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } }
3918
3919 private:
3920 VMA_MUTEX* m_pMutex;
3921 };
3922
3923 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
3924 struct VmaMutexLockRead
3925 {
VMA_CLASS_NO_COPYVmaMutexLockRead3926 VMA_CLASS_NO_COPY(VmaMutexLockRead)
3927 public:
3928 VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
3929 m_pMutex(useMutex ? &mutex : VMA_NULL)
3930 {
3931 if (m_pMutex) { m_pMutex->LockRead(); }
3932 }
~VmaMutexLockReadVmaMutexLockRead3933 ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }
3934
3935 private:
3936 VMA_RW_MUTEX* m_pMutex;
3937 };
3938
3939 // Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
3940 struct VmaMutexLockWrite
3941 {
VMA_CLASS_NO_COPYVmaMutexLockWrite3942 VMA_CLASS_NO_COPY(VmaMutexLockWrite)
3943 public:
3944 VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex)
3945 : m_pMutex(useMutex ? &mutex : VMA_NULL)
3946 {
3947 if (m_pMutex) { m_pMutex->LockWrite(); }
3948 }
~VmaMutexLockWriteVmaMutexLockWrite3949 ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }
3950
3951 private:
3952 VMA_RW_MUTEX* m_pMutex;
3953 };
3954
3955 #if VMA_DEBUG_GLOBAL_MUTEX
3956 static VMA_MUTEX gDebugGlobalMutex;
3957 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
3958 #else
3959 #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
3960 #endif
3961 #endif // _VMA_MUTEX_LOCK
3962
3963 #ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
3964 // An object that increments given atomic but decrements it back in the destructor unless Commit() is called.
3965 template<typename T>
3966 struct AtomicTransactionalIncrement
3967 {
3968 public:
3969 typedef std::atomic<T> AtomicT;
3970
~AtomicTransactionalIncrementAtomicTransactionalIncrement3971 ~AtomicTransactionalIncrement()
3972 {
3973 if(m_Atomic)
3974 --(*m_Atomic);
3975 }
3976
CommitAtomicTransactionalIncrement3977 void Commit() { m_Atomic = nullptr; }
IncrementAtomicTransactionalIncrement3978 T Increment(AtomicT* atomic)
3979 {
3980 m_Atomic = atomic;
3981 return m_Atomic->fetch_add(1);
3982 }
3983
3984 private:
3985 AtomicT* m_Atomic = nullptr;
3986 };
3987 #endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
3988
3989 #ifndef _VMA_STL_ALLOCATOR
3990 // STL-compatible allocator.
3991 template<typename T>
3992 struct VmaStlAllocator
3993 {
3994 const VkAllocationCallbacks* const m_pCallbacks;
3995 typedef T value_type;
3996
VmaStlAllocatorVmaStlAllocator3997 VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {}
3998 template<typename U>
VmaStlAllocatorVmaStlAllocator3999 VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) {}
4000 VmaStlAllocator(const VmaStlAllocator&) = default;
4001 VmaStlAllocator& operator=(const VmaStlAllocator&) = delete;
4002
allocateVmaStlAllocator4003 T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
deallocateVmaStlAllocator4004 void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
4005
4006 template<typename U>
4007 bool operator==(const VmaStlAllocator<U>& rhs) const
4008 {
4009 return m_pCallbacks == rhs.m_pCallbacks;
4010 }
4011 template<typename U>
4012 bool operator!=(const VmaStlAllocator<U>& rhs) const
4013 {
4014 return m_pCallbacks != rhs.m_pCallbacks;
4015 }
4016 };
4017 #endif // _VMA_STL_ALLOCATOR
4018
4019 #ifndef _VMA_VECTOR
4020 /* Class with interface compatible with subset of std::vector.
4021 T must be POD because constructors and destructors are not called and memcpy is
4022 used for these objects. */
4023 template<typename T, typename AllocatorT>
4024 class VmaVector
4025 {
4026 public:
4027 typedef T value_type;
4028 typedef T* iterator;
4029 typedef const T* const_iterator;
4030
4031 VmaVector(const AllocatorT& allocator);
4032 VmaVector(size_t count, const AllocatorT& allocator);
4033 // This version of the constructor is here for compatibility with pre-C++14 std::vector.
4034 // value is unused.
VmaVector(size_t count,const T & value,const AllocatorT & allocator)4035 VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {}
4036 VmaVector(const VmaVector<T, AllocatorT>& src);
4037 VmaVector& operator=(const VmaVector& rhs);
~VmaVector()4038 ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); }
4039
empty()4040 bool empty() const { return m_Count == 0; }
size()4041 size_t size() const { return m_Count; }
data()4042 T* data() { return m_pArray; }
front()4043 T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
back()4044 T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
data()4045 const T* data() const { return m_pArray; }
front()4046 const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }
back()4047 const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }
4048
begin()4049 iterator begin() { return m_pArray; }
end()4050 iterator end() { return m_pArray + m_Count; }
cbegin()4051 const_iterator cbegin() const { return m_pArray; }
cend()4052 const_iterator cend() const { return m_pArray + m_Count; }
begin()4053 const_iterator begin() const { return cbegin(); }
end()4054 const_iterator end() const { return cend(); }
4055
pop_front()4056 void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
pop_back()4057 void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
push_front(const T & src)4058 void push_front(const T& src) { insert(0, src); }
4059
4060 void push_back(const T& src);
4061 void reserve(size_t newCapacity, bool freeMemory = false);
4062 void resize(size_t newCount);
clear()4063 void clear() { resize(0); }
4064 void shrink_to_fit();
4065 void insert(size_t index, const T& src);
4066 void remove(size_t index);
4067
4068 T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
4069 const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }
4070
4071 private:
4072 AllocatorT m_Allocator;
4073 T* m_pArray;
4074 size_t m_Count;
4075 size_t m_Capacity;
4076 };
4077
4078 #ifndef _VMA_VECTOR_FUNCTIONS
4079 template<typename T, typename AllocatorT>
VmaVector(const AllocatorT & allocator)4080 VmaVector<T, AllocatorT>::VmaVector(const AllocatorT& allocator)
4081 : m_Allocator(allocator),
4082 m_pArray(VMA_NULL),
4083 m_Count(0),
4084 m_Capacity(0) {}
4085
4086 template<typename T, typename AllocatorT>
VmaVector(size_t count,const AllocatorT & allocator)4087 VmaVector<T, AllocatorT>::VmaVector(size_t count, const AllocatorT& allocator)
4088 : m_Allocator(allocator),
4089 m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
4090 m_Count(count),
4091 m_Capacity(count) {}
4092
4093 template<typename T, typename AllocatorT>
VmaVector(const VmaVector & src)4094 VmaVector<T, AllocatorT>::VmaVector(const VmaVector& src)
4095 : m_Allocator(src.m_Allocator),
4096 m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
4097 m_Count(src.m_Count),
4098 m_Capacity(src.m_Count)
4099 {
4100 if (m_Count != 0)
4101 {
4102 memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
4103 }
4104 }
4105
4106 template<typename T, typename AllocatorT>
4107 VmaVector<T, AllocatorT>& VmaVector<T, AllocatorT>::operator=(const VmaVector& rhs)
4108 {
4109 if (&rhs != this)
4110 {
4111 resize(rhs.m_Count);
4112 if (m_Count != 0)
4113 {
4114 memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
4115 }
4116 }
4117 return *this;
4118 }
4119
4120 template<typename T, typename AllocatorT>
push_back(const T & src)4121 void VmaVector<T, AllocatorT>::push_back(const T& src)
4122 {
4123 const size_t newIndex = size();
4124 resize(newIndex + 1);
4125 m_pArray[newIndex] = src;
4126 }
4127
4128 template<typename T, typename AllocatorT>
reserve(size_t newCapacity,bool freeMemory)4129 void VmaVector<T, AllocatorT>::reserve(size_t newCapacity, bool freeMemory)
4130 {
4131 newCapacity = VMA_MAX(newCapacity, m_Count);
4132
4133 if ((newCapacity < m_Capacity) && !freeMemory)
4134 {
4135 newCapacity = m_Capacity;
4136 }
4137
4138 if (newCapacity != m_Capacity)
4139 {
4140 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
4141 if (m_Count != 0)
4142 {
4143 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4144 }
4145 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4146 m_Capacity = newCapacity;
4147 m_pArray = newArray;
4148 }
4149 }
4150
4151 template<typename T, typename AllocatorT>
resize(size_t newCount)4152 void VmaVector<T, AllocatorT>::resize(size_t newCount)
4153 {
4154 size_t newCapacity = m_Capacity;
4155 if (newCount > m_Capacity)
4156 {
4157 newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
4158 }
4159
4160 if (newCapacity != m_Capacity)
4161 {
4162 T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
4163 const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
4164 if (elementsToCopy != 0)
4165 {
4166 memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
4167 }
4168 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4169 m_Capacity = newCapacity;
4170 m_pArray = newArray;
4171 }
4172
4173 m_Count = newCount;
4174 }
4175
4176 template<typename T, typename AllocatorT>
shrink_to_fit()4177 void VmaVector<T, AllocatorT>::shrink_to_fit()
4178 {
4179 if (m_Capacity > m_Count)
4180 {
4181 T* newArray = VMA_NULL;
4182 if (m_Count > 0)
4183 {
4184 newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);
4185 memcpy(newArray, m_pArray, m_Count * sizeof(T));
4186 }
4187 VmaFree(m_Allocator.m_pCallbacks, m_pArray);
4188 m_Capacity = m_Count;
4189 m_pArray = newArray;
4190 }
4191 }
4192
4193 template<typename T, typename AllocatorT>
insert(size_t index,const T & src)4194 void VmaVector<T, AllocatorT>::insert(size_t index, const T& src)
4195 {
4196 VMA_HEAVY_ASSERT(index <= m_Count);
4197 const size_t oldCount = size();
4198 resize(oldCount + 1);
4199 if (index < oldCount)
4200 {
4201 memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
4202 }
4203 m_pArray[index] = src;
4204 }
4205
4206 template<typename T, typename AllocatorT>
remove(size_t index)4207 void VmaVector<T, AllocatorT>::remove(size_t index)
4208 {
4209 VMA_HEAVY_ASSERT(index < m_Count);
4210 const size_t oldCount = size();
4211 if (index < oldCount - 1)
4212 {
4213 memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
4214 }
4215 resize(oldCount - 1);
4216 }
4217 #endif // _VMA_VECTOR_FUNCTIONS
4218
4219 template<typename T, typename allocatorT>
VmaVectorInsert(VmaVector<T,allocatorT> & vec,size_t index,const T & item)4220 static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
4221 {
4222 vec.insert(index, item);
4223 }
4224
4225 template<typename T, typename allocatorT>
VmaVectorRemove(VmaVector<T,allocatorT> & vec,size_t index)4226 static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
4227 {
4228 vec.remove(index);
4229 }
4230 #endif // _VMA_VECTOR
4231
4232 #ifndef _VMA_SMALL_VECTOR
4233 /*
4234 This is a vector (a variable-sized array), optimized for the case when the array is small.
4235
4236 It contains some number of elements in-place, which allows it to avoid heap allocation
4237 when the actual number of elements is below that threshold. This allows normal "small"
4238 cases to be fast without losing generality for large inputs.
4239 */
4240 template<typename T, typename AllocatorT, size_t N>
4241 class VmaSmallVector
4242 {
4243 public:
4244 typedef T value_type;
4245 typedef T* iterator;
4246
4247 VmaSmallVector(const AllocatorT& allocator);
4248 VmaSmallVector(size_t count, const AllocatorT& allocator);
4249 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
4250 VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
4251 template<typename SrcT, typename SrcAllocatorT, size_t SrcN>
4252 VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;
4253 ~VmaSmallVector() = default;
4254
empty()4255 bool empty() const { return m_Count == 0; }
size()4256 size_t size() const { return m_Count; }
data()4257 T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
front()4258 T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
back()4259 T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
data()4260 const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }
front()4261 const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }
back()4262 const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }
4263
begin()4264 iterator begin() { return data(); }
end()4265 iterator end() { return data() + m_Count; }
4266
pop_front()4267 void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }
pop_back()4268 void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }
push_front(const T & src)4269 void push_front(const T& src) { insert(0, src); }
4270
4271 void push_back(const T& src);
4272 void resize(size_t newCount, bool freeMemory = false);
4273 void clear(bool freeMemory = false);
4274 void insert(size_t index, const T& src);
4275 void remove(size_t index);
4276
4277 T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
4278 const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }
4279
4280 private:
4281 size_t m_Count;
4282 T m_StaticArray[N]; // Used when m_Size <= N
4283 VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N
4284 };
4285
4286 #ifndef _VMA_SMALL_VECTOR_FUNCTIONS
4287 template<typename T, typename AllocatorT, size_t N>
VmaSmallVector(const AllocatorT & allocator)4288 VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(const AllocatorT& allocator)
4289 : m_Count(0),
4290 m_DynamicArray(allocator) {}
4291
4292 template<typename T, typename AllocatorT, size_t N>
VmaSmallVector(size_t count,const AllocatorT & allocator)4293 VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(size_t count, const AllocatorT& allocator)
4294 : m_Count(count),
4295 m_DynamicArray(count > N ? count : 0, allocator) {}
4296
4297 template<typename T, typename AllocatorT, size_t N>
push_back(const T & src)4298 void VmaSmallVector<T, AllocatorT, N>::push_back(const T& src)
4299 {
4300 const size_t newIndex = size();
4301 resize(newIndex + 1);
4302 data()[newIndex] = src;
4303 }
4304
4305 template<typename T, typename AllocatorT, size_t N>
resize(size_t newCount,bool freeMemory)4306 void VmaSmallVector<T, AllocatorT, N>::resize(size_t newCount, bool freeMemory)
4307 {
4308 if (newCount > N && m_Count > N)
4309 {
4310 // Any direction, staying in m_DynamicArray
4311 m_DynamicArray.resize(newCount);
4312 if (freeMemory)
4313 {
4314 m_DynamicArray.shrink_to_fit();
4315 }
4316 }
4317 else if (newCount > N && m_Count <= N)
4318 {
4319 // Growing, moving from m_StaticArray to m_DynamicArray
4320 m_DynamicArray.resize(newCount);
4321 if (m_Count > 0)
4322 {
4323 memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));
4324 }
4325 }
4326 else if (newCount <= N && m_Count > N)
4327 {
4328 // Shrinking, moving from m_DynamicArray to m_StaticArray
4329 if (newCount > 0)
4330 {
4331 memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));
4332 }
4333 m_DynamicArray.resize(0);
4334 if (freeMemory)
4335 {
4336 m_DynamicArray.shrink_to_fit();
4337 }
4338 }
4339 else
4340 {
4341 // Any direction, staying in m_StaticArray - nothing to do here
4342 }
4343 m_Count = newCount;
4344 }
4345
4346 template<typename T, typename AllocatorT, size_t N>
clear(bool freeMemory)4347 void VmaSmallVector<T, AllocatorT, N>::clear(bool freeMemory)
4348 {
4349 m_DynamicArray.clear();
4350 if (freeMemory)
4351 {
4352 m_DynamicArray.shrink_to_fit();
4353 }
4354 m_Count = 0;
4355 }
4356
4357 template<typename T, typename AllocatorT, size_t N>
insert(size_t index,const T & src)4358 void VmaSmallVector<T, AllocatorT, N>::insert(size_t index, const T& src)
4359 {
4360 VMA_HEAVY_ASSERT(index <= m_Count);
4361 const size_t oldCount = size();
4362 resize(oldCount + 1);
4363 T* const dataPtr = data();
4364 if (index < oldCount)
4365 {
4366 // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.
4367 memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));
4368 }
4369 dataPtr[index] = src;
4370 }
4371
4372 template<typename T, typename AllocatorT, size_t N>
remove(size_t index)4373 void VmaSmallVector<T, AllocatorT, N>::remove(size_t index)
4374 {
4375 VMA_HEAVY_ASSERT(index < m_Count);
4376 const size_t oldCount = size();
4377 if (index < oldCount - 1)
4378 {
4379 // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.
4380 T* const dataPtr = data();
4381 memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));
4382 }
4383 resize(oldCount - 1);
4384 }
4385 #endif // _VMA_SMALL_VECTOR_FUNCTIONS
4386 #endif // _VMA_SMALL_VECTOR
4387
4388 #ifndef _VMA_POOL_ALLOCATOR
4389 /*
4390 Allocator for objects of type T using a list of arrays (pools) to speed up
4391 allocation. Number of elements that can be allocated is not bounded because
4392 allocator can create multiple blocks.
4393 */
4394 template<typename T>
4395 class VmaPoolAllocator
4396 {
4397 VMA_CLASS_NO_COPY(VmaPoolAllocator)
4398 public:
4399 VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);
4400 ~VmaPoolAllocator();
4401 template<typename... Types> T* Alloc(Types&&... args);
4402 void Free(T* ptr);
4403
4404 private:
4405 union Item
4406 {
4407 uint32_t NextFreeIndex;
4408 alignas(T) char Value[sizeof(T)];
4409 };
4410 struct ItemBlock
4411 {
4412 Item* pItems;
4413 uint32_t Capacity;
4414 uint32_t FirstFreeIndex;
4415 };
4416
4417 const VkAllocationCallbacks* m_pAllocationCallbacks;
4418 const uint32_t m_FirstBlockCapacity;
4419 VmaVector<ItemBlock, VmaStlAllocator<ItemBlock>> m_ItemBlocks;
4420
4421 ItemBlock& CreateNewBlock();
4422 };
4423
4424 #ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS
4425 template<typename T>
VmaPoolAllocator(const VkAllocationCallbacks * pAllocationCallbacks,uint32_t firstBlockCapacity)4426 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity)
4427 : m_pAllocationCallbacks(pAllocationCallbacks),
4428 m_FirstBlockCapacity(firstBlockCapacity),
4429 m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
4430 {
4431 VMA_ASSERT(m_FirstBlockCapacity > 1);
4432 }
4433
4434 template<typename T>
~VmaPoolAllocator()4435 VmaPoolAllocator<T>::~VmaPoolAllocator()
4436 {
4437 for (size_t i = m_ItemBlocks.size(); i--;)
4438 vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
4439 m_ItemBlocks.clear();
4440 }
4441
4442 template<typename T>
Alloc(Types &&...args)4443 template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)
4444 {
4445 for (size_t i = m_ItemBlocks.size(); i--; )
4446 {
4447 ItemBlock& block = m_ItemBlocks[i];
4448 // This block has some free items: Use first one.
4449 if (block.FirstFreeIndex != UINT32_MAX)
4450 {
4451 Item* const pItem = &block.pItems[block.FirstFreeIndex];
4452 block.FirstFreeIndex = pItem->NextFreeIndex;
4453 T* result = (T*)&pItem->Value;
4454 new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
4455 return result;
4456 }
4457 }
4458
4459 // No block has free item: Create new one and use it.
4460 ItemBlock& newBlock = CreateNewBlock();
4461 Item* const pItem = &newBlock.pItems[0];
4462 newBlock.FirstFreeIndex = pItem->NextFreeIndex;
4463 T* result = (T*)&pItem->Value;
4464 new(result) T(std::forward<Types>(args)...); // Explicit constructor call.
4465 return result;
4466 }
4467
4468 template<typename T>
Free(T * ptr)4469 void VmaPoolAllocator<T>::Free(T* ptr)
4470 {
4471 // Search all memory blocks to find ptr.
4472 for (size_t i = m_ItemBlocks.size(); i--; )
4473 {
4474 ItemBlock& block = m_ItemBlocks[i];
4475
4476 // Casting to union.
4477 Item* pItemPtr;
4478 memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
4479
4480 // Check if pItemPtr is in address range of this block.
4481 if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
4482 {
4483 ptr->~T(); // Explicit destructor call.
4484 const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
4485 pItemPtr->NextFreeIndex = block.FirstFreeIndex;
4486 block.FirstFreeIndex = index;
4487 return;
4488 }
4489 }
4490 VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
4491 }
4492
4493 template<typename T>
CreateNewBlock()4494 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
4495 {
4496 const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?
4497 m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
4498
4499 const ItemBlock newBlock =
4500 {
4501 vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),
4502 newBlockCapacity,
4503 0
4504 };
4505
4506 m_ItemBlocks.push_back(newBlock);
4507
4508 // Setup singly-linked list of all free items in this block.
4509 for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)
4510 newBlock.pItems[i].NextFreeIndex = i + 1;
4511 newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
4512 return m_ItemBlocks.back();
4513 }
4514 #endif // _VMA_POOL_ALLOCATOR_FUNCTIONS
4515 #endif // _VMA_POOL_ALLOCATOR
4516
4517 #ifndef _VMA_RAW_LIST
4518 template<typename T>
4519 struct VmaListItem
4520 {
4521 VmaListItem* pPrev;
4522 VmaListItem* pNext;
4523 T Value;
4524 };
4525
4526 // Doubly linked list.
4527 template<typename T>
4528 class VmaRawList
4529 {
4530 VMA_CLASS_NO_COPY(VmaRawList)
4531 public:
4532 typedef VmaListItem<T> ItemType;
4533
4534 VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
4535 // Intentionally not calling Clear, because that would be unnecessary
4536 // computations to return all items to m_ItemAllocator as free.
4537 ~VmaRawList() = default;
4538
GetCount()4539 size_t GetCount() const { return m_Count; }
IsEmpty()4540 bool IsEmpty() const { return m_Count == 0; }
4541
Front()4542 ItemType* Front() { return m_pFront; }
Back()4543 ItemType* Back() { return m_pBack; }
Front()4544 const ItemType* Front() const { return m_pFront; }
Back()4545 const ItemType* Back() const { return m_pBack; }
4546
4547 ItemType* PushFront();
4548 ItemType* PushBack();
4549 ItemType* PushFront(const T& value);
4550 ItemType* PushBack(const T& value);
4551 void PopFront();
4552 void PopBack();
4553
4554 // Item can be null - it means PushBack.
4555 ItemType* InsertBefore(ItemType* pItem);
4556 // Item can be null - it means PushFront.
4557 ItemType* InsertAfter(ItemType* pItem);
4558 ItemType* InsertBefore(ItemType* pItem, const T& value);
4559 ItemType* InsertAfter(ItemType* pItem, const T& value);
4560
4561 void Clear();
4562 void Remove(ItemType* pItem);
4563
4564 private:
4565 const VkAllocationCallbacks* const m_pAllocationCallbacks;
4566 VmaPoolAllocator<ItemType> m_ItemAllocator;
4567 ItemType* m_pFront;
4568 ItemType* m_pBack;
4569 size_t m_Count;
4570 };
4571
4572 #ifndef _VMA_RAW_LIST_FUNCTIONS
4573 template<typename T>
VmaRawList(const VkAllocationCallbacks * pAllocationCallbacks)4574 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks)
4575 : m_pAllocationCallbacks(pAllocationCallbacks),
4576 m_ItemAllocator(pAllocationCallbacks, 128),
4577 m_pFront(VMA_NULL),
4578 m_pBack(VMA_NULL),
4579 m_Count(0) {}
4580
4581 template<typename T>
PushFront()4582 VmaListItem<T>* VmaRawList<T>::PushFront()
4583 {
4584 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4585 pNewItem->pPrev = VMA_NULL;
4586 if (IsEmpty())
4587 {
4588 pNewItem->pNext = VMA_NULL;
4589 m_pFront = pNewItem;
4590 m_pBack = pNewItem;
4591 m_Count = 1;
4592 }
4593 else
4594 {
4595 pNewItem->pNext = m_pFront;
4596 m_pFront->pPrev = pNewItem;
4597 m_pFront = pNewItem;
4598 ++m_Count;
4599 }
4600 return pNewItem;
4601 }
4602
4603 template<typename T>
PushBack()4604 VmaListItem<T>* VmaRawList<T>::PushBack()
4605 {
4606 ItemType* const pNewItem = m_ItemAllocator.Alloc();
4607 pNewItem->pNext = VMA_NULL;
4608 if(IsEmpty())
4609 {
4610 pNewItem->pPrev = VMA_NULL;
4611 m_pFront = pNewItem;
4612 m_pBack = pNewItem;
4613 m_Count = 1;
4614 }
4615 else
4616 {
4617 pNewItem->pPrev = m_pBack;
4618 m_pBack->pNext = pNewItem;
4619 m_pBack = pNewItem;
4620 ++m_Count;
4621 }
4622 return pNewItem;
4623 }
4624
4625 template<typename T>
PushFront(const T & value)4626 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
4627 {
4628 ItemType* const pNewItem = PushFront();
4629 pNewItem->Value = value;
4630 return pNewItem;
4631 }
4632
4633 template<typename T>
PushBack(const T & value)4634 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
4635 {
4636 ItemType* const pNewItem = PushBack();
4637 pNewItem->Value = value;
4638 return pNewItem;
4639 }
4640
4641 template<typename T>
PopFront()4642 void VmaRawList<T>::PopFront()
4643 {
4644 VMA_HEAVY_ASSERT(m_Count > 0);
4645 ItemType* const pFrontItem = m_pFront;
4646 ItemType* const pNextItem = pFrontItem->pNext;
4647 if (pNextItem != VMA_NULL)
4648 {
4649 pNextItem->pPrev = VMA_NULL;
4650 }
4651 m_pFront = pNextItem;
4652 m_ItemAllocator.Free(pFrontItem);
4653 --m_Count;
4654 }
4655
4656 template<typename T>
PopBack()4657 void VmaRawList<T>::PopBack()
4658 {
4659 VMA_HEAVY_ASSERT(m_Count > 0);
4660 ItemType* const pBackItem = m_pBack;
4661 ItemType* const pPrevItem = pBackItem->pPrev;
4662 if(pPrevItem != VMA_NULL)
4663 {
4664 pPrevItem->pNext = VMA_NULL;
4665 }
4666 m_pBack = pPrevItem;
4667 m_ItemAllocator.Free(pBackItem);
4668 --m_Count;
4669 }
4670
4671 template<typename T>
Clear()4672 void VmaRawList<T>::Clear()
4673 {
4674 if (IsEmpty() == false)
4675 {
4676 ItemType* pItem = m_pBack;
4677 while (pItem != VMA_NULL)
4678 {
4679 ItemType* const pPrevItem = pItem->pPrev;
4680 m_ItemAllocator.Free(pItem);
4681 pItem = pPrevItem;
4682 }
4683 m_pFront = VMA_NULL;
4684 m_pBack = VMA_NULL;
4685 m_Count = 0;
4686 }
4687 }
4688
4689 template<typename T>
Remove(ItemType * pItem)4690 void VmaRawList<T>::Remove(ItemType* pItem)
4691 {
4692 VMA_HEAVY_ASSERT(pItem != VMA_NULL);
4693 VMA_HEAVY_ASSERT(m_Count > 0);
4694
4695 if(pItem->pPrev != VMA_NULL)
4696 {
4697 pItem->pPrev->pNext = pItem->pNext;
4698 }
4699 else
4700 {
4701 VMA_HEAVY_ASSERT(m_pFront == pItem);
4702 m_pFront = pItem->pNext;
4703 }
4704
4705 if(pItem->pNext != VMA_NULL)
4706 {
4707 pItem->pNext->pPrev = pItem->pPrev;
4708 }
4709 else
4710 {
4711 VMA_HEAVY_ASSERT(m_pBack == pItem);
4712 m_pBack = pItem->pPrev;
4713 }
4714
4715 m_ItemAllocator.Free(pItem);
4716 --m_Count;
4717 }
4718
4719 template<typename T>
InsertBefore(ItemType * pItem)4720 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
4721 {
4722 if(pItem != VMA_NULL)
4723 {
4724 ItemType* const prevItem = pItem->pPrev;
4725 ItemType* const newItem = m_ItemAllocator.Alloc();
4726 newItem->pPrev = prevItem;
4727 newItem->pNext = pItem;
4728 pItem->pPrev = newItem;
4729 if(prevItem != VMA_NULL)
4730 {
4731 prevItem->pNext = newItem;
4732 }
4733 else
4734 {
4735 VMA_HEAVY_ASSERT(m_pFront == pItem);
4736 m_pFront = newItem;
4737 }
4738 ++m_Count;
4739 return newItem;
4740 }
4741 else
4742 return PushBack();
4743 }
4744
4745 template<typename T>
InsertAfter(ItemType * pItem)4746 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
4747 {
4748 if(pItem != VMA_NULL)
4749 {
4750 ItemType* const nextItem = pItem->pNext;
4751 ItemType* const newItem = m_ItemAllocator.Alloc();
4752 newItem->pNext = nextItem;
4753 newItem->pPrev = pItem;
4754 pItem->pNext = newItem;
4755 if(nextItem != VMA_NULL)
4756 {
4757 nextItem->pPrev = newItem;
4758 }
4759 else
4760 {
4761 VMA_HEAVY_ASSERT(m_pBack == pItem);
4762 m_pBack = newItem;
4763 }
4764 ++m_Count;
4765 return newItem;
4766 }
4767 else
4768 return PushFront();
4769 }
4770
4771 template<typename T>
InsertBefore(ItemType * pItem,const T & value)4772 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
4773 {
4774 ItemType* const newItem = InsertBefore(pItem);
4775 newItem->Value = value;
4776 return newItem;
4777 }
4778
4779 template<typename T>
InsertAfter(ItemType * pItem,const T & value)4780 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
4781 {
4782 ItemType* const newItem = InsertAfter(pItem);
4783 newItem->Value = value;
4784 return newItem;
4785 }
4786 #endif // _VMA_RAW_LIST_FUNCTIONS
4787 #endif // _VMA_RAW_LIST
4788
4789 #ifndef _VMA_LIST
4790 template<typename T, typename AllocatorT>
4791 class VmaList
4792 {
4793 VMA_CLASS_NO_COPY(VmaList)
4794 public:
4795 class reverse_iterator;
4796 class const_iterator;
4797 class const_reverse_iterator;
4798
4799 class iterator
4800 {
4801 friend class const_iterator;
4802 friend class VmaList<T, AllocatorT>;
4803 public:
iterator()4804 iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
iterator(const reverse_iterator & src)4805 iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4806
4807 T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4808 T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4809
4810 bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4811 bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4812
4813 iterator operator++(int) { iterator result = *this; ++*this; return result; }
4814 iterator operator--(int) { iterator result = *this; --*this; return result; }
4815
4816 iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
4817 iterator& operator--();
4818
4819 private:
4820 VmaRawList<T>* m_pList;
4821 VmaListItem<T>* m_pItem;
4822
iterator(VmaRawList<T> * pList,VmaListItem<T> * pItem)4823 iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
4824 };
4825 class reverse_iterator
4826 {
4827 friend class const_reverse_iterator;
4828 friend class VmaList<T, AllocatorT>;
4829 public:
reverse_iterator()4830 reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
reverse_iterator(const iterator & src)4831 reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4832
4833 T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4834 T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4835
4836 bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4837 bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4838
4839 reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; }
4840 reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; }
4841
4842 reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
4843 reverse_iterator& operator--();
4844
4845 private:
4846 VmaRawList<T>* m_pList;
4847 VmaListItem<T>* m_pItem;
4848
reverse_iterator(VmaRawList<T> * pList,VmaListItem<T> * pItem)4849 reverse_iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
4850 };
4851 class const_iterator
4852 {
4853 friend class VmaList<T, AllocatorT>;
4854 public:
const_iterator()4855 const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
const_iterator(const iterator & src)4856 const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
const_iterator(const reverse_iterator & src)4857 const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4858
drop_const()4859 iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
4860
4861 const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4862 const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4863
4864 bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4865 bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4866
4867 const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; }
4868 const_iterator operator--(int) { const_iterator result = *this; --* this; return result; }
4869
4870 const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }
4871 const_iterator& operator--();
4872
4873 private:
4874 const VmaRawList<T>* m_pList;
4875 const VmaListItem<T>* m_pItem;
4876
const_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4877 const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
4878 };
4879 class const_reverse_iterator
4880 {
4881 friend class VmaList<T, AllocatorT>;
4882 public:
const_reverse_iterator()4883 const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}
const_reverse_iterator(const reverse_iterator & src)4884 const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
const_reverse_iterator(const iterator & src)4885 const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}
4886
drop_const()4887 reverse_iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }
4888
4889 const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }
4890 const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }
4891
4892 bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }
4893 bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }
4894
4895 const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; }
4896 const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; }
4897
4898 const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }
4899 const_reverse_iterator& operator--();
4900
4901 private:
4902 const VmaRawList<T>* m_pList;
4903 const VmaListItem<T>* m_pItem;
4904
const_reverse_iterator(const VmaRawList<T> * pList,const VmaListItem<T> * pItem)4905 const_reverse_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}
4906 };
4907
VmaList(const AllocatorT & allocator)4908 VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {}
4909
empty()4910 bool empty() const { return m_RawList.IsEmpty(); }
size()4911 size_t size() const { return m_RawList.GetCount(); }
4912
begin()4913 iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
end()4914 iterator end() { return iterator(&m_RawList, VMA_NULL); }
4915
cbegin()4916 const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
cend()4917 const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
4918
begin()4919 const_iterator begin() const { return cbegin(); }
end()4920 const_iterator end() const { return cend(); }
4921
rbegin()4922 reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); }
rend()4923 reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); }
4924
crbegin()4925 const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); }
crend()4926 const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); }
4927
rbegin()4928 const_reverse_iterator rbegin() const { return crbegin(); }
rend()4929 const_reverse_iterator rend() const { return crend(); }
4930
push_back(const T & value)4931 void push_back(const T& value) { m_RawList.PushBack(value); }
insert(iterator it,const T & value)4932 iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
4933
clear()4934 void clear() { m_RawList.Clear(); }
erase(iterator it)4935 void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
4936
4937 private:
4938 VmaRawList<T> m_RawList;
4939 };
4940
4941 #ifndef _VMA_LIST_FUNCTIONS
4942 template<typename T, typename AllocatorT>
4943 typename VmaList<T, AllocatorT>::iterator& VmaList<T, AllocatorT>::iterator::operator--()
4944 {
4945 if (m_pItem != VMA_NULL)
4946 {
4947 m_pItem = m_pItem->pPrev;
4948 }
4949 else
4950 {
4951 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4952 m_pItem = m_pList->Back();
4953 }
4954 return *this;
4955 }
4956
4957 template<typename T, typename AllocatorT>
4958 typename VmaList<T, AllocatorT>::reverse_iterator& VmaList<T, AllocatorT>::reverse_iterator::operator--()
4959 {
4960 if (m_pItem != VMA_NULL)
4961 {
4962 m_pItem = m_pItem->pNext;
4963 }
4964 else
4965 {
4966 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4967 m_pItem = m_pList->Front();
4968 }
4969 return *this;
4970 }
4971
4972 template<typename T, typename AllocatorT>
4973 typename VmaList<T, AllocatorT>::const_iterator& VmaList<T, AllocatorT>::const_iterator::operator--()
4974 {
4975 if (m_pItem != VMA_NULL)
4976 {
4977 m_pItem = m_pItem->pPrev;
4978 }
4979 else
4980 {
4981 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4982 m_pItem = m_pList->Back();
4983 }
4984 return *this;
4985 }
4986
4987 template<typename T, typename AllocatorT>
4988 typename VmaList<T, AllocatorT>::const_reverse_iterator& VmaList<T, AllocatorT>::const_reverse_iterator::operator--()
4989 {
4990 if (m_pItem != VMA_NULL)
4991 {
4992 m_pItem = m_pItem->pNext;
4993 }
4994 else
4995 {
4996 VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
4997 m_pItem = m_pList->Back();
4998 }
4999 return *this;
5000 }
5001 #endif // _VMA_LIST_FUNCTIONS
5002 #endif // _VMA_LIST
5003
5004 #ifndef _VMA_INTRUSIVE_LINKED_LIST
5005 /*
5006 Expected interface of ItemTypeTraits:
5007 struct MyItemTypeTraits
5008 {
5009 typedef MyItem ItemType;
5010 static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
5011 static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
5012 static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
5013 static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
5014 };
5015 */
5016 template<typename ItemTypeTraits>
5017 class VmaIntrusiveLinkedList
5018 {
5019 public:
5020 typedef typename ItemTypeTraits::ItemType ItemType;
GetPrev(const ItemType * item)5021 static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
GetNext(const ItemType * item)5022 static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
5023
5024 // Movable, not copyable.
5025 VmaIntrusiveLinkedList() = default;
5026 VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src);
5027 VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete;
5028 VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src);
5029 VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete;
~VmaIntrusiveLinkedList()5030 ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); }
5031
GetCount()5032 size_t GetCount() const { return m_Count; }
IsEmpty()5033 bool IsEmpty() const { return m_Count == 0; }
Front()5034 ItemType* Front() { return m_Front; }
Back()5035 ItemType* Back() { return m_Back; }
Front()5036 const ItemType* Front() const { return m_Front; }
Back()5037 const ItemType* Back() const { return m_Back; }
5038
5039 void PushBack(ItemType* item);
5040 void PushFront(ItemType* item);
5041 ItemType* PopBack();
5042 ItemType* PopFront();
5043
5044 // MyItem can be null - it means PushBack.
5045 void InsertBefore(ItemType* existingItem, ItemType* newItem);
5046 // MyItem can be null - it means PushFront.
5047 void InsertAfter(ItemType* existingItem, ItemType* newItem);
5048 void Remove(ItemType* item);
5049 void RemoveAll();
5050
5051 private:
5052 ItemType* m_Front = VMA_NULL;
5053 ItemType* m_Back = VMA_NULL;
5054 size_t m_Count = 0;
5055 };
5056
5057 #ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
5058 template<typename ItemTypeTraits>
VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src)5059 VmaIntrusiveLinkedList<ItemTypeTraits>::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src)
5060 : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
5061 {
5062 src.m_Front = src.m_Back = VMA_NULL;
5063 src.m_Count = 0;
5064 }
5065
5066 template<typename ItemTypeTraits>
5067 VmaIntrusiveLinkedList<ItemTypeTraits>& VmaIntrusiveLinkedList<ItemTypeTraits>::operator=(VmaIntrusiveLinkedList&& src)
5068 {
5069 if (&src != this)
5070 {
5071 VMA_HEAVY_ASSERT(IsEmpty());
5072 m_Front = src.m_Front;
5073 m_Back = src.m_Back;
5074 m_Count = src.m_Count;
5075 src.m_Front = src.m_Back = VMA_NULL;
5076 src.m_Count = 0;
5077 }
5078 return *this;
5079 }
5080
5081 template<typename ItemTypeTraits>
PushBack(ItemType * item)5082 void VmaIntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)
5083 {
5084 VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
5085 if (IsEmpty())
5086 {
5087 m_Front = item;
5088 m_Back = item;
5089 m_Count = 1;
5090 }
5091 else
5092 {
5093 ItemTypeTraits::AccessPrev(item) = m_Back;
5094 ItemTypeTraits::AccessNext(m_Back) = item;
5095 m_Back = item;
5096 ++m_Count;
5097 }
5098 }
5099
5100 template<typename ItemTypeTraits>
PushFront(ItemType * item)5101 void VmaIntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)
5102 {
5103 VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);
5104 if (IsEmpty())
5105 {
5106 m_Front = item;
5107 m_Back = item;
5108 m_Count = 1;
5109 }
5110 else
5111 {
5112 ItemTypeTraits::AccessNext(item) = m_Front;
5113 ItemTypeTraits::AccessPrev(m_Front) = item;
5114 m_Front = item;
5115 ++m_Count;
5116 }
5117 }
5118
5119 template<typename ItemTypeTraits>
PopBack()5120 typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopBack()
5121 {
5122 VMA_HEAVY_ASSERT(m_Count > 0);
5123 ItemType* const backItem = m_Back;
5124 ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
5125 if (prevItem != VMA_NULL)
5126 {
5127 ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;
5128 }
5129 m_Back = prevItem;
5130 --m_Count;
5131 ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;
5132 ItemTypeTraits::AccessNext(backItem) = VMA_NULL;
5133 return backItem;
5134 }
5135
5136 template<typename ItemTypeTraits>
PopFront()5137 typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopFront()
5138 {
5139 VMA_HEAVY_ASSERT(m_Count > 0);
5140 ItemType* const frontItem = m_Front;
5141 ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
5142 if (nextItem != VMA_NULL)
5143 {
5144 ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;
5145 }
5146 m_Front = nextItem;
5147 --m_Count;
5148 ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;
5149 ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;
5150 return frontItem;
5151 }
5152
5153 template<typename ItemTypeTraits>
InsertBefore(ItemType * existingItem,ItemType * newItem)5154 void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)
5155 {
5156 VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
5157 if (existingItem != VMA_NULL)
5158 {
5159 ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
5160 ItemTypeTraits::AccessPrev(newItem) = prevItem;
5161 ItemTypeTraits::AccessNext(newItem) = existingItem;
5162 ItemTypeTraits::AccessPrev(existingItem) = newItem;
5163 if (prevItem != VMA_NULL)
5164 {
5165 ItemTypeTraits::AccessNext(prevItem) = newItem;
5166 }
5167 else
5168 {
5169 VMA_HEAVY_ASSERT(m_Front == existingItem);
5170 m_Front = newItem;
5171 }
5172 ++m_Count;
5173 }
5174 else
5175 PushBack(newItem);
5176 }
5177
5178 template<typename ItemTypeTraits>
InsertAfter(ItemType * existingItem,ItemType * newItem)5179 void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)
5180 {
5181 VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);
5182 if (existingItem != VMA_NULL)
5183 {
5184 ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
5185 ItemTypeTraits::AccessNext(newItem) = nextItem;
5186 ItemTypeTraits::AccessPrev(newItem) = existingItem;
5187 ItemTypeTraits::AccessNext(existingItem) = newItem;
5188 if (nextItem != VMA_NULL)
5189 {
5190 ItemTypeTraits::AccessPrev(nextItem) = newItem;
5191 }
5192 else
5193 {
5194 VMA_HEAVY_ASSERT(m_Back == existingItem);
5195 m_Back = newItem;
5196 }
5197 ++m_Count;
5198 }
5199 else
5200 return PushFront(newItem);
5201 }
5202
5203 template<typename ItemTypeTraits>
Remove(ItemType * item)5204 void VmaIntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)
5205 {
5206 VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);
5207 if (ItemTypeTraits::GetPrev(item) != VMA_NULL)
5208 {
5209 ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
5210 }
5211 else
5212 {
5213 VMA_HEAVY_ASSERT(m_Front == item);
5214 m_Front = ItemTypeTraits::GetNext(item);
5215 }
5216
5217 if (ItemTypeTraits::GetNext(item) != VMA_NULL)
5218 {
5219 ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
5220 }
5221 else
5222 {
5223 VMA_HEAVY_ASSERT(m_Back == item);
5224 m_Back = ItemTypeTraits::GetPrev(item);
5225 }
5226 ItemTypeTraits::AccessPrev(item) = VMA_NULL;
5227 ItemTypeTraits::AccessNext(item) = VMA_NULL;
5228 --m_Count;
5229 }
5230
5231 template<typename ItemTypeTraits>
RemoveAll()5232 void VmaIntrusiveLinkedList<ItemTypeTraits>::RemoveAll()
5233 {
5234 if (!IsEmpty())
5235 {
5236 ItemType* item = m_Back;
5237 while (item != VMA_NULL)
5238 {
5239 ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
5240 ItemTypeTraits::AccessPrev(item) = VMA_NULL;
5241 ItemTypeTraits::AccessNext(item) = VMA_NULL;
5242 item = prevItem;
5243 }
5244 m_Front = VMA_NULL;
5245 m_Back = VMA_NULL;
5246 m_Count = 0;
5247 }
5248 }
5249 #endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS
5250 #endif // _VMA_INTRUSIVE_LINKED_LIST
5251
5252 // Unused in this version.
5253 #if 0
5254
5255 #ifndef _VMA_PAIR
5256 template<typename T1, typename T2>
5257 struct VmaPair
5258 {
5259 T1 first;
5260 T2 second;
5261
5262 VmaPair() : first(), second() {}
5263 VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {}
5264 };
5265
5266 template<typename FirstT, typename SecondT>
5267 struct VmaPairFirstLess
5268 {
5269 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
5270 {
5271 return lhs.first < rhs.first;
5272 }
5273 bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
5274 {
5275 return lhs.first < rhsFirst;
5276 }
5277 };
5278 #endif // _VMA_PAIR
5279
5280 #ifndef _VMA_MAP
5281 /* Class compatible with subset of interface of std::unordered_map.
5282 KeyT, ValueT must be POD because they will be stored in VmaVector.
5283 */
5284 template<typename KeyT, typename ValueT>
5285 class VmaMap
5286 {
5287 public:
5288 typedef VmaPair<KeyT, ValueT> PairType;
5289 typedef PairType* iterator;
5290
5291 VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) {}
5292
5293 iterator begin() { return m_Vector.begin(); }
5294 iterator end() { return m_Vector.end(); }
5295 size_t size() { return m_Vector.size(); }
5296
5297 void insert(const PairType& pair);
5298 iterator find(const KeyT& key);
5299 void erase(iterator it);
5300
5301 private:
5302 VmaVector< PairType, VmaStlAllocator<PairType>> m_Vector;
5303 };
5304
5305 #ifndef _VMA_MAP_FUNCTIONS
5306 template<typename KeyT, typename ValueT>
5307 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
5308 {
5309 const size_t indexToInsert = VmaBinaryFindFirstNotLess(
5310 m_Vector.data(),
5311 m_Vector.data() + m_Vector.size(),
5312 pair,
5313 VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
5314 VmaVectorInsert(m_Vector, indexToInsert, pair);
5315 }
5316
5317 template<typename KeyT, typename ValueT>
5318 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
5319 {
5320 PairType* it = VmaBinaryFindFirstNotLess(
5321 m_Vector.data(),
5322 m_Vector.data() + m_Vector.size(),
5323 key,
5324 VmaPairFirstLess<KeyT, ValueT>());
5325 if ((it != m_Vector.end()) && (it->first == key))
5326 {
5327 return it;
5328 }
5329 else
5330 {
5331 return m_Vector.end();
5332 }
5333 }
5334
5335 template<typename KeyT, typename ValueT>
5336 void VmaMap<KeyT, ValueT>::erase(iterator it)
5337 {
5338 VmaVectorRemove(m_Vector, it - m_Vector.begin());
5339 }
5340 #endif // _VMA_MAP_FUNCTIONS
5341 #endif // _VMA_MAP
5342
5343 #endif // #if 0
5344
5345 #if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED
5346 class VmaStringBuilder
5347 {
5348 public:
VmaStringBuilder(const VkAllocationCallbacks * allocationCallbacks)5349 VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) {}
5350 ~VmaStringBuilder() = default;
5351
GetLength()5352 size_t GetLength() const { return m_Data.size(); }
GetData()5353 const char* GetData() const { return m_Data.data(); }
AddNewLine()5354 void AddNewLine() { Add('\n'); }
Add(char ch)5355 void Add(char ch) { m_Data.push_back(ch); }
5356
5357 void Add(const char* pStr);
5358 void AddNumber(uint32_t num);
5359 void AddNumber(uint64_t num);
5360 void AddPointer(const void* ptr);
5361
5362 private:
5363 VmaVector<char, VmaStlAllocator<char>> m_Data;
5364 };
5365
5366 #ifndef _VMA_STRING_BUILDER_FUNCTIONS
Add(const char * pStr)5367 void VmaStringBuilder::Add(const char* pStr)
5368 {
5369 const size_t strLen = strlen(pStr);
5370 if (strLen > 0)
5371 {
5372 const size_t oldCount = m_Data.size();
5373 m_Data.resize(oldCount + strLen);
5374 memcpy(m_Data.data() + oldCount, pStr, strLen);
5375 }
5376 }
5377
AddNumber(uint32_t num)5378 void VmaStringBuilder::AddNumber(uint32_t num)
5379 {
5380 char buf[11];
5381 buf[10] = '\0';
5382 char* p = &buf[10];
5383 do
5384 {
5385 *--p = '0' + (num % 10);
5386 num /= 10;
5387 } while (num);
5388 Add(p);
5389 }
5390
AddNumber(uint64_t num)5391 void VmaStringBuilder::AddNumber(uint64_t num)
5392 {
5393 char buf[21];
5394 buf[20] = '\0';
5395 char* p = &buf[20];
5396 do
5397 {
5398 *--p = '0' + (num % 10);
5399 num /= 10;
5400 } while (num);
5401 Add(p);
5402 }
5403
AddPointer(const void * ptr)5404 void VmaStringBuilder::AddPointer(const void* ptr)
5405 {
5406 char buf[21];
5407 VmaPtrToStr(buf, sizeof(buf), ptr);
5408 Add(buf);
5409 }
5410 #endif //_VMA_STRING_BUILDER_FUNCTIONS
5411 #endif // _VMA_STRING_BUILDER
5412
5413 #if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED
5414 /*
5415 Allows to conveniently build a correct JSON document to be written to the
5416 VmaStringBuilder passed to the constructor.
5417 */
5418 class VmaJsonWriter
5419 {
5420 VMA_CLASS_NO_COPY(VmaJsonWriter)
5421 public:
5422 // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object.
5423 VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
5424 ~VmaJsonWriter();
5425
5426 // Begins object by writing "{".
5427 // Inside an object, you must call pairs of WriteString and a value, e.g.:
5428 // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();
5429 // Will write: { "A": 1, "B": 2 }
5430 void BeginObject(bool singleLine = false);
5431 // Ends object by writing "}".
5432 void EndObject();
5433
5434 // Begins array by writing "[".
5435 // Inside an array, you can write a sequence of any values.
5436 void BeginArray(bool singleLine = false);
5437 // Ends array by writing "[".
5438 void EndArray();
5439
5440 // Writes a string value inside "".
5441 // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped.
5442 void WriteString(const char* pStr);
5443
5444 // Begins writing a string value.
5445 // Call BeginString, ContinueString, ContinueString, ..., EndString instead of
5446 // WriteString to conveniently build the string content incrementally, made of
5447 // parts including numbers.
5448 void BeginString(const char* pStr = VMA_NULL);
5449 // Posts next part of an open string.
5450 void ContinueString(const char* pStr);
5451 // Posts next part of an open string. The number is converted to decimal characters.
5452 void ContinueString(uint32_t n);
5453 void ContinueString(uint64_t n);
5454 void ContinueString_Size(size_t n);
5455 // Posts next part of an open string. Pointer value is converted to characters
5456 // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
5457 void ContinueString_Pointer(const void* ptr);
5458 // Ends writing a string value by writing '"'.
5459 void EndString(const char* pStr = VMA_NULL);
5460
5461 // Writes a number value.
5462 void WriteNumber(uint32_t n);
5463 void WriteNumber(uint64_t n);
5464 void WriteSize(size_t n);
5465 // Writes a boolean value - false or true.
5466 void WriteBool(bool b);
5467 // Writes a null value.
5468 void WriteNull();
5469
5470 private:
5471 enum COLLECTION_TYPE
5472 {
5473 COLLECTION_TYPE_OBJECT,
5474 COLLECTION_TYPE_ARRAY,
5475 };
5476 struct StackItem
5477 {
5478 COLLECTION_TYPE type;
5479 uint32_t valueCount;
5480 bool singleLineMode;
5481 };
5482
5483 static const char* const INDENT;
5484
5485 VmaStringBuilder& m_SB;
5486 VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
5487 bool m_InsideString;
5488
5489 // Write size_t for less than 64bits
WriteSize(size_t n,std::integral_constant<bool,false>)5490 void WriteSize(size_t n, std::integral_constant<bool, false>) { m_SB.AddNumber(static_cast<uint32_t>(n)); }
5491 // Write size_t for 64bits
WriteSize(size_t n,std::integral_constant<bool,true>)5492 void WriteSize(size_t n, std::integral_constant<bool, true>) { m_SB.AddNumber(static_cast<uint64_t>(n)); }
5493
5494 void BeginValue(bool isString);
5495 void WriteIndent(bool oneLess = false);
5496 };
5497 const char* const VmaJsonWriter::INDENT = " ";
5498
5499 #ifndef _VMA_JSON_WRITER_FUNCTIONS
VmaJsonWriter(const VkAllocationCallbacks * pAllocationCallbacks,VmaStringBuilder & sb)5500 VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb)
5501 : m_SB(sb),
5502 m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
5503 m_InsideString(false) {}
5504
~VmaJsonWriter()5505 VmaJsonWriter::~VmaJsonWriter()
5506 {
5507 VMA_ASSERT(!m_InsideString);
5508 VMA_ASSERT(m_Stack.empty());
5509 }
5510
BeginObject(bool singleLine)5511 void VmaJsonWriter::BeginObject(bool singleLine)
5512 {
5513 VMA_ASSERT(!m_InsideString);
5514
5515 BeginValue(false);
5516 m_SB.Add('{');
5517
5518 StackItem item;
5519 item.type = COLLECTION_TYPE_OBJECT;
5520 item.valueCount = 0;
5521 item.singleLineMode = singleLine;
5522 m_Stack.push_back(item);
5523 }
5524
EndObject()5525 void VmaJsonWriter::EndObject()
5526 {
5527 VMA_ASSERT(!m_InsideString);
5528
5529 WriteIndent(true);
5530 m_SB.Add('}');
5531
5532 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
5533 m_Stack.pop_back();
5534 }
5535
BeginArray(bool singleLine)5536 void VmaJsonWriter::BeginArray(bool singleLine)
5537 {
5538 VMA_ASSERT(!m_InsideString);
5539
5540 BeginValue(false);
5541 m_SB.Add('[');
5542
5543 StackItem item;
5544 item.type = COLLECTION_TYPE_ARRAY;
5545 item.valueCount = 0;
5546 item.singleLineMode = singleLine;
5547 m_Stack.push_back(item);
5548 }
5549
EndArray()5550 void VmaJsonWriter::EndArray()
5551 {
5552 VMA_ASSERT(!m_InsideString);
5553
5554 WriteIndent(true);
5555 m_SB.Add(']');
5556
5557 VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
5558 m_Stack.pop_back();
5559 }
5560
WriteString(const char * pStr)5561 void VmaJsonWriter::WriteString(const char* pStr)
5562 {
5563 BeginString(pStr);
5564 EndString();
5565 }
5566
BeginString(const char * pStr)5567 void VmaJsonWriter::BeginString(const char* pStr)
5568 {
5569 VMA_ASSERT(!m_InsideString);
5570
5571 BeginValue(true);
5572 m_SB.Add('"');
5573 m_InsideString = true;
5574 if (pStr != VMA_NULL && pStr[0] != '\0')
5575 {
5576 ContinueString(pStr);
5577 }
5578 }
5579
ContinueString(const char * pStr)5580 void VmaJsonWriter::ContinueString(const char* pStr)
5581 {
5582 VMA_ASSERT(m_InsideString);
5583
5584 const size_t strLen = strlen(pStr);
5585 for (size_t i = 0; i < strLen; ++i)
5586 {
5587 char ch = pStr[i];
5588 if (ch == '\\')
5589 {
5590 m_SB.Add("\\\\");
5591 }
5592 else if (ch == '"')
5593 {
5594 m_SB.Add("\\\"");
5595 }
5596 else if (ch >= 32)
5597 {
5598 m_SB.Add(ch);
5599 }
5600 else switch (ch)
5601 {
5602 case '\b':
5603 m_SB.Add("\\b");
5604 break;
5605 case '\f':
5606 m_SB.Add("\\f");
5607 break;
5608 case '\n':
5609 m_SB.Add("\\n");
5610 break;
5611 case '\r':
5612 m_SB.Add("\\r");
5613 break;
5614 case '\t':
5615 m_SB.Add("\\t");
5616 break;
5617 default:
5618 VMA_ASSERT(0 && "Character not currently supported.");
5619 break;
5620 }
5621 }
5622 }
5623
ContinueString(uint32_t n)5624 void VmaJsonWriter::ContinueString(uint32_t n)
5625 {
5626 VMA_ASSERT(m_InsideString);
5627 m_SB.AddNumber(n);
5628 }
5629
ContinueString(uint64_t n)5630 void VmaJsonWriter::ContinueString(uint64_t n)
5631 {
5632 VMA_ASSERT(m_InsideString);
5633 m_SB.AddNumber(n);
5634 }
5635
ContinueString_Size(size_t n)5636 void VmaJsonWriter::ContinueString_Size(size_t n)
5637 {
5638 VMA_ASSERT(m_InsideString);
5639 // Fix for AppleClang incorrect type casting
5640 // TODO: Change to if constexpr when C++17 used as minimal standard
5641 WriteSize(n, std::is_same<size_t, uint64_t>{});
5642 }
5643
ContinueString_Pointer(const void * ptr)5644 void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
5645 {
5646 VMA_ASSERT(m_InsideString);
5647 m_SB.AddPointer(ptr);
5648 }
5649
EndString(const char * pStr)5650 void VmaJsonWriter::EndString(const char* pStr)
5651 {
5652 VMA_ASSERT(m_InsideString);
5653 if (pStr != VMA_NULL && pStr[0] != '\0')
5654 {
5655 ContinueString(pStr);
5656 }
5657 m_SB.Add('"');
5658 m_InsideString = false;
5659 }
5660
WriteNumber(uint32_t n)5661 void VmaJsonWriter::WriteNumber(uint32_t n)
5662 {
5663 VMA_ASSERT(!m_InsideString);
5664 BeginValue(false);
5665 m_SB.AddNumber(n);
5666 }
5667
WriteNumber(uint64_t n)5668 void VmaJsonWriter::WriteNumber(uint64_t n)
5669 {
5670 VMA_ASSERT(!m_InsideString);
5671 BeginValue(false);
5672 m_SB.AddNumber(n);
5673 }
5674
WriteSize(size_t n)5675 void VmaJsonWriter::WriteSize(size_t n)
5676 {
5677 VMA_ASSERT(!m_InsideString);
5678 BeginValue(false);
5679 // Fix for AppleClang incorrect type casting
5680 // TODO: Change to if constexpr when C++17 used as minimal standard
5681 WriteSize(n, std::is_same<size_t, uint64_t>{});
5682 }
5683
WriteBool(bool b)5684 void VmaJsonWriter::WriteBool(bool b)
5685 {
5686 VMA_ASSERT(!m_InsideString);
5687 BeginValue(false);
5688 m_SB.Add(b ? "true" : "false");
5689 }
5690
WriteNull()5691 void VmaJsonWriter::WriteNull()
5692 {
5693 VMA_ASSERT(!m_InsideString);
5694 BeginValue(false);
5695 m_SB.Add("null");
5696 }
5697
BeginValue(bool isString)5698 void VmaJsonWriter::BeginValue(bool isString)
5699 {
5700 if (!m_Stack.empty())
5701 {
5702 StackItem& currItem = m_Stack.back();
5703 if (currItem.type == COLLECTION_TYPE_OBJECT &&
5704 currItem.valueCount % 2 == 0)
5705 {
5706 VMA_ASSERT(isString);
5707 }
5708
5709 if (currItem.type == COLLECTION_TYPE_OBJECT &&
5710 currItem.valueCount % 2 != 0)
5711 {
5712 m_SB.Add(": ");
5713 }
5714 else if (currItem.valueCount > 0)
5715 {
5716 m_SB.Add(", ");
5717 WriteIndent();
5718 }
5719 else
5720 {
5721 WriteIndent();
5722 }
5723 ++currItem.valueCount;
5724 }
5725 }
5726
WriteIndent(bool oneLess)5727 void VmaJsonWriter::WriteIndent(bool oneLess)
5728 {
5729 if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
5730 {
5731 m_SB.AddNewLine();
5732
5733 size_t count = m_Stack.size();
5734 if (count > 0 && oneLess)
5735 {
5736 --count;
5737 }
5738 for (size_t i = 0; i < count; ++i)
5739 {
5740 m_SB.Add(INDENT);
5741 }
5742 }
5743 }
5744 #endif // _VMA_JSON_WRITER_FUNCTIONS
5745
VmaPrintDetailedStatistics(VmaJsonWriter & json,const VmaDetailedStatistics & stat)5746 static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat)
5747 {
5748 json.BeginObject();
5749
5750 json.WriteString("BlockCount");
5751 json.WriteNumber(stat.statistics.blockCount);
5752 json.WriteString("BlockBytes");
5753 json.WriteNumber(stat.statistics.blockBytes);
5754 json.WriteString("AllocationCount");
5755 json.WriteNumber(stat.statistics.allocationCount);
5756 json.WriteString("AllocationBytes");
5757 json.WriteNumber(stat.statistics.allocationBytes);
5758 json.WriteString("UnusedRangeCount");
5759 json.WriteNumber(stat.unusedRangeCount);
5760
5761 if (stat.statistics.allocationCount > 1)
5762 {
5763 json.WriteString("AllocationSizeMin");
5764 json.WriteNumber(stat.allocationSizeMin);
5765 json.WriteString("AllocationSizeMax");
5766 json.WriteNumber(stat.allocationSizeMax);
5767 }
5768 if (stat.unusedRangeCount > 1)
5769 {
5770 json.WriteString("UnusedRangeSizeMin");
5771 json.WriteNumber(stat.unusedRangeSizeMin);
5772 json.WriteString("UnusedRangeSizeMax");
5773 json.WriteNumber(stat.unusedRangeSizeMax);
5774 }
5775 json.EndObject();
5776 }
5777 #endif // _VMA_JSON_WRITER
5778
5779 #ifndef _VMA_MAPPING_HYSTERESIS
5780
5781 class VmaMappingHysteresis
5782 {
5783 VMA_CLASS_NO_COPY(VmaMappingHysteresis)
5784 public:
5785 VmaMappingHysteresis() = default;
5786
GetExtraMapping()5787 uint32_t GetExtraMapping() const { return m_ExtraMapping; }
5788
5789 // Call when Map was called.
5790 // Returns true if switched to extra +1 mapping reference count.
PostMap()5791 bool PostMap()
5792 {
5793 #if VMA_MAPPING_HYSTERESIS_ENABLED
5794 if(m_ExtraMapping == 0)
5795 {
5796 ++m_MajorCounter;
5797 if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING)
5798 {
5799 m_ExtraMapping = 1;
5800 m_MajorCounter = 0;
5801 m_MinorCounter = 0;
5802 return true;
5803 }
5804 }
5805 else // m_ExtraMapping == 1
5806 PostMinorCounter();
5807 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5808 return false;
5809 }
5810
5811 // Call when Unmap was called.
PostUnmap()5812 void PostUnmap()
5813 {
5814 #if VMA_MAPPING_HYSTERESIS_ENABLED
5815 if(m_ExtraMapping == 0)
5816 ++m_MajorCounter;
5817 else // m_ExtraMapping == 1
5818 PostMinorCounter();
5819 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5820 }
5821
5822 // Call when allocation was made from the memory block.
PostAlloc()5823 void PostAlloc()
5824 {
5825 #if VMA_MAPPING_HYSTERESIS_ENABLED
5826 if(m_ExtraMapping == 1)
5827 ++m_MajorCounter;
5828 else // m_ExtraMapping == 0
5829 PostMinorCounter();
5830 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5831 }
5832
5833 // Call when allocation was freed from the memory block.
5834 // Returns true if switched to extra -1 mapping reference count.
PostFree()5835 bool PostFree()
5836 {
5837 #if VMA_MAPPING_HYSTERESIS_ENABLED
5838 if(m_ExtraMapping == 1)
5839 {
5840 ++m_MajorCounter;
5841 if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING &&
5842 m_MajorCounter > m_MinorCounter + 1)
5843 {
5844 m_ExtraMapping = 0;
5845 m_MajorCounter = 0;
5846 m_MinorCounter = 0;
5847 return true;
5848 }
5849 }
5850 else // m_ExtraMapping == 0
5851 PostMinorCounter();
5852 #endif // #if VMA_MAPPING_HYSTERESIS_ENABLED
5853 return false;
5854 }
5855
5856 private:
5857 static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7;
5858
5859 uint32_t m_MinorCounter = 0;
5860 uint32_t m_MajorCounter = 0;
5861 uint32_t m_ExtraMapping = 0; // 0 or 1.
5862
PostMinorCounter()5863 void PostMinorCounter()
5864 {
5865 if(m_MinorCounter < m_MajorCounter)
5866 {
5867 ++m_MinorCounter;
5868 }
5869 else if(m_MajorCounter > 0)
5870 {
5871 --m_MajorCounter;
5872 --m_MinorCounter;
5873 }
5874 }
5875 };
5876
5877 #endif // _VMA_MAPPING_HYSTERESIS
5878
5879 #ifndef _VMA_DEVICE_MEMORY_BLOCK
5880 /*
5881 Represents a single block of device memory (`VkDeviceMemory`) with all the
5882 data about its regions (aka suballocations, #VmaAllocation), assigned and free.
5883
5884 Thread-safety:
5885 - Access to m_pMetadata must be externally synchronized.
5886 - Map, Unmap, Bind* are synchronized internally.
5887 */
5888 class VmaDeviceMemoryBlock
5889 {
5890 VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
5891 public:
5892 VmaBlockMetadata* m_pMetadata;
5893
5894 VmaDeviceMemoryBlock(VmaAllocator hAllocator);
5895 ~VmaDeviceMemoryBlock();
5896
5897 // Always call after construction.
5898 void Init(
5899 VmaAllocator hAllocator,
5900 VmaPool hParentPool,
5901 uint32_t newMemoryTypeIndex,
5902 VkDeviceMemory newMemory,
5903 VkDeviceSize newSize,
5904 uint32_t id,
5905 uint32_t algorithm,
5906 VkDeviceSize bufferImageGranularity);
5907 // Always call before destruction.
5908 void Destroy(VmaAllocator allocator);
5909
GetParentPool()5910 VmaPool GetParentPool() const { return m_hParentPool; }
GetDeviceMemory()5911 VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
GetMemoryTypeIndex()5912 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetId()5913 uint32_t GetId() const { return m_Id; }
GetMappedData()5914 void* GetMappedData() const { return m_pMappedData; }
GetMapRefCount()5915 uint32_t GetMapRefCount() const { return m_MapCount; }
5916
5917 // Call when allocation/free was made from m_pMetadata.
5918 // Used for m_MappingHysteresis.
PostAlloc()5919 void PostAlloc() { m_MappingHysteresis.PostAlloc(); }
5920 void PostFree(VmaAllocator hAllocator);
5921
5922 // Validates all data structures inside this object. If not valid, returns false.
5923 bool Validate() const;
5924 VkResult CheckCorruption(VmaAllocator hAllocator);
5925
5926 // ppData can be null.
5927 VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
5928 void Unmap(VmaAllocator hAllocator, uint32_t count);
5929
5930 VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5931 VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
5932
5933 VkResult BindBufferMemory(
5934 const VmaAllocator hAllocator,
5935 const VmaAllocation hAllocation,
5936 VkDeviceSize allocationLocalOffset,
5937 VkBuffer hBuffer,
5938 const void* pNext);
5939 VkResult BindImageMemory(
5940 const VmaAllocator hAllocator,
5941 const VmaAllocation hAllocation,
5942 VkDeviceSize allocationLocalOffset,
5943 VkImage hImage,
5944 const void* pNext);
5945
5946 private:
5947 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
5948 uint32_t m_MemoryTypeIndex;
5949 uint32_t m_Id;
5950 VkDeviceMemory m_hMemory;
5951
5952 /*
5953 Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
5954 Also protects m_MapCount, m_pMappedData.
5955 Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
5956 */
5957 VMA_MUTEX m_MapAndBindMutex;
5958 VmaMappingHysteresis m_MappingHysteresis;
5959 uint32_t m_MapCount;
5960 void* m_pMappedData;
5961 };
5962 #endif // _VMA_DEVICE_MEMORY_BLOCK
5963
5964 #ifndef _VMA_ALLOCATION_T
5965 struct VmaAllocation_T
5966 {
5967 friend struct VmaDedicatedAllocationListItemTraits;
5968
5969 enum FLAGS
5970 {
5971 FLAG_PERSISTENT_MAP = 0x01,
5972 FLAG_MAPPING_ALLOWED = 0x02,
5973 };
5974
5975 public:
5976 enum ALLOCATION_TYPE
5977 {
5978 ALLOCATION_TYPE_NONE,
5979 ALLOCATION_TYPE_BLOCK,
5980 ALLOCATION_TYPE_DEDICATED,
5981 };
5982
5983 // This struct is allocated using VmaPoolAllocator.
5984 VmaAllocation_T(bool mappingAllowed);
5985 ~VmaAllocation_T();
5986
5987 void InitBlockAllocation(
5988 VmaDeviceMemoryBlock* block,
5989 VmaAllocHandle allocHandle,
5990 VkDeviceSize alignment,
5991 VkDeviceSize size,
5992 uint32_t memoryTypeIndex,
5993 VmaSuballocationType suballocationType,
5994 bool mapped);
5995 // pMappedData not null means allocation is created with MAPPED flag.
5996 void InitDedicatedAllocation(
5997 VmaPool hParentPool,
5998 uint32_t memoryTypeIndex,
5999 VkDeviceMemory hMemory,
6000 VmaSuballocationType suballocationType,
6001 void* pMappedData,
6002 VkDeviceSize size);
6003
GetTypeVmaAllocation_T6004 ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
GetAlignmentVmaAllocation_T6005 VkDeviceSize GetAlignment() const { return m_Alignment; }
GetSizeVmaAllocation_T6006 VkDeviceSize GetSize() const { return m_Size; }
GetUserDataVmaAllocation_T6007 void* GetUserData() const { return m_pUserData; }
GetNameVmaAllocation_T6008 const char* GetName() const { return m_pName; }
GetSuballocationTypeVmaAllocation_T6009 VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
6010
GetBlockVmaAllocation_T6011 VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; }
GetMemoryTypeIndexVmaAllocation_T6012 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
IsPersistentMapVmaAllocation_T6013 bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; }
IsMappingAllowedVmaAllocation_T6014 bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; }
6015
SetUserDataVmaAllocation_T6016 void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; }
6017 void SetName(VmaAllocator hAllocator, const char* pName);
6018 void FreeName(VmaAllocator hAllocator);
6019 uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation);
6020 VmaAllocHandle GetAllocHandle() const;
6021 VkDeviceSize GetOffset() const;
6022 VmaPool GetParentPool() const;
6023 VkDeviceMemory GetMemory() const;
6024 void* GetMappedData() const;
6025
6026 void BlockAllocMap();
6027 void BlockAllocUnmap();
6028 VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
6029 void DedicatedAllocUnmap(VmaAllocator hAllocator);
6030
6031 void FreeEmptyBlock();
6032
6033 #if VMA_STATS_STRING_ENABLED
GetBufferImageUsageVmaAllocation_T6034 uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
6035
6036 void InitBufferImageUsage(uint32_t bufferImageUsage);
6037 void PrintParameters(class VmaJsonWriter& json) const;
6038 #endif
6039
6040 private:
6041 // Allocation out of VmaDeviceMemoryBlock.
6042 struct BlockAllocation
6043 {
6044 VmaDeviceMemoryBlock* m_Block;
6045 VmaAllocHandle m_AllocHandle;
6046 };
6047 // Allocation for an object that has its own private VkDeviceMemory.
6048 struct DedicatedAllocation
6049 {
6050 VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.
6051 VkDeviceMemory m_hMemory;
6052 void* m_pMappedData; // Not null means memory is mapped.
6053 VmaAllocation_T* m_Prev;
6054 VmaAllocation_T* m_Next;
6055 };
6056 union
6057 {
6058 // Allocation out of VmaDeviceMemoryBlock.
6059 BlockAllocation m_BlockAllocation;
6060 // Allocation for an object that has its own private VkDeviceMemory.
6061 DedicatedAllocation m_DedicatedAllocation;
6062 };
6063
6064 VkDeviceSize m_Alignment;
6065 VkDeviceSize m_Size;
6066 void* m_pUserData;
6067 char* m_pName;
6068 uint32_t m_MemoryTypeIndex;
6069 uint8_t m_Type; // ALLOCATION_TYPE
6070 uint8_t m_SuballocationType; // VmaSuballocationType
6071 // Reference counter for vmaMapMemory()/vmaUnmapMemory().
6072 uint8_t m_MapCount;
6073 uint8_t m_Flags; // enum FLAGS
6074 #if VMA_STATS_STRING_ENABLED
6075 uint32_t m_BufferImageUsage; // 0 if unknown.
6076 #endif
6077 };
6078 #endif // _VMA_ALLOCATION_T
6079
6080 #ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
6081 struct VmaDedicatedAllocationListItemTraits
6082 {
6083 typedef VmaAllocation_T ItemType;
6084
GetPrevVmaDedicatedAllocationListItemTraits6085 static ItemType* GetPrev(const ItemType* item)
6086 {
6087 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6088 return item->m_DedicatedAllocation.m_Prev;
6089 }
GetNextVmaDedicatedAllocationListItemTraits6090 static ItemType* GetNext(const ItemType* item)
6091 {
6092 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6093 return item->m_DedicatedAllocation.m_Next;
6094 }
AccessPrevVmaDedicatedAllocationListItemTraits6095 static ItemType*& AccessPrev(ItemType* item)
6096 {
6097 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6098 return item->m_DedicatedAllocation.m_Prev;
6099 }
AccessNextVmaDedicatedAllocationListItemTraits6100 static ItemType*& AccessNext(ItemType* item)
6101 {
6102 VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
6103 return item->m_DedicatedAllocation.m_Next;
6104 }
6105 };
6106 #endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS
6107
6108 #ifndef _VMA_DEDICATED_ALLOCATION_LIST
6109 /*
6110 Stores linked list of VmaAllocation_T objects.
6111 Thread-safe, synchronized internally.
6112 */
6113 class VmaDedicatedAllocationList
6114 {
6115 public:
VmaDedicatedAllocationList()6116 VmaDedicatedAllocationList() {}
6117 ~VmaDedicatedAllocationList();
6118
Init(bool useMutex)6119 void Init(bool useMutex) { m_UseMutex = useMutex; }
6120 bool Validate();
6121
6122 void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
6123 void AddStatistics(VmaStatistics& inoutStats);
6124 #if VMA_STATS_STRING_ENABLED
6125 // Writes JSON array with the list of allocations.
6126 void BuildStatsString(VmaJsonWriter& json);
6127 #endif
6128
6129 bool IsEmpty();
6130 void Register(VmaAllocation alloc);
6131 void Unregister(VmaAllocation alloc);
6132
6133 private:
6134 typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;
6135
6136 bool m_UseMutex = true;
6137 VMA_RW_MUTEX m_Mutex;
6138 DedicatedAllocationLinkedList m_AllocationList;
6139 };
6140
6141 #ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
6142
~VmaDedicatedAllocationList()6143 VmaDedicatedAllocationList::~VmaDedicatedAllocationList()
6144 {
6145 VMA_HEAVY_ASSERT(Validate());
6146
6147 if (!m_AllocationList.IsEmpty())
6148 {
6149 VMA_ASSERT(false && "Unfreed dedicated allocations found!");
6150 }
6151 }
6152
Validate()6153 bool VmaDedicatedAllocationList::Validate()
6154 {
6155 const size_t declaredCount = m_AllocationList.GetCount();
6156 size_t actualCount = 0;
6157 VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6158 for (VmaAllocation alloc = m_AllocationList.Front();
6159 alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
6160 {
6161 ++actualCount;
6162 }
6163 VMA_VALIDATE(actualCount == declaredCount);
6164
6165 return true;
6166 }
6167
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)6168 void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
6169 {
6170 for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
6171 {
6172 const VkDeviceSize size = item->GetSize();
6173 inoutStats.statistics.blockCount++;
6174 inoutStats.statistics.blockBytes += size;
6175 VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize());
6176 }
6177 }
6178
AddStatistics(VmaStatistics & inoutStats)6179 void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)
6180 {
6181 VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6182
6183 const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount();
6184 inoutStats.blockCount += allocCount;
6185 inoutStats.allocationCount += allocCount;
6186
6187 for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
6188 {
6189 const VkDeviceSize size = item->GetSize();
6190 inoutStats.blockBytes += size;
6191 inoutStats.allocationBytes += size;
6192 }
6193 }
6194
6195 #if VMA_STATS_STRING_ENABLED
BuildStatsString(VmaJsonWriter & json)6196 void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)
6197 {
6198 VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6199 json.BeginArray();
6200 for (VmaAllocation alloc = m_AllocationList.Front();
6201 alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))
6202 {
6203 json.BeginObject(true);
6204 alloc->PrintParameters(json);
6205 json.EndObject();
6206 }
6207 json.EndArray();
6208 }
6209 #endif // VMA_STATS_STRING_ENABLED
6210
IsEmpty()6211 bool VmaDedicatedAllocationList::IsEmpty()
6212 {
6213 VmaMutexLockRead lock(m_Mutex, m_UseMutex);
6214 return m_AllocationList.IsEmpty();
6215 }
6216
Register(VmaAllocation alloc)6217 void VmaDedicatedAllocationList::Register(VmaAllocation alloc)
6218 {
6219 VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
6220 m_AllocationList.PushBack(alloc);
6221 }
6222
Unregister(VmaAllocation alloc)6223 void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc)
6224 {
6225 VmaMutexLockWrite lock(m_Mutex, m_UseMutex);
6226 m_AllocationList.Remove(alloc);
6227 }
6228 #endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS
6229 #endif // _VMA_DEDICATED_ALLOCATION_LIST
6230
6231 #ifndef _VMA_SUBALLOCATION
6232 /*
6233 Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
6234 allocated memory block or free.
6235 */
6236 struct VmaSuballocation
6237 {
6238 VkDeviceSize offset;
6239 VkDeviceSize size;
6240 void* userData;
6241 VmaSuballocationType type;
6242 };
6243
6244 // Comparator for offsets.
6245 struct VmaSuballocationOffsetLess
6246 {
operatorVmaSuballocationOffsetLess6247 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6248 {
6249 return lhs.offset < rhs.offset;
6250 }
6251 };
6252
6253 struct VmaSuballocationOffsetGreater
6254 {
operatorVmaSuballocationOffsetGreater6255 bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
6256 {
6257 return lhs.offset > rhs.offset;
6258 }
6259 };
6260
6261 struct VmaSuballocationItemSizeLess
6262 {
operatorVmaSuballocationItemSizeLess6263 bool operator()(const VmaSuballocationList::iterator lhs,
6264 const VmaSuballocationList::iterator rhs) const
6265 {
6266 return lhs->size < rhs->size;
6267 }
6268
operatorVmaSuballocationItemSizeLess6269 bool operator()(const VmaSuballocationList::iterator lhs,
6270 VkDeviceSize rhsSize) const
6271 {
6272 return lhs->size < rhsSize;
6273 }
6274 };
6275 #endif // _VMA_SUBALLOCATION
6276
6277 #ifndef _VMA_ALLOCATION_REQUEST
6278 /*
6279 Parameters of planned allocation inside a VmaDeviceMemoryBlock.
6280 item points to a FREE suballocation.
6281 */
6282 struct VmaAllocationRequest
6283 {
6284 VmaAllocHandle allocHandle;
6285 VkDeviceSize size;
6286 VmaSuballocationList::iterator item;
6287 void* customData;
6288 uint64_t algorithmData;
6289 VmaAllocationRequestType type;
6290 };
6291 #endif // _VMA_ALLOCATION_REQUEST
6292
6293 #ifndef _VMA_BLOCK_METADATA
6294 /*
6295 Data structure used for bookkeeping of allocations and unused ranges of memory
6296 in a single VkDeviceMemory block.
6297 */
6298 class VmaBlockMetadata
6299 {
6300 public:
6301 // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.
6302 VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
6303 VkDeviceSize bufferImageGranularity, bool isVirtual);
6304 virtual ~VmaBlockMetadata() = default;
6305
Init(VkDeviceSize size)6306 virtual void Init(VkDeviceSize size) { m_Size = size; }
IsVirtual()6307 bool IsVirtual() const { return m_IsVirtual; }
GetSize()6308 VkDeviceSize GetSize() const { return m_Size; }
6309
6310 // Validates all data structures inside this object. If not valid, returns false.
6311 virtual bool Validate() const = 0;
6312 virtual size_t GetAllocationCount() const = 0;
6313 virtual size_t GetFreeRegionsCount() const = 0;
6314 virtual VkDeviceSize GetSumFreeSize() const = 0;
6315 // Returns true if this block is empty - contains only single free suballocation.
6316 virtual bool IsEmpty() const = 0;
6317 virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0;
6318 virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0;
6319 virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0;
6320
6321 virtual VmaAllocHandle GetAllocationListBegin() const = 0;
6322 virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0;
6323 virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0;
6324
6325 // Shouldn't modify blockCount.
6326 virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0;
6327 virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;
6328
6329 #if VMA_STATS_STRING_ENABLED
6330 virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
6331 #endif
6332
6333 // Tries to find a place for suballocation with given parameters inside this block.
6334 // If succeeded, fills pAllocationRequest and returns true.
6335 // If failed, returns false.
6336 virtual bool CreateAllocationRequest(
6337 VkDeviceSize allocSize,
6338 VkDeviceSize allocAlignment,
6339 bool upperAddress,
6340 VmaSuballocationType allocType,
6341 // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
6342 uint32_t strategy,
6343 VmaAllocationRequest* pAllocationRequest) = 0;
6344
6345 virtual VkResult CheckCorruption(const void* pBlockData) = 0;
6346
6347 // Makes actual allocation based on request. Request must already be checked and valid.
6348 virtual void Alloc(
6349 const VmaAllocationRequest& request,
6350 VmaSuballocationType type,
6351 void* userData) = 0;
6352
6353 // Frees suballocation assigned to given memory region.
6354 virtual void Free(VmaAllocHandle allocHandle) = 0;
6355
6356 // Frees all allocations.
6357 // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!
6358 virtual void Clear() = 0;
6359
6360 virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;
6361 virtual void DebugLogAllAllocations() const = 0;
6362
6363 protected:
GetAllocationCallbacks()6364 const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
GetBufferImageGranularity()6365 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetDebugMargin()6366 VkDeviceSize GetDebugMargin() const { return IsVirtual() ? 0 : VMA_DEBUG_MARGIN; }
6367
6368 void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;
6369 #if VMA_STATS_STRING_ENABLED
6370 // mapRefCount == UINT32_MAX means unspecified.
6371 void PrintDetailedMap_Begin(class VmaJsonWriter& json,
6372 VkDeviceSize unusedBytes,
6373 size_t allocationCount,
6374 size_t unusedRangeCount) const;
6375 void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6376 VkDeviceSize offset, VkDeviceSize size, void* userData) const;
6377 void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6378 VkDeviceSize offset,
6379 VkDeviceSize size) const;
6380 void PrintDetailedMap_End(class VmaJsonWriter& json) const;
6381 #endif
6382
6383 private:
6384 VkDeviceSize m_Size;
6385 const VkAllocationCallbacks* m_pAllocationCallbacks;
6386 const VkDeviceSize m_BufferImageGranularity;
6387 const bool m_IsVirtual;
6388 };
6389
6390 #ifndef _VMA_BLOCK_METADATA_FUNCTIONS
VmaBlockMetadata(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize bufferImageGranularity,bool isVirtual)6391 VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,
6392 VkDeviceSize bufferImageGranularity, bool isVirtual)
6393 : m_Size(0),
6394 m_pAllocationCallbacks(pAllocationCallbacks),
6395 m_BufferImageGranularity(bufferImageGranularity),
6396 m_IsVirtual(isVirtual) {}
6397
DebugLogAllocation(VkDeviceSize offset,VkDeviceSize size,void * userData)6398 void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const
6399 {
6400 if (IsVirtual())
6401 {
6402 VMA_DEBUG_LOG("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData);
6403 }
6404 else
6405 {
6406 VMA_ASSERT(userData != VMA_NULL);
6407 VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);
6408
6409 userData = allocation->GetUserData();
6410 const char* name = allocation->GetName();
6411
6412 #if VMA_STATS_STRING_ENABLED
6413 VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %s; Usage: %u",
6414 offset, size, userData, name ? name : "vma_empty",
6415 VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],
6416 allocation->GetBufferImageUsage());
6417 #else
6418 VMA_DEBUG_LOG("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %u",
6419 offset, size, userData, name ? name : "vma_empty",
6420 (uint32_t)allocation->GetSuballocationType());
6421 #endif // VMA_STATS_STRING_ENABLED
6422 }
6423
6424 }
6425
6426 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap_Begin(class VmaJsonWriter & json,VkDeviceSize unusedBytes,size_t allocationCount,size_t unusedRangeCount)6427 void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
6428 VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const
6429 {
6430 json.WriteString("TotalBytes");
6431 json.WriteNumber(GetSize());
6432
6433 json.WriteString("UnusedBytes");
6434 json.WriteSize(unusedBytes);
6435
6436 json.WriteString("Allocations");
6437 json.WriteSize(allocationCount);
6438
6439 json.WriteString("UnusedRanges");
6440 json.WriteSize(unusedRangeCount);
6441
6442 json.WriteString("Suballocations");
6443 json.BeginArray();
6444 }
6445
PrintDetailedMap_Allocation(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size,void * userData)6446 void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
6447 VkDeviceSize offset, VkDeviceSize size, void* userData) const
6448 {
6449 json.BeginObject(true);
6450
6451 json.WriteString("Offset");
6452 json.WriteNumber(offset);
6453
6454 if (IsVirtual())
6455 {
6456 json.WriteString("Size");
6457 json.WriteNumber(size);
6458 if (userData)
6459 {
6460 json.WriteString("CustomData");
6461 json.BeginString();
6462 json.ContinueString_Pointer(userData);
6463 json.EndString();
6464 }
6465 }
6466 else
6467 {
6468 ((VmaAllocation)userData)->PrintParameters(json);
6469 }
6470
6471 json.EndObject();
6472 }
6473
PrintDetailedMap_UnusedRange(class VmaJsonWriter & json,VkDeviceSize offset,VkDeviceSize size)6474 void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
6475 VkDeviceSize offset, VkDeviceSize size) const
6476 {
6477 json.BeginObject(true);
6478
6479 json.WriteString("Offset");
6480 json.WriteNumber(offset);
6481
6482 json.WriteString("Type");
6483 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
6484
6485 json.WriteString("Size");
6486 json.WriteNumber(size);
6487
6488 json.EndObject();
6489 }
6490
PrintDetailedMap_End(class VmaJsonWriter & json)6491 void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
6492 {
6493 json.EndArray();
6494 }
6495 #endif // VMA_STATS_STRING_ENABLED
6496 #endif // _VMA_BLOCK_METADATA_FUNCTIONS
6497 #endif // _VMA_BLOCK_METADATA
6498
6499 #ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
6500 // Before deleting object of this class remember to call 'Destroy()'
6501 class VmaBlockBufferImageGranularity final
6502 {
6503 public:
6504 struct ValidationContext
6505 {
6506 const VkAllocationCallbacks* allocCallbacks;
6507 uint16_t* pageAllocs;
6508 };
6509
6510 VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity);
6511 ~VmaBlockBufferImageGranularity();
6512
IsEnabled()6513 bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; }
6514
6515 void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size);
6516 // Before destroying object you must call free it's memory
6517 void Destroy(const VkAllocationCallbacks* pAllocationCallbacks);
6518
6519 void RoundupAllocRequest(VmaSuballocationType allocType,
6520 VkDeviceSize& inOutAllocSize,
6521 VkDeviceSize& inOutAllocAlignment) const;
6522
6523 bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
6524 VkDeviceSize allocSize,
6525 VkDeviceSize blockOffset,
6526 VkDeviceSize blockSize,
6527 VmaSuballocationType allocType) const;
6528
6529 void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size);
6530 void FreePages(VkDeviceSize offset, VkDeviceSize size);
6531 void Clear();
6532
6533 ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks,
6534 bool isVirutal) const;
6535 bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const;
6536 bool FinishValidation(ValidationContext& ctx) const;
6537
6538 private:
6539 static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256;
6540
6541 struct RegionInfo
6542 {
6543 uint8_t allocType;
6544 uint16_t allocCount;
6545 };
6546
6547 VkDeviceSize m_BufferImageGranularity;
6548 uint32_t m_RegionCount;
6549 RegionInfo* m_RegionInfo;
6550
GetStartPage(VkDeviceSize offset)6551 uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); }
GetEndPage(VkDeviceSize offset,VkDeviceSize size)6552 uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); }
6553
6554 uint32_t OffsetToPageIndex(VkDeviceSize offset) const;
6555 void AllocPage(RegionInfo& page, uint8_t allocType);
6556 };
6557
6558 #ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)6559 VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)
6560 : m_BufferImageGranularity(bufferImageGranularity),
6561 m_RegionCount(0),
6562 m_RegionInfo(VMA_NULL) {}
6563
~VmaBlockBufferImageGranularity()6564 VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity()
6565 {
6566 VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!");
6567 }
6568
Init(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize size)6569 void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size)
6570 {
6571 if (IsEnabled())
6572 {
6573 m_RegionCount = static_cast<uint32_t>(VmaDivideRoundingUp(size, m_BufferImageGranularity));
6574 m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount);
6575 memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
6576 }
6577 }
6578
Destroy(const VkAllocationCallbacks * pAllocationCallbacks)6579 void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks)
6580 {
6581 if (m_RegionInfo)
6582 {
6583 vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount);
6584 m_RegionInfo = VMA_NULL;
6585 }
6586 }
6587
RoundupAllocRequest(VmaSuballocationType allocType,VkDeviceSize & inOutAllocSize,VkDeviceSize & inOutAllocAlignment)6588 void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType,
6589 VkDeviceSize& inOutAllocSize,
6590 VkDeviceSize& inOutAllocAlignment) const
6591 {
6592 if (m_BufferImageGranularity > 1 &&
6593 m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY)
6594 {
6595 if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
6596 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
6597 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
6598 {
6599 inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity);
6600 inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity);
6601 }
6602 }
6603 }
6604
CheckConflictAndAlignUp(VkDeviceSize & inOutAllocOffset,VkDeviceSize allocSize,VkDeviceSize blockOffset,VkDeviceSize blockSize,VmaSuballocationType allocType)6605 bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,
6606 VkDeviceSize allocSize,
6607 VkDeviceSize blockOffset,
6608 VkDeviceSize blockSize,
6609 VmaSuballocationType allocType) const
6610 {
6611 if (IsEnabled())
6612 {
6613 uint32_t startPage = GetStartPage(inOutAllocOffset);
6614 if (m_RegionInfo[startPage].allocCount > 0 &&
6615 VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[startPage].allocType), allocType))
6616 {
6617 inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity);
6618 if (blockSize < allocSize + inOutAllocOffset - blockOffset)
6619 return true;
6620 ++startPage;
6621 }
6622 uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize);
6623 if (endPage != startPage &&
6624 m_RegionInfo[endPage].allocCount > 0 &&
6625 VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[endPage].allocType), allocType))
6626 {
6627 return true;
6628 }
6629 }
6630 return false;
6631 }
6632
AllocPages(uint8_t allocType,VkDeviceSize offset,VkDeviceSize size)6633 void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size)
6634 {
6635 if (IsEnabled())
6636 {
6637 uint32_t startPage = GetStartPage(offset);
6638 AllocPage(m_RegionInfo[startPage], allocType);
6639
6640 uint32_t endPage = GetEndPage(offset, size);
6641 if (startPage != endPage)
6642 AllocPage(m_RegionInfo[endPage], allocType);
6643 }
6644 }
6645
FreePages(VkDeviceSize offset,VkDeviceSize size)6646 void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size)
6647 {
6648 if (IsEnabled())
6649 {
6650 uint32_t startPage = GetStartPage(offset);
6651 --m_RegionInfo[startPage].allocCount;
6652 if (m_RegionInfo[startPage].allocCount == 0)
6653 m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
6654 uint32_t endPage = GetEndPage(offset, size);
6655 if (startPage != endPage)
6656 {
6657 --m_RegionInfo[endPage].allocCount;
6658 if (m_RegionInfo[endPage].allocCount == 0)
6659 m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;
6660 }
6661 }
6662 }
6663
Clear()6664 void VmaBlockBufferImageGranularity::Clear()
6665 {
6666 if (m_RegionInfo)
6667 memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));
6668 }
6669
StartValidation(const VkAllocationCallbacks * pAllocationCallbacks,bool isVirutal)6670 VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation(
6671 const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const
6672 {
6673 ValidationContext ctx{ pAllocationCallbacks, VMA_NULL };
6674 if (!isVirutal && IsEnabled())
6675 {
6676 ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount);
6677 memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t));
6678 }
6679 return ctx;
6680 }
6681
Validate(ValidationContext & ctx,VkDeviceSize offset,VkDeviceSize size)6682 bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx,
6683 VkDeviceSize offset, VkDeviceSize size) const
6684 {
6685 if (IsEnabled())
6686 {
6687 uint32_t start = GetStartPage(offset);
6688 ++ctx.pageAllocs[start];
6689 VMA_VALIDATE(m_RegionInfo[start].allocCount > 0);
6690
6691 uint32_t end = GetEndPage(offset, size);
6692 if (start != end)
6693 {
6694 ++ctx.pageAllocs[end];
6695 VMA_VALIDATE(m_RegionInfo[end].allocCount > 0);
6696 }
6697 }
6698 return true;
6699 }
6700
FinishValidation(ValidationContext & ctx)6701 bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const
6702 {
6703 // Check proper page structure
6704 if (IsEnabled())
6705 {
6706 VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!");
6707
6708 for (uint32_t page = 0; page < m_RegionCount; ++page)
6709 {
6710 VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount);
6711 }
6712 vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount);
6713 ctx.pageAllocs = VMA_NULL;
6714 }
6715 return true;
6716 }
6717
OffsetToPageIndex(VkDeviceSize offset)6718 uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const
6719 {
6720 return static_cast<uint32_t>(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity));
6721 }
6722
AllocPage(RegionInfo & page,uint8_t allocType)6723 void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType)
6724 {
6725 // When current alloc type is free then it can be overriden by new type
6726 if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE))
6727 page.allocType = allocType;
6728
6729 ++page.allocCount;
6730 }
6731 #endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS
6732 #endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY
6733
6734 #if 0
6735 #ifndef _VMA_BLOCK_METADATA_GENERIC
6736 class VmaBlockMetadata_Generic : public VmaBlockMetadata
6737 {
6738 friend class VmaDefragmentationAlgorithm_Generic;
6739 friend class VmaDefragmentationAlgorithm_Fast;
6740 VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
6741 public:
6742 VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,
6743 VkDeviceSize bufferImageGranularity, bool isVirtual);
6744 virtual ~VmaBlockMetadata_Generic() = default;
6745
6746 size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }
6747 VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
6748 bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); }
6749 void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle - 1)); }
6750 VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; };
6751
6752 void Init(VkDeviceSize size) override;
6753 bool Validate() const override;
6754
6755 void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
6756 void AddStatistics(VmaStatistics& inoutStats) const override;
6757
6758 void FreeEmptyBlock();
6759
6760 #if VMA_STATS_STRING_ENABLED
6761 void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
6762 #endif
6763
6764 bool CreateAllocationRequest(
6765 VkDeviceSize allocSize,
6766 VkDeviceSize allocAlignment,
6767 bool upperAddress,
6768 VmaSuballocationType allocType,
6769 uint32_t strategy,
6770 VmaAllocationRequest* pAllocationRequest) override;
6771
6772 VkResult CheckCorruption(const void* pBlockData) override;
6773
6774 void Alloc(
6775 const VmaAllocationRequest& request,
6776 VmaSuballocationType type,
6777 void* userData) override;
6778
6779 void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
6780 void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
6781 VmaAllocHandle GetAllocationListBegin() const override;
6782 VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
6783 void Clear() override;
6784 void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
6785 void DebugLogAllAllocations() const override;
6786
6787 private:
6788 uint32_t m_FreeCount;
6789 VkDeviceSize m_SumFreeSize;
6790 VmaSuballocationList m_Suballocations;
6791 // Suballocations that are free. Sorted by size, ascending.
6792 VmaVector<VmaSuballocationList::iterator, VmaStlAllocator<VmaSuballocationList::iterator>> m_FreeSuballocationsBySize;
6793
6794 VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); }
6795
6796 VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset) const;
6797 bool ValidateFreeSuballocationList() const;
6798
6799 // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
6800 // If yes, fills pOffset and returns true. If no, returns false.
6801 bool CheckAllocation(
6802 VkDeviceSize allocSize,
6803 VkDeviceSize allocAlignment,
6804 VmaSuballocationType allocType,
6805 VmaSuballocationList::const_iterator suballocItem,
6806 VmaAllocHandle* pAllocHandle) const;
6807
6808 // Given free suballocation, it merges it with following one, which must also be free.
6809 void MergeFreeWithNext(VmaSuballocationList::iterator item);
6810 // Releases given suballocation, making it free.
6811 // Merges it with adjacent free suballocations if applicable.
6812 // Returns iterator to new free suballocation at this place.
6813 VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
6814 // Given free suballocation, it inserts it into sorted list of
6815 // m_FreeSuballocationsBySize if it is suitable.
6816 void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
6817 // Given free suballocation, it removes it from sorted list of
6818 // m_FreeSuballocationsBySize if it is suitable.
6819 void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
6820 };
6821
6822 #ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS
6823 VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,
6824 VkDeviceSize bufferImageGranularity, bool isVirtual)
6825 : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
6826 m_FreeCount(0),
6827 m_SumFreeSize(0),
6828 m_Suballocations(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
6829 m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(pAllocationCallbacks)) {}
6830
6831 void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
6832 {
6833 VmaBlockMetadata::Init(size);
6834
6835 m_FreeCount = 1;
6836 m_SumFreeSize = size;
6837
6838 VmaSuballocation suballoc = {};
6839 suballoc.offset = 0;
6840 suballoc.size = size;
6841 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
6842
6843 m_Suballocations.push_back(suballoc);
6844 m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
6845 }
6846
6847 bool VmaBlockMetadata_Generic::Validate() const
6848 {
6849 VMA_VALIDATE(!m_Suballocations.empty());
6850
6851 // Expected offset of new suballocation as calculated from previous ones.
6852 VkDeviceSize calculatedOffset = 0;
6853 // Expected number of free suballocations as calculated from traversing their list.
6854 uint32_t calculatedFreeCount = 0;
6855 // Expected sum size of free suballocations as calculated from traversing their list.
6856 VkDeviceSize calculatedSumFreeSize = 0;
6857 // Expected number of free suballocations that should be registered in
6858 // m_FreeSuballocationsBySize calculated from traversing their list.
6859 size_t freeSuballocationsToRegister = 0;
6860 // True if previous visited suballocation was free.
6861 bool prevFree = false;
6862
6863 const VkDeviceSize debugMargin = GetDebugMargin();
6864
6865 for (const auto& subAlloc : m_Suballocations)
6866 {
6867 // Actual offset of this suballocation doesn't match expected one.
6868 VMA_VALIDATE(subAlloc.offset == calculatedOffset);
6869
6870 const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
6871 // Two adjacent free suballocations are invalid. They should be merged.
6872 VMA_VALIDATE(!prevFree || !currFree);
6873
6874 VmaAllocation alloc = (VmaAllocation)subAlloc.userData;
6875 if (!IsVirtual())
6876 {
6877 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
6878 }
6879
6880 if (currFree)
6881 {
6882 calculatedSumFreeSize += subAlloc.size;
6883 ++calculatedFreeCount;
6884 ++freeSuballocationsToRegister;
6885
6886 // Margin required between allocations - every free space must be at least that large.
6887 VMA_VALIDATE(subAlloc.size >= debugMargin);
6888 }
6889 else
6890 {
6891 if (!IsVirtual())
6892 {
6893 VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset + 1);
6894 VMA_VALIDATE(alloc->GetSize() == subAlloc.size);
6895 }
6896
6897 // Margin required between allocations - previous allocation must be free.
6898 VMA_VALIDATE(debugMargin == 0 || prevFree);
6899 }
6900
6901 calculatedOffset += subAlloc.size;
6902 prevFree = currFree;
6903 }
6904
6905 // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
6906 // match expected one.
6907 VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
6908
6909 VkDeviceSize lastSize = 0;
6910 for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
6911 {
6912 VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
6913
6914 // Only free suballocations can be registered in m_FreeSuballocationsBySize.
6915 VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
6916 // They must be sorted by size ascending.
6917 VMA_VALIDATE(suballocItem->size >= lastSize);
6918
6919 lastSize = suballocItem->size;
6920 }
6921
6922 // Check if totals match calculated values.
6923 VMA_VALIDATE(ValidateFreeSuballocationList());
6924 VMA_VALIDATE(calculatedOffset == GetSize());
6925 VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
6926 VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
6927
6928 return true;
6929 }
6930
6931 void VmaBlockMetadata_Generic::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
6932 {
6933 const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
6934 inoutStats.statistics.blockCount++;
6935 inoutStats.statistics.blockBytes += GetSize();
6936
6937 for (const auto& suballoc : m_Suballocations)
6938 {
6939 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
6940 VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
6941 else
6942 VmaAddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);
6943 }
6944 }
6945
6946 void VmaBlockMetadata_Generic::AddStatistics(VmaStatistics& inoutStats) const
6947 {
6948 inoutStats.blockCount++;
6949 inoutStats.allocationCount += (uint32_t)m_Suballocations.size() - m_FreeCount;
6950 inoutStats.blockBytes += GetSize();
6951 inoutStats.allocationBytes += GetSize() - m_SumFreeSize;
6952 }
6953
6954 #if VMA_STATS_STRING_ENABLED
6955 void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
6956 {
6957 PrintDetailedMap_Begin(json,
6958 m_SumFreeSize, // unusedBytes
6959 m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
6960 m_FreeCount, // unusedRangeCount
6961 mapRefCount);
6962
6963 for (const auto& suballoc : m_Suballocations)
6964 {
6965 if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
6966 {
6967 PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);
6968 }
6969 else
6970 {
6971 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
6972 }
6973 }
6974
6975 PrintDetailedMap_End(json);
6976 }
6977 #endif // VMA_STATS_STRING_ENABLED
6978
6979 bool VmaBlockMetadata_Generic::CreateAllocationRequest(
6980 VkDeviceSize allocSize,
6981 VkDeviceSize allocAlignment,
6982 bool upperAddress,
6983 VmaSuballocationType allocType,
6984 uint32_t strategy,
6985 VmaAllocationRequest* pAllocationRequest)
6986 {
6987 VMA_ASSERT(allocSize > 0);
6988 VMA_ASSERT(!upperAddress);
6989 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
6990 VMA_ASSERT(pAllocationRequest != VMA_NULL);
6991 VMA_HEAVY_ASSERT(Validate());
6992
6993 allocSize = AlignAllocationSize(allocSize);
6994
6995 pAllocationRequest->type = VmaAllocationRequestType::Normal;
6996 pAllocationRequest->size = allocSize;
6997
6998 const VkDeviceSize debugMargin = GetDebugMargin();
6999
7000 // There is not enough total free space in this block to fulfill the request: Early return.
7001 if (m_SumFreeSize < allocSize + debugMargin)
7002 {
7003 return false;
7004 }
7005
7006 // New algorithm, efficiently searching freeSuballocationsBySize.
7007 const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
7008 if (freeSuballocCount > 0)
7009 {
7010 if (strategy == 0 ||
7011 strategy == VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
7012 {
7013 // Find first free suballocation with size not less than allocSize + debugMargin.
7014 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7015 m_FreeSuballocationsBySize.data(),
7016 m_FreeSuballocationsBySize.data() + freeSuballocCount,
7017 allocSize + debugMargin,
7018 VmaSuballocationItemSizeLess());
7019 size_t index = it - m_FreeSuballocationsBySize.data();
7020 for (; index < freeSuballocCount; ++index)
7021 {
7022 if (CheckAllocation(
7023 allocSize,
7024 allocAlignment,
7025 allocType,
7026 m_FreeSuballocationsBySize[index],
7027 &pAllocationRequest->allocHandle))
7028 {
7029 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7030 return true;
7031 }
7032 }
7033 }
7034 else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
7035 {
7036 for (VmaSuballocationList::iterator it = m_Suballocations.begin();
7037 it != m_Suballocations.end();
7038 ++it)
7039 {
7040 if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
7041 allocSize,
7042 allocAlignment,
7043 allocType,
7044 it,
7045 &pAllocationRequest->allocHandle))
7046 {
7047 pAllocationRequest->item = it;
7048 return true;
7049 }
7050 }
7051 }
7052 else
7053 {
7054 VMA_ASSERT(strategy & (VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ));
7055 // Search staring from biggest suballocations.
7056 for (size_t index = freeSuballocCount; index--; )
7057 {
7058 if (CheckAllocation(
7059 allocSize,
7060 allocAlignment,
7061 allocType,
7062 m_FreeSuballocationsBySize[index],
7063 &pAllocationRequest->allocHandle))
7064 {
7065 pAllocationRequest->item = m_FreeSuballocationsBySize[index];
7066 return true;
7067 }
7068 }
7069 }
7070 }
7071
7072 return false;
7073 }
7074
7075 VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
7076 {
7077 for (auto& suballoc : m_Suballocations)
7078 {
7079 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7080 {
7081 if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
7082 {
7083 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
7084 return VK_ERROR_UNKNOWN_COPY;
7085 }
7086 }
7087 }
7088
7089 return VK_SUCCESS;
7090 }
7091
7092 void VmaBlockMetadata_Generic::Alloc(
7093 const VmaAllocationRequest& request,
7094 VmaSuballocationType type,
7095 void* userData)
7096 {
7097 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
7098 VMA_ASSERT(request.item != m_Suballocations.end());
7099 VmaSuballocation& suballoc = *request.item;
7100 // Given suballocation is a free block.
7101 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7102
7103 // Given offset is inside this suballocation.
7104 VMA_ASSERT((VkDeviceSize)request.allocHandle - 1 >= suballoc.offset);
7105 const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset - 1;
7106 VMA_ASSERT(suballoc.size >= paddingBegin + request.size);
7107 const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size;
7108
7109 // Unregister this free suballocation from m_FreeSuballocationsBySize and update
7110 // it to become used.
7111 UnregisterFreeSuballocation(request.item);
7112
7113 suballoc.offset = (VkDeviceSize)request.allocHandle - 1;
7114 suballoc.size = request.size;
7115 suballoc.type = type;
7116 suballoc.userData = userData;
7117
7118 // If there are any free bytes remaining at the end, insert new free suballocation after current one.
7119 if (paddingEnd)
7120 {
7121 VmaSuballocation paddingSuballoc = {};
7122 paddingSuballoc.offset = suballoc.offset + suballoc.size;
7123 paddingSuballoc.size = paddingEnd;
7124 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7125 VmaSuballocationList::iterator next = request.item;
7126 ++next;
7127 const VmaSuballocationList::iterator paddingEndItem =
7128 m_Suballocations.insert(next, paddingSuballoc);
7129 RegisterFreeSuballocation(paddingEndItem);
7130 }
7131
7132 // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
7133 if (paddingBegin)
7134 {
7135 VmaSuballocation paddingSuballoc = {};
7136 paddingSuballoc.offset = suballoc.offset - paddingBegin;
7137 paddingSuballoc.size = paddingBegin;
7138 paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7139 const VmaSuballocationList::iterator paddingBeginItem =
7140 m_Suballocations.insert(request.item, paddingSuballoc);
7141 RegisterFreeSuballocation(paddingBeginItem);
7142 }
7143
7144 // Update totals.
7145 m_FreeCount = m_FreeCount - 1;
7146 if (paddingBegin > 0)
7147 {
7148 ++m_FreeCount;
7149 }
7150 if (paddingEnd > 0)
7151 {
7152 ++m_FreeCount;
7153 }
7154 m_SumFreeSize -= request.size;
7155 }
7156
7157 void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
7158 {
7159 outInfo.offset = (VkDeviceSize)allocHandle - 1;
7160 const VmaSuballocation& suballoc = *FindAtOffset(outInfo.offset);
7161 outInfo.size = suballoc.size;
7162 outInfo.pUserData = suballoc.userData;
7163 }
7164
7165 void* VmaBlockMetadata_Generic::GetAllocationUserData(VmaAllocHandle allocHandle) const
7166 {
7167 return FindAtOffset((VkDeviceSize)allocHandle - 1)->userData;
7168 }
7169
7170 VmaAllocHandle VmaBlockMetadata_Generic::GetAllocationListBegin() const
7171 {
7172 if (IsEmpty())
7173 return VK_NULL_HANDLE;
7174
7175 for (const auto& suballoc : m_Suballocations)
7176 {
7177 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7178 return (VmaAllocHandle)(suballoc.offset + 1);
7179 }
7180 VMA_ASSERT(false && "Should contain at least 1 allocation!");
7181 return VK_NULL_HANDLE;
7182 }
7183
7184 VmaAllocHandle VmaBlockMetadata_Generic::GetNextAllocation(VmaAllocHandle prevAlloc) const
7185 {
7186 VmaSuballocationList::const_iterator prev = FindAtOffset((VkDeviceSize)prevAlloc - 1);
7187
7188 for (VmaSuballocationList::const_iterator it = ++prev; it != m_Suballocations.end(); ++it)
7189 {
7190 if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
7191 return (VmaAllocHandle)(it->offset + 1);
7192 }
7193 return VK_NULL_HANDLE;
7194 }
7195
7196 void VmaBlockMetadata_Generic::Clear()
7197 {
7198 const VkDeviceSize size = GetSize();
7199
7200 VMA_ASSERT(IsVirtual());
7201 m_FreeCount = 1;
7202 m_SumFreeSize = size;
7203 m_Suballocations.clear();
7204 m_FreeSuballocationsBySize.clear();
7205
7206 VmaSuballocation suballoc = {};
7207 suballoc.offset = 0;
7208 suballoc.size = size;
7209 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7210 m_Suballocations.push_back(suballoc);
7211
7212 m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());
7213 }
7214
7215 void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
7216 {
7217 VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle - 1);
7218 suballoc.userData = userData;
7219 }
7220
7221 void VmaBlockMetadata_Generic::DebugLogAllAllocations() const
7222 {
7223 for (const auto& suballoc : m_Suballocations)
7224 {
7225 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
7226 DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData);
7227 }
7228 }
7229
7230 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) const
7231 {
7232 VMA_HEAVY_ASSERT(!m_Suballocations.empty());
7233 const VkDeviceSize last = m_Suballocations.rbegin()->offset;
7234 if (last == offset)
7235 return m_Suballocations.rbegin().drop_const();
7236 const VkDeviceSize first = m_Suballocations.begin()->offset;
7237 if (first == offset)
7238 return m_Suballocations.begin().drop_const();
7239
7240 const size_t suballocCount = m_Suballocations.size();
7241 const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount;
7242 auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator
7243 {
7244 for (auto suballocItem = begin;
7245 suballocItem != end;
7246 ++suballocItem)
7247 {
7248 if (suballocItem->offset == offset)
7249 return suballocItem.drop_const();
7250 }
7251 VMA_ASSERT(false && "Not found!");
7252 return m_Suballocations.end().drop_const();
7253 };
7254 // If requested offset is closer to the end of range, search from the end
7255 if (offset - first > suballocCount * step / 2)
7256 {
7257 return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend());
7258 }
7259 return findSuballocation(m_Suballocations.begin(), m_Suballocations.end());
7260 }
7261
7262 bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
7263 {
7264 VkDeviceSize lastSize = 0;
7265 for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
7266 {
7267 const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
7268
7269 VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
7270 VMA_VALIDATE(it->size >= lastSize);
7271 lastSize = it->size;
7272 }
7273 return true;
7274 }
7275
7276 bool VmaBlockMetadata_Generic::CheckAllocation(
7277 VkDeviceSize allocSize,
7278 VkDeviceSize allocAlignment,
7279 VmaSuballocationType allocType,
7280 VmaSuballocationList::const_iterator suballocItem,
7281 VmaAllocHandle* pAllocHandle) const
7282 {
7283 VMA_ASSERT(allocSize > 0);
7284 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
7285 VMA_ASSERT(suballocItem != m_Suballocations.cend());
7286 VMA_ASSERT(pAllocHandle != VMA_NULL);
7287
7288 const VkDeviceSize debugMargin = GetDebugMargin();
7289 const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
7290
7291 const VmaSuballocation& suballoc = *suballocItem;
7292 VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7293
7294 // Size of this suballocation is too small for this request: Early return.
7295 if (suballoc.size < allocSize)
7296 {
7297 return false;
7298 }
7299
7300 // Start from offset equal to beginning of this suballocation.
7301 VkDeviceSize offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());
7302
7303 // Apply debugMargin from the end of previous alloc.
7304 if (debugMargin > 0)
7305 {
7306 offset += debugMargin;
7307 }
7308
7309 // Apply alignment.
7310 offset = VmaAlignUp(offset, allocAlignment);
7311
7312 // Check previous suballocations for BufferImageGranularity conflicts.
7313 // Make bigger alignment if necessary.
7314 if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
7315 {
7316 bool bufferImageGranularityConflict = false;
7317 VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
7318 while (prevSuballocItem != m_Suballocations.cbegin())
7319 {
7320 --prevSuballocItem;
7321 const VmaSuballocation& prevSuballoc = *prevSuballocItem;
7322 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity))
7323 {
7324 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
7325 {
7326 bufferImageGranularityConflict = true;
7327 break;
7328 }
7329 }
7330 else
7331 // Already on previous page.
7332 break;
7333 }
7334 if (bufferImageGranularityConflict)
7335 {
7336 offset = VmaAlignUp(offset, bufferImageGranularity);
7337 }
7338 }
7339
7340 // Calculate padding at the beginning based on current offset.
7341 const VkDeviceSize paddingBegin = offset - suballoc.offset;
7342
7343 // Fail if requested size plus margin after is bigger than size of this suballocation.
7344 if (paddingBegin + allocSize + debugMargin > suballoc.size)
7345 {
7346 return false;
7347 }
7348
7349 // Check next suballocations for BufferImageGranularity conflicts.
7350 // If conflict exists, allocation cannot be made here.
7351 if (allocSize % bufferImageGranularity || offset % bufferImageGranularity)
7352 {
7353 VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
7354 ++nextSuballocItem;
7355 while (nextSuballocItem != m_Suballocations.cend())
7356 {
7357 const VmaSuballocation& nextSuballoc = *nextSuballocItem;
7358 if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity))
7359 {
7360 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
7361 {
7362 return false;
7363 }
7364 }
7365 else
7366 {
7367 // Already on next page.
7368 break;
7369 }
7370 ++nextSuballocItem;
7371 }
7372 }
7373
7374 *pAllocHandle = (VmaAllocHandle)(offset + 1);
7375 // All tests passed: Success. pAllocHandle is already filled.
7376 return true;
7377 }
7378
7379 void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
7380 {
7381 VMA_ASSERT(item != m_Suballocations.end());
7382 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
7383
7384 VmaSuballocationList::iterator nextItem = item;
7385 ++nextItem;
7386 VMA_ASSERT(nextItem != m_Suballocations.end());
7387 VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
7388
7389 item->size += nextItem->size;
7390 --m_FreeCount;
7391 m_Suballocations.erase(nextItem);
7392 }
7393
7394 VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
7395 {
7396 // Change this suballocation to be marked as free.
7397 VmaSuballocation& suballoc = *suballocItem;
7398 suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
7399 suballoc.userData = VMA_NULL;
7400
7401 // Update totals.
7402 ++m_FreeCount;
7403 m_SumFreeSize += suballoc.size;
7404
7405 // Merge with previous and/or next suballocation if it's also free.
7406 bool mergeWithNext = false;
7407 bool mergeWithPrev = false;
7408
7409 VmaSuballocationList::iterator nextItem = suballocItem;
7410 ++nextItem;
7411 if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
7412 {
7413 mergeWithNext = true;
7414 }
7415
7416 VmaSuballocationList::iterator prevItem = suballocItem;
7417 if (suballocItem != m_Suballocations.begin())
7418 {
7419 --prevItem;
7420 if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
7421 {
7422 mergeWithPrev = true;
7423 }
7424 }
7425
7426 if (mergeWithNext)
7427 {
7428 UnregisterFreeSuballocation(nextItem);
7429 MergeFreeWithNext(suballocItem);
7430 }
7431
7432 if (mergeWithPrev)
7433 {
7434 UnregisterFreeSuballocation(prevItem);
7435 MergeFreeWithNext(prevItem);
7436 RegisterFreeSuballocation(prevItem);
7437 return prevItem;
7438 }
7439 else
7440 {
7441 RegisterFreeSuballocation(suballocItem);
7442 return suballocItem;
7443 }
7444 }
7445
7446 void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
7447 {
7448 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
7449 VMA_ASSERT(item->size > 0);
7450
7451 // You may want to enable this validation at the beginning or at the end of
7452 // this function, depending on what do you want to check.
7453 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7454
7455 if (m_FreeSuballocationsBySize.empty())
7456 {
7457 m_FreeSuballocationsBySize.push_back(item);
7458 }
7459 else
7460 {
7461 VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
7462 }
7463
7464 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7465 }
7466
7467 void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
7468 {
7469 VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
7470 VMA_ASSERT(item->size > 0);
7471
7472 // You may want to enable this validation at the beginning or at the end of
7473 // this function, depending on what do you want to check.
7474 VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7475
7476 VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
7477 m_FreeSuballocationsBySize.data(),
7478 m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
7479 item,
7480 VmaSuballocationItemSizeLess());
7481 for (size_t index = it - m_FreeSuballocationsBySize.data();
7482 index < m_FreeSuballocationsBySize.size();
7483 ++index)
7484 {
7485 if (m_FreeSuballocationsBySize[index] == item)
7486 {
7487 VmaVectorRemove(m_FreeSuballocationsBySize, index);
7488 return;
7489 }
7490 VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
7491 }
7492 VMA_ASSERT(0 && "Not found.");
7493
7494 //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
7495 }
7496 #endif // _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS
7497 #endif // _VMA_BLOCK_METADATA_GENERIC
7498 #endif // #if 0
7499
7500 #ifndef _VMA_BLOCK_METADATA_LINEAR
7501 /*
7502 Allocations and their references in internal data structure look like this:
7503
7504 if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
7505
7506 0 +-------+
7507 | |
7508 | |
7509 | |
7510 +-------+
7511 | Alloc | 1st[m_1stNullItemsBeginCount]
7512 +-------+
7513 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
7514 +-------+
7515 | ... |
7516 +-------+
7517 | Alloc | 1st[1st.size() - 1]
7518 +-------+
7519 | |
7520 | |
7521 | |
7522 GetSize() +-------+
7523
7524 if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
7525
7526 0 +-------+
7527 | Alloc | 2nd[0]
7528 +-------+
7529 | Alloc | 2nd[1]
7530 +-------+
7531 | ... |
7532 +-------+
7533 | Alloc | 2nd[2nd.size() - 1]
7534 +-------+
7535 | |
7536 | |
7537 | |
7538 +-------+
7539 | Alloc | 1st[m_1stNullItemsBeginCount]
7540 +-------+
7541 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
7542 +-------+
7543 | ... |
7544 +-------+
7545 | Alloc | 1st[1st.size() - 1]
7546 +-------+
7547 | |
7548 GetSize() +-------+
7549
7550 if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
7551
7552 0 +-------+
7553 | |
7554 | |
7555 | |
7556 +-------+
7557 | Alloc | 1st[m_1stNullItemsBeginCount]
7558 +-------+
7559 | Alloc | 1st[m_1stNullItemsBeginCount + 1]
7560 +-------+
7561 | ... |
7562 +-------+
7563 | Alloc | 1st[1st.size() - 1]
7564 +-------+
7565 | |
7566 | |
7567 | |
7568 +-------+
7569 | Alloc | 2nd[2nd.size() - 1]
7570 +-------+
7571 | ... |
7572 +-------+
7573 | Alloc | 2nd[1]
7574 +-------+
7575 | Alloc | 2nd[0]
7576 GetSize() +-------+
7577
7578 */
7579 class VmaBlockMetadata_Linear : public VmaBlockMetadata
7580 {
7581 VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
7582 public:
7583 VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
7584 VkDeviceSize bufferImageGranularity, bool isVirtual);
7585 virtual ~VmaBlockMetadata_Linear() = default;
7586
GetSumFreeSize()7587 VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }
IsEmpty()7588 bool IsEmpty() const override { return GetAllocationCount() == 0; }
GetAllocationOffset(VmaAllocHandle allocHandle)7589 VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; };
7590
7591 void Init(VkDeviceSize size) override;
7592 bool Validate() const override;
7593 size_t GetAllocationCount() const override;
7594 size_t GetFreeRegionsCount() const override;
7595
7596 void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
7597 void AddStatistics(VmaStatistics& inoutStats) const override;
7598
7599 #if VMA_STATS_STRING_ENABLED
7600 void PrintDetailedMap(class VmaJsonWriter& json) const override;
7601 #endif
7602
7603 bool CreateAllocationRequest(
7604 VkDeviceSize allocSize,
7605 VkDeviceSize allocAlignment,
7606 bool upperAddress,
7607 VmaSuballocationType allocType,
7608 uint32_t strategy,
7609 VmaAllocationRequest* pAllocationRequest) override;
7610
7611 VkResult CheckCorruption(const void* pBlockData) override;
7612
7613 void Alloc(
7614 const VmaAllocationRequest& request,
7615 VmaSuballocationType type,
7616 void* userData) override;
7617
7618 void Free(VmaAllocHandle allocHandle) override;
7619 void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
7620 void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
7621 VmaAllocHandle GetAllocationListBegin() const override;
7622 VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
7623 VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
7624 void Clear() override;
7625 void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
7626 void DebugLogAllAllocations() const override;
7627
7628 private:
7629 /*
7630 There are two suballocation vectors, used in ping-pong way.
7631 The one with index m_1stVectorIndex is called 1st.
7632 The one with index (m_1stVectorIndex ^ 1) is called 2nd.
7633 2nd can be non-empty only when 1st is not empty.
7634 When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
7635 */
7636 typedef VmaVector<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> SuballocationVectorType;
7637
7638 enum SECOND_VECTOR_MODE
7639 {
7640 SECOND_VECTOR_EMPTY,
7641 /*
7642 Suballocations in 2nd vector are created later than the ones in 1st, but they
7643 all have smaller offset.
7644 */
7645 SECOND_VECTOR_RING_BUFFER,
7646 /*
7647 Suballocations in 2nd vector are upper side of double stack.
7648 They all have offsets higher than those in 1st vector.
7649 Top of this stack means smaller offsets, but higher indices in this vector.
7650 */
7651 SECOND_VECTOR_DOUBLE_STACK,
7652 };
7653
7654 VkDeviceSize m_SumFreeSize;
7655 SuballocationVectorType m_Suballocations0, m_Suballocations1;
7656 uint32_t m_1stVectorIndex;
7657 SECOND_VECTOR_MODE m_2ndVectorMode;
7658 // Number of items in 1st vector with hAllocation = null at the beginning.
7659 size_t m_1stNullItemsBeginCount;
7660 // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
7661 size_t m_1stNullItemsMiddleCount;
7662 // Number of items in 2nd vector with hAllocation = null.
7663 size_t m_2ndNullItemsCount;
7664
AccessSuballocations1st()7665 SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()7666 SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
AccessSuballocations1st()7667 const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
AccessSuballocations2nd()7668 const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
7669
7670 VmaSuballocation& FindSuballocation(VkDeviceSize offset) const;
7671 bool ShouldCompact1st() const;
7672 void CleanupAfterFree();
7673
7674 bool CreateAllocationRequest_LowerAddress(
7675 VkDeviceSize allocSize,
7676 VkDeviceSize allocAlignment,
7677 VmaSuballocationType allocType,
7678 uint32_t strategy,
7679 VmaAllocationRequest* pAllocationRequest);
7680 bool CreateAllocationRequest_UpperAddress(
7681 VkDeviceSize allocSize,
7682 VkDeviceSize allocAlignment,
7683 VmaSuballocationType allocType,
7684 uint32_t strategy,
7685 VmaAllocationRequest* pAllocationRequest);
7686 };
7687
7688 #ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
VmaBlockMetadata_Linear(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize bufferImageGranularity,bool isVirtual)7689 VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,
7690 VkDeviceSize bufferImageGranularity, bool isVirtual)
7691 : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
7692 m_SumFreeSize(0),
7693 m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7694 m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),
7695 m_1stVectorIndex(0),
7696 m_2ndVectorMode(SECOND_VECTOR_EMPTY),
7697 m_1stNullItemsBeginCount(0),
7698 m_1stNullItemsMiddleCount(0),
7699 m_2ndNullItemsCount(0) {}
7700
Init(VkDeviceSize size)7701 void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
7702 {
7703 VmaBlockMetadata::Init(size);
7704 m_SumFreeSize = size;
7705 }
7706
Validate()7707 bool VmaBlockMetadata_Linear::Validate() const
7708 {
7709 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7710 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7711
7712 VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
7713 VMA_VALIDATE(!suballocations1st.empty() ||
7714 suballocations2nd.empty() ||
7715 m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
7716
7717 if (!suballocations1st.empty())
7718 {
7719 // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
7720 VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);
7721 // Null item at the end should be just pop_back().
7722 VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);
7723 }
7724 if (!suballocations2nd.empty())
7725 {
7726 // Null item at the end should be just pop_back().
7727 VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);
7728 }
7729
7730 VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
7731 VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
7732
7733 VkDeviceSize sumUsedSize = 0;
7734 const size_t suballoc1stCount = suballocations1st.size();
7735 const VkDeviceSize debugMargin = GetDebugMargin();
7736 VkDeviceSize offset = 0;
7737
7738 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7739 {
7740 const size_t suballoc2ndCount = suballocations2nd.size();
7741 size_t nullItem2ndCount = 0;
7742 for (size_t i = 0; i < suballoc2ndCount; ++i)
7743 {
7744 const VmaSuballocation& suballoc = suballocations2nd[i];
7745 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7746
7747 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7748 if (!IsVirtual())
7749 {
7750 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7751 }
7752 VMA_VALIDATE(suballoc.offset >= offset);
7753
7754 if (!currFree)
7755 {
7756 if (!IsVirtual())
7757 {
7758 VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7759 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7760 }
7761 sumUsedSize += suballoc.size;
7762 }
7763 else
7764 {
7765 ++nullItem2ndCount;
7766 }
7767
7768 offset = suballoc.offset + suballoc.size + debugMargin;
7769 }
7770
7771 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
7772 }
7773
7774 for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
7775 {
7776 const VmaSuballocation& suballoc = suballocations1st[i];
7777 VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
7778 suballoc.userData == VMA_NULL);
7779 }
7780
7781 size_t nullItem1stCount = m_1stNullItemsBeginCount;
7782
7783 for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
7784 {
7785 const VmaSuballocation& suballoc = suballocations1st[i];
7786 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7787
7788 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7789 if (!IsVirtual())
7790 {
7791 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7792 }
7793 VMA_VALIDATE(suballoc.offset >= offset);
7794 VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
7795
7796 if (!currFree)
7797 {
7798 if (!IsVirtual())
7799 {
7800 VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7801 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7802 }
7803 sumUsedSize += suballoc.size;
7804 }
7805 else
7806 {
7807 ++nullItem1stCount;
7808 }
7809
7810 offset = suballoc.offset + suballoc.size + debugMargin;
7811 }
7812 VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
7813
7814 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7815 {
7816 const size_t suballoc2ndCount = suballocations2nd.size();
7817 size_t nullItem2ndCount = 0;
7818 for (size_t i = suballoc2ndCount; i--; )
7819 {
7820 const VmaSuballocation& suballoc = suballocations2nd[i];
7821 const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
7822
7823 VmaAllocation const alloc = (VmaAllocation)suballoc.userData;
7824 if (!IsVirtual())
7825 {
7826 VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));
7827 }
7828 VMA_VALIDATE(suballoc.offset >= offset);
7829
7830 if (!currFree)
7831 {
7832 if (!IsVirtual())
7833 {
7834 VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);
7835 VMA_VALIDATE(alloc->GetSize() == suballoc.size);
7836 }
7837 sumUsedSize += suballoc.size;
7838 }
7839 else
7840 {
7841 ++nullItem2ndCount;
7842 }
7843
7844 offset = suballoc.offset + suballoc.size + debugMargin;
7845 }
7846
7847 VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
7848 }
7849
7850 VMA_VALIDATE(offset <= GetSize());
7851 VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
7852
7853 return true;
7854 }
7855
GetAllocationCount()7856 size_t VmaBlockMetadata_Linear::GetAllocationCount() const
7857 {
7858 return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +
7859 AccessSuballocations2nd().size() - m_2ndNullItemsCount;
7860 }
7861
GetFreeRegionsCount()7862 size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const
7863 {
7864 // Function only used for defragmentation, which is disabled for this algorithm
7865 VMA_ASSERT(0);
7866 return SIZE_MAX;
7867 }
7868
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)7869 void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
7870 {
7871 const VkDeviceSize size = GetSize();
7872 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
7873 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
7874 const size_t suballoc1stCount = suballocations1st.size();
7875 const size_t suballoc2ndCount = suballocations2nd.size();
7876
7877 inoutStats.statistics.blockCount++;
7878 inoutStats.statistics.blockBytes += size;
7879
7880 VkDeviceSize lastOffset = 0;
7881
7882 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
7883 {
7884 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
7885 size_t nextAlloc2ndIndex = 0;
7886 while (lastOffset < freeSpace2ndTo1stEnd)
7887 {
7888 // Find next non-null allocation or move nextAllocIndex to the end.
7889 while (nextAlloc2ndIndex < suballoc2ndCount &&
7890 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7891 {
7892 ++nextAlloc2ndIndex;
7893 }
7894
7895 // Found non-null allocation.
7896 if (nextAlloc2ndIndex < suballoc2ndCount)
7897 {
7898 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7899
7900 // 1. Process free space before this allocation.
7901 if (lastOffset < suballoc.offset)
7902 {
7903 // There is free space from lastOffset to suballoc.offset.
7904 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7905 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7906 }
7907
7908 // 2. Process this allocation.
7909 // There is allocation with suballoc.offset, suballoc.size.
7910 VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7911
7912 // 3. Prepare for next iteration.
7913 lastOffset = suballoc.offset + suballoc.size;
7914 ++nextAlloc2ndIndex;
7915 }
7916 // We are at the end.
7917 else
7918 {
7919 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
7920 if (lastOffset < freeSpace2ndTo1stEnd)
7921 {
7922 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
7923 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7924 }
7925
7926 // End of loop.
7927 lastOffset = freeSpace2ndTo1stEnd;
7928 }
7929 }
7930 }
7931
7932 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
7933 const VkDeviceSize freeSpace1stTo2ndEnd =
7934 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
7935 while (lastOffset < freeSpace1stTo2ndEnd)
7936 {
7937 // Find next non-null allocation or move nextAllocIndex to the end.
7938 while (nextAlloc1stIndex < suballoc1stCount &&
7939 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
7940 {
7941 ++nextAlloc1stIndex;
7942 }
7943
7944 // Found non-null allocation.
7945 if (nextAlloc1stIndex < suballoc1stCount)
7946 {
7947 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
7948
7949 // 1. Process free space before this allocation.
7950 if (lastOffset < suballoc.offset)
7951 {
7952 // There is free space from lastOffset to suballoc.offset.
7953 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
7954 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7955 }
7956
7957 // 2. Process this allocation.
7958 // There is allocation with suballoc.offset, suballoc.size.
7959 VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
7960
7961 // 3. Prepare for next iteration.
7962 lastOffset = suballoc.offset + suballoc.size;
7963 ++nextAlloc1stIndex;
7964 }
7965 // We are at the end.
7966 else
7967 {
7968 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
7969 if (lastOffset < freeSpace1stTo2ndEnd)
7970 {
7971 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
7972 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
7973 }
7974
7975 // End of loop.
7976 lastOffset = freeSpace1stTo2ndEnd;
7977 }
7978 }
7979
7980 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
7981 {
7982 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
7983 while (lastOffset < size)
7984 {
7985 // Find next non-null allocation or move nextAllocIndex to the end.
7986 while (nextAlloc2ndIndex != SIZE_MAX &&
7987 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
7988 {
7989 --nextAlloc2ndIndex;
7990 }
7991
7992 // Found non-null allocation.
7993 if (nextAlloc2ndIndex != SIZE_MAX)
7994 {
7995 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
7996
7997 // 1. Process free space before this allocation.
7998 if (lastOffset < suballoc.offset)
7999 {
8000 // There is free space from lastOffset to suballoc.offset.
8001 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8002 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
8003 }
8004
8005 // 2. Process this allocation.
8006 // There is allocation with suballoc.offset, suballoc.size.
8007 VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);
8008
8009 // 3. Prepare for next iteration.
8010 lastOffset = suballoc.offset + suballoc.size;
8011 --nextAlloc2ndIndex;
8012 }
8013 // We are at the end.
8014 else
8015 {
8016 // There is free space from lastOffset to size.
8017 if (lastOffset < size)
8018 {
8019 const VkDeviceSize unusedRangeSize = size - lastOffset;
8020 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);
8021 }
8022
8023 // End of loop.
8024 lastOffset = size;
8025 }
8026 }
8027 }
8028 }
8029
AddStatistics(VmaStatistics & inoutStats)8030 void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const
8031 {
8032 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8033 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8034 const VkDeviceSize size = GetSize();
8035 const size_t suballoc1stCount = suballocations1st.size();
8036 const size_t suballoc2ndCount = suballocations2nd.size();
8037
8038 inoutStats.blockCount++;
8039 inoutStats.blockBytes += size;
8040 inoutStats.allocationBytes += size - m_SumFreeSize;
8041
8042 VkDeviceSize lastOffset = 0;
8043
8044 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8045 {
8046 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8047 size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
8048 while (lastOffset < freeSpace2ndTo1stEnd)
8049 {
8050 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8051 while (nextAlloc2ndIndex < suballoc2ndCount &&
8052 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8053 {
8054 ++nextAlloc2ndIndex;
8055 }
8056
8057 // Found non-null allocation.
8058 if (nextAlloc2ndIndex < suballoc2ndCount)
8059 {
8060 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8061
8062 // 1. Process free space before this allocation.
8063 if (lastOffset < suballoc.offset)
8064 {
8065 // There is free space from lastOffset to suballoc.offset.
8066 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8067 }
8068
8069 // 2. Process this allocation.
8070 // There is allocation with suballoc.offset, suballoc.size.
8071 ++inoutStats.allocationCount;
8072
8073 // 3. Prepare for next iteration.
8074 lastOffset = suballoc.offset + suballoc.size;
8075 ++nextAlloc2ndIndex;
8076 }
8077 // We are at the end.
8078 else
8079 {
8080 if (lastOffset < freeSpace2ndTo1stEnd)
8081 {
8082 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8083 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
8084 }
8085
8086 // End of loop.
8087 lastOffset = freeSpace2ndTo1stEnd;
8088 }
8089 }
8090 }
8091
8092 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
8093 const VkDeviceSize freeSpace1stTo2ndEnd =
8094 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
8095 while (lastOffset < freeSpace1stTo2ndEnd)
8096 {
8097 // Find next non-null allocation or move nextAllocIndex to the end.
8098 while (nextAlloc1stIndex < suballoc1stCount &&
8099 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
8100 {
8101 ++nextAlloc1stIndex;
8102 }
8103
8104 // Found non-null allocation.
8105 if (nextAlloc1stIndex < suballoc1stCount)
8106 {
8107 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
8108
8109 // 1. Process free space before this allocation.
8110 if (lastOffset < suballoc.offset)
8111 {
8112 // There is free space from lastOffset to suballoc.offset.
8113 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8114 }
8115
8116 // 2. Process this allocation.
8117 // There is allocation with suballoc.offset, suballoc.size.
8118 ++inoutStats.allocationCount;
8119
8120 // 3. Prepare for next iteration.
8121 lastOffset = suballoc.offset + suballoc.size;
8122 ++nextAlloc1stIndex;
8123 }
8124 // We are at the end.
8125 else
8126 {
8127 if (lastOffset < freeSpace1stTo2ndEnd)
8128 {
8129 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
8130 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
8131 }
8132
8133 // End of loop.
8134 lastOffset = freeSpace1stTo2ndEnd;
8135 }
8136 }
8137
8138 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8139 {
8140 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
8141 while (lastOffset < size)
8142 {
8143 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8144 while (nextAlloc2ndIndex != SIZE_MAX &&
8145 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8146 {
8147 --nextAlloc2ndIndex;
8148 }
8149
8150 // Found non-null allocation.
8151 if (nextAlloc2ndIndex != SIZE_MAX)
8152 {
8153 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8154
8155 // 1. Process free space before this allocation.
8156 if (lastOffset < suballoc.offset)
8157 {
8158 // There is free space from lastOffset to suballoc.offset.
8159 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8160 }
8161
8162 // 2. Process this allocation.
8163 // There is allocation with suballoc.offset, suballoc.size.
8164 ++inoutStats.allocationCount;
8165
8166 // 3. Prepare for next iteration.
8167 lastOffset = suballoc.offset + suballoc.size;
8168 --nextAlloc2ndIndex;
8169 }
8170 // We are at the end.
8171 else
8172 {
8173 if (lastOffset < size)
8174 {
8175 // There is free space from lastOffset to size.
8176 const VkDeviceSize unusedRangeSize = size - lastOffset;
8177 }
8178
8179 // End of loop.
8180 lastOffset = size;
8181 }
8182 }
8183 }
8184 }
8185
8186 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)8187 void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
8188 {
8189 const VkDeviceSize size = GetSize();
8190 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8191 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8192 const size_t suballoc1stCount = suballocations1st.size();
8193 const size_t suballoc2ndCount = suballocations2nd.size();
8194
8195 // FIRST PASS
8196
8197 size_t unusedRangeCount = 0;
8198 VkDeviceSize usedBytes = 0;
8199
8200 VkDeviceSize lastOffset = 0;
8201
8202 size_t alloc2ndCount = 0;
8203 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8204 {
8205 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8206 size_t nextAlloc2ndIndex = 0;
8207 while (lastOffset < freeSpace2ndTo1stEnd)
8208 {
8209 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8210 while (nextAlloc2ndIndex < suballoc2ndCount &&
8211 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8212 {
8213 ++nextAlloc2ndIndex;
8214 }
8215
8216 // Found non-null allocation.
8217 if (nextAlloc2ndIndex < suballoc2ndCount)
8218 {
8219 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8220
8221 // 1. Process free space before this allocation.
8222 if (lastOffset < suballoc.offset)
8223 {
8224 // There is free space from lastOffset to suballoc.offset.
8225 ++unusedRangeCount;
8226 }
8227
8228 // 2. Process this allocation.
8229 // There is allocation with suballoc.offset, suballoc.size.
8230 ++alloc2ndCount;
8231 usedBytes += suballoc.size;
8232
8233 // 3. Prepare for next iteration.
8234 lastOffset = suballoc.offset + suballoc.size;
8235 ++nextAlloc2ndIndex;
8236 }
8237 // We are at the end.
8238 else
8239 {
8240 if (lastOffset < freeSpace2ndTo1stEnd)
8241 {
8242 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8243 ++unusedRangeCount;
8244 }
8245
8246 // End of loop.
8247 lastOffset = freeSpace2ndTo1stEnd;
8248 }
8249 }
8250 }
8251
8252 size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
8253 size_t alloc1stCount = 0;
8254 const VkDeviceSize freeSpace1stTo2ndEnd =
8255 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
8256 while (lastOffset < freeSpace1stTo2ndEnd)
8257 {
8258 // Find next non-null allocation or move nextAllocIndex to the end.
8259 while (nextAlloc1stIndex < suballoc1stCount &&
8260 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
8261 {
8262 ++nextAlloc1stIndex;
8263 }
8264
8265 // Found non-null allocation.
8266 if (nextAlloc1stIndex < suballoc1stCount)
8267 {
8268 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
8269
8270 // 1. Process free space before this allocation.
8271 if (lastOffset < suballoc.offset)
8272 {
8273 // There is free space from lastOffset to suballoc.offset.
8274 ++unusedRangeCount;
8275 }
8276
8277 // 2. Process this allocation.
8278 // There is allocation with suballoc.offset, suballoc.size.
8279 ++alloc1stCount;
8280 usedBytes += suballoc.size;
8281
8282 // 3. Prepare for next iteration.
8283 lastOffset = suballoc.offset + suballoc.size;
8284 ++nextAlloc1stIndex;
8285 }
8286 // We are at the end.
8287 else
8288 {
8289 if (lastOffset < size)
8290 {
8291 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
8292 ++unusedRangeCount;
8293 }
8294
8295 // End of loop.
8296 lastOffset = freeSpace1stTo2ndEnd;
8297 }
8298 }
8299
8300 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8301 {
8302 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
8303 while (lastOffset < size)
8304 {
8305 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8306 while (nextAlloc2ndIndex != SIZE_MAX &&
8307 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8308 {
8309 --nextAlloc2ndIndex;
8310 }
8311
8312 // Found non-null allocation.
8313 if (nextAlloc2ndIndex != SIZE_MAX)
8314 {
8315 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8316
8317 // 1. Process free space before this allocation.
8318 if (lastOffset < suballoc.offset)
8319 {
8320 // There is free space from lastOffset to suballoc.offset.
8321 ++unusedRangeCount;
8322 }
8323
8324 // 2. Process this allocation.
8325 // There is allocation with suballoc.offset, suballoc.size.
8326 ++alloc2ndCount;
8327 usedBytes += suballoc.size;
8328
8329 // 3. Prepare for next iteration.
8330 lastOffset = suballoc.offset + suballoc.size;
8331 --nextAlloc2ndIndex;
8332 }
8333 // We are at the end.
8334 else
8335 {
8336 if (lastOffset < size)
8337 {
8338 // There is free space from lastOffset to size.
8339 ++unusedRangeCount;
8340 }
8341
8342 // End of loop.
8343 lastOffset = size;
8344 }
8345 }
8346 }
8347
8348 const VkDeviceSize unusedBytes = size - usedBytes;
8349 PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
8350
8351 // SECOND PASS
8352 lastOffset = 0;
8353
8354 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8355 {
8356 const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
8357 size_t nextAlloc2ndIndex = 0;
8358 while (lastOffset < freeSpace2ndTo1stEnd)
8359 {
8360 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8361 while (nextAlloc2ndIndex < suballoc2ndCount &&
8362 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8363 {
8364 ++nextAlloc2ndIndex;
8365 }
8366
8367 // Found non-null allocation.
8368 if (nextAlloc2ndIndex < suballoc2ndCount)
8369 {
8370 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8371
8372 // 1. Process free space before this allocation.
8373 if (lastOffset < suballoc.offset)
8374 {
8375 // There is free space from lastOffset to suballoc.offset.
8376 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8377 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8378 }
8379
8380 // 2. Process this allocation.
8381 // There is allocation with suballoc.offset, suballoc.size.
8382 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8383
8384 // 3. Prepare for next iteration.
8385 lastOffset = suballoc.offset + suballoc.size;
8386 ++nextAlloc2ndIndex;
8387 }
8388 // We are at the end.
8389 else
8390 {
8391 if (lastOffset < freeSpace2ndTo1stEnd)
8392 {
8393 // There is free space from lastOffset to freeSpace2ndTo1stEnd.
8394 const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
8395 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8396 }
8397
8398 // End of loop.
8399 lastOffset = freeSpace2ndTo1stEnd;
8400 }
8401 }
8402 }
8403
8404 nextAlloc1stIndex = m_1stNullItemsBeginCount;
8405 while (lastOffset < freeSpace1stTo2ndEnd)
8406 {
8407 // Find next non-null allocation or move nextAllocIndex to the end.
8408 while (nextAlloc1stIndex < suballoc1stCount &&
8409 suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)
8410 {
8411 ++nextAlloc1stIndex;
8412 }
8413
8414 // Found non-null allocation.
8415 if (nextAlloc1stIndex < suballoc1stCount)
8416 {
8417 const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
8418
8419 // 1. Process free space before this allocation.
8420 if (lastOffset < suballoc.offset)
8421 {
8422 // There is free space from lastOffset to suballoc.offset.
8423 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8424 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8425 }
8426
8427 // 2. Process this allocation.
8428 // There is allocation with suballoc.offset, suballoc.size.
8429 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8430
8431 // 3. Prepare for next iteration.
8432 lastOffset = suballoc.offset + suballoc.size;
8433 ++nextAlloc1stIndex;
8434 }
8435 // We are at the end.
8436 else
8437 {
8438 if (lastOffset < freeSpace1stTo2ndEnd)
8439 {
8440 // There is free space from lastOffset to freeSpace1stTo2ndEnd.
8441 const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
8442 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8443 }
8444
8445 // End of loop.
8446 lastOffset = freeSpace1stTo2ndEnd;
8447 }
8448 }
8449
8450 if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8451 {
8452 size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
8453 while (lastOffset < size)
8454 {
8455 // Find next non-null allocation or move nextAlloc2ndIndex to the end.
8456 while (nextAlloc2ndIndex != SIZE_MAX &&
8457 suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)
8458 {
8459 --nextAlloc2ndIndex;
8460 }
8461
8462 // Found non-null allocation.
8463 if (nextAlloc2ndIndex != SIZE_MAX)
8464 {
8465 const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
8466
8467 // 1. Process free space before this allocation.
8468 if (lastOffset < suballoc.offset)
8469 {
8470 // There is free space from lastOffset to suballoc.offset.
8471 const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
8472 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8473 }
8474
8475 // 2. Process this allocation.
8476 // There is allocation with suballoc.offset, suballoc.size.
8477 PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);
8478
8479 // 3. Prepare for next iteration.
8480 lastOffset = suballoc.offset + suballoc.size;
8481 --nextAlloc2ndIndex;
8482 }
8483 // We are at the end.
8484 else
8485 {
8486 if (lastOffset < size)
8487 {
8488 // There is free space from lastOffset to size.
8489 const VkDeviceSize unusedRangeSize = size - lastOffset;
8490 PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
8491 }
8492
8493 // End of loop.
8494 lastOffset = size;
8495 }
8496 }
8497 }
8498
8499 PrintDetailedMap_End(json);
8500 }
8501 #endif // VMA_STATS_STRING_ENABLED
8502
CreateAllocationRequest(VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)8503 bool VmaBlockMetadata_Linear::CreateAllocationRequest(
8504 VkDeviceSize allocSize,
8505 VkDeviceSize allocAlignment,
8506 bool upperAddress,
8507 VmaSuballocationType allocType,
8508 uint32_t strategy,
8509 VmaAllocationRequest* pAllocationRequest)
8510 {
8511 VMA_ASSERT(allocSize > 0);
8512 VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
8513 VMA_ASSERT(pAllocationRequest != VMA_NULL);
8514 VMA_HEAVY_ASSERT(Validate());
8515 pAllocationRequest->size = allocSize;
8516 return upperAddress ?
8517 CreateAllocationRequest_UpperAddress(
8518 allocSize, allocAlignment, allocType, strategy, pAllocationRequest) :
8519 CreateAllocationRequest_LowerAddress(
8520 allocSize, allocAlignment, allocType, strategy, pAllocationRequest);
8521 }
8522
CheckCorruption(const void * pBlockData)8523 VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
8524 {
8525 VMA_ASSERT(!IsVirtual());
8526 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8527 for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
8528 {
8529 const VmaSuballocation& suballoc = suballocations1st[i];
8530 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8531 {
8532 if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
8533 {
8534 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8535 return VK_ERROR_UNKNOWN_COPY;
8536 }
8537 }
8538 }
8539
8540 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8541 for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
8542 {
8543 const VmaSuballocation& suballoc = suballocations2nd[i];
8544 if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
8545 {
8546 if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
8547 {
8548 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
8549 return VK_ERROR_UNKNOWN_COPY;
8550 }
8551 }
8552 }
8553
8554 return VK_SUCCESS;
8555 }
8556
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)8557 void VmaBlockMetadata_Linear::Alloc(
8558 const VmaAllocationRequest& request,
8559 VmaSuballocationType type,
8560 void* userData)
8561 {
8562 const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
8563 const VmaSuballocation newSuballoc = { offset, request.size, userData, type };
8564
8565 switch (request.type)
8566 {
8567 case VmaAllocationRequestType::UpperAddress:
8568 {
8569 VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
8570 "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
8571 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8572 suballocations2nd.push_back(newSuballoc);
8573 m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
8574 }
8575 break;
8576 case VmaAllocationRequestType::EndOf1st:
8577 {
8578 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8579
8580 VMA_ASSERT(suballocations1st.empty() ||
8581 offset >= suballocations1st.back().offset + suballocations1st.back().size);
8582 // Check if it fits before the end of the block.
8583 VMA_ASSERT(offset + request.size <= GetSize());
8584
8585 suballocations1st.push_back(newSuballoc);
8586 }
8587 break;
8588 case VmaAllocationRequestType::EndOf2nd:
8589 {
8590 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8591 // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
8592 VMA_ASSERT(!suballocations1st.empty() &&
8593 offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);
8594 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8595
8596 switch (m_2ndVectorMode)
8597 {
8598 case SECOND_VECTOR_EMPTY:
8599 // First allocation from second part ring buffer.
8600 VMA_ASSERT(suballocations2nd.empty());
8601 m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
8602 break;
8603 case SECOND_VECTOR_RING_BUFFER:
8604 // 2-part ring buffer is already started.
8605 VMA_ASSERT(!suballocations2nd.empty());
8606 break;
8607 case SECOND_VECTOR_DOUBLE_STACK:
8608 VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
8609 break;
8610 default:
8611 VMA_ASSERT(0);
8612 }
8613
8614 suballocations2nd.push_back(newSuballoc);
8615 }
8616 break;
8617 default:
8618 VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
8619 }
8620
8621 m_SumFreeSize -= newSuballoc.size;
8622 }
8623
Free(VmaAllocHandle allocHandle)8624 void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle)
8625 {
8626 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8627 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8628 VkDeviceSize offset = (VkDeviceSize)allocHandle - 1;
8629
8630 if (!suballocations1st.empty())
8631 {
8632 // First allocation: Mark it as next empty at the beginning.
8633 VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
8634 if (firstSuballoc.offset == offset)
8635 {
8636 firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
8637 firstSuballoc.userData = VMA_NULL;
8638 m_SumFreeSize += firstSuballoc.size;
8639 ++m_1stNullItemsBeginCount;
8640 CleanupAfterFree();
8641 return;
8642 }
8643 }
8644
8645 // Last allocation in 2-part ring buffer or top of upper stack (same logic).
8646 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
8647 m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8648 {
8649 VmaSuballocation& lastSuballoc = suballocations2nd.back();
8650 if (lastSuballoc.offset == offset)
8651 {
8652 m_SumFreeSize += lastSuballoc.size;
8653 suballocations2nd.pop_back();
8654 CleanupAfterFree();
8655 return;
8656 }
8657 }
8658 // Last allocation in 1st vector.
8659 else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)
8660 {
8661 VmaSuballocation& lastSuballoc = suballocations1st.back();
8662 if (lastSuballoc.offset == offset)
8663 {
8664 m_SumFreeSize += lastSuballoc.size;
8665 suballocations1st.pop_back();
8666 CleanupAfterFree();
8667 return;
8668 }
8669 }
8670
8671 VmaSuballocation refSuballoc;
8672 refSuballoc.offset = offset;
8673 // Rest of members stays uninitialized intentionally for better performance.
8674
8675 // Item from the middle of 1st vector.
8676 {
8677 const SuballocationVectorType::iterator it = VmaBinaryFindSorted(
8678 suballocations1st.begin() + m_1stNullItemsBeginCount,
8679 suballocations1st.end(),
8680 refSuballoc,
8681 VmaSuballocationOffsetLess());
8682 if (it != suballocations1st.end())
8683 {
8684 it->type = VMA_SUBALLOCATION_TYPE_FREE;
8685 it->userData = VMA_NULL;
8686 ++m_1stNullItemsMiddleCount;
8687 m_SumFreeSize += it->size;
8688 CleanupAfterFree();
8689 return;
8690 }
8691 }
8692
8693 if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
8694 {
8695 // Item from the middle of 2nd vector.
8696 const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
8697 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
8698 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
8699 if (it != suballocations2nd.end())
8700 {
8701 it->type = VMA_SUBALLOCATION_TYPE_FREE;
8702 it->userData = VMA_NULL;
8703 ++m_2ndNullItemsCount;
8704 m_SumFreeSize += it->size;
8705 CleanupAfterFree();
8706 return;
8707 }
8708 }
8709
8710 VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
8711 }
8712
GetAllocationInfo(VmaAllocHandle allocHandle,VmaVirtualAllocationInfo & outInfo)8713 void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
8714 {
8715 outInfo.offset = (VkDeviceSize)allocHandle - 1;
8716 VmaSuballocation& suballoc = FindSuballocation(outInfo.offset);
8717 outInfo.size = suballoc.size;
8718 outInfo.pUserData = suballoc.userData;
8719 }
8720
GetAllocationUserData(VmaAllocHandle allocHandle)8721 void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const
8722 {
8723 return FindSuballocation((VkDeviceSize)allocHandle - 1).userData;
8724 }
8725
GetAllocationListBegin()8726 VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const
8727 {
8728 // Function only used for defragmentation, which is disabled for this algorithm
8729 VMA_ASSERT(0);
8730 return VK_NULL_HANDLE;
8731 }
8732
GetNextAllocation(VmaAllocHandle prevAlloc)8733 VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const
8734 {
8735 // Function only used for defragmentation, which is disabled for this algorithm
8736 VMA_ASSERT(0);
8737 return VK_NULL_HANDLE;
8738 }
8739
GetNextFreeRegionSize(VmaAllocHandle alloc)8740 VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const
8741 {
8742 // Function only used for defragmentation, which is disabled for this algorithm
8743 VMA_ASSERT(0);
8744 return 0;
8745 }
8746
Clear()8747 void VmaBlockMetadata_Linear::Clear()
8748 {
8749 m_SumFreeSize = GetSize();
8750 m_Suballocations0.clear();
8751 m_Suballocations1.clear();
8752 // Leaving m_1stVectorIndex unchanged - it doesn't matter.
8753 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8754 m_1stNullItemsBeginCount = 0;
8755 m_1stNullItemsMiddleCount = 0;
8756 m_2ndNullItemsCount = 0;
8757 }
8758
SetAllocationUserData(VmaAllocHandle allocHandle,void * userData)8759 void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
8760 {
8761 VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1);
8762 suballoc.userData = userData;
8763 }
8764
DebugLogAllAllocations()8765 void VmaBlockMetadata_Linear::DebugLogAllAllocations() const
8766 {
8767 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8768 for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)
8769 if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
8770 DebugLogAllocation(it->offset, it->size, it->userData);
8771
8772 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8773 for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)
8774 if (it->type != VMA_SUBALLOCATION_TYPE_FREE)
8775 DebugLogAllocation(it->offset, it->size, it->userData);
8776 }
8777
FindSuballocation(VkDeviceSize offset)8778 VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const
8779 {
8780 const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8781 const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8782
8783 VmaSuballocation refSuballoc;
8784 refSuballoc.offset = offset;
8785 // Rest of members stays uninitialized intentionally for better performance.
8786
8787 // Item from the 1st vector.
8788 {
8789 SuballocationVectorType::const_iterator it = VmaBinaryFindSorted(
8790 suballocations1st.begin() + m_1stNullItemsBeginCount,
8791 suballocations1st.end(),
8792 refSuballoc,
8793 VmaSuballocationOffsetLess());
8794 if (it != suballocations1st.end())
8795 {
8796 return const_cast<VmaSuballocation&>(*it);
8797 }
8798 }
8799
8800 if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)
8801 {
8802 // Rest of members stays uninitialized intentionally for better performance.
8803 SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
8804 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :
8805 VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());
8806 if (it != suballocations2nd.end())
8807 {
8808 return const_cast<VmaSuballocation&>(*it);
8809 }
8810 }
8811
8812 VMA_ASSERT(0 && "Allocation not found in linear allocator!");
8813 return const_cast<VmaSuballocation&>(suballocations1st.back()); // Should never occur.
8814 }
8815
ShouldCompact1st()8816 bool VmaBlockMetadata_Linear::ShouldCompact1st() const
8817 {
8818 const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
8819 const size_t suballocCount = AccessSuballocations1st().size();
8820 return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
8821 }
8822
CleanupAfterFree()8823 void VmaBlockMetadata_Linear::CleanupAfterFree()
8824 {
8825 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8826 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8827
8828 if (IsEmpty())
8829 {
8830 suballocations1st.clear();
8831 suballocations2nd.clear();
8832 m_1stNullItemsBeginCount = 0;
8833 m_1stNullItemsMiddleCount = 0;
8834 m_2ndNullItemsCount = 0;
8835 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8836 }
8837 else
8838 {
8839 const size_t suballoc1stCount = suballocations1st.size();
8840 const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
8841 VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
8842
8843 // Find more null items at the beginning of 1st vector.
8844 while (m_1stNullItemsBeginCount < suballoc1stCount &&
8845 suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
8846 {
8847 ++m_1stNullItemsBeginCount;
8848 --m_1stNullItemsMiddleCount;
8849 }
8850
8851 // Find more null items at the end of 1st vector.
8852 while (m_1stNullItemsMiddleCount > 0 &&
8853 suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)
8854 {
8855 --m_1stNullItemsMiddleCount;
8856 suballocations1st.pop_back();
8857 }
8858
8859 // Find more null items at the end of 2nd vector.
8860 while (m_2ndNullItemsCount > 0 &&
8861 suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)
8862 {
8863 --m_2ndNullItemsCount;
8864 suballocations2nd.pop_back();
8865 }
8866
8867 // Find more null items at the beginning of 2nd vector.
8868 while (m_2ndNullItemsCount > 0 &&
8869 suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)
8870 {
8871 --m_2ndNullItemsCount;
8872 VmaVectorRemove(suballocations2nd, 0);
8873 }
8874
8875 if (ShouldCompact1st())
8876 {
8877 const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
8878 size_t srcIndex = m_1stNullItemsBeginCount;
8879 for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
8880 {
8881 while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)
8882 {
8883 ++srcIndex;
8884 }
8885 if (dstIndex != srcIndex)
8886 {
8887 suballocations1st[dstIndex] = suballocations1st[srcIndex];
8888 }
8889 ++srcIndex;
8890 }
8891 suballocations1st.resize(nonNullItemCount);
8892 m_1stNullItemsBeginCount = 0;
8893 m_1stNullItemsMiddleCount = 0;
8894 }
8895
8896 // 2nd vector became empty.
8897 if (suballocations2nd.empty())
8898 {
8899 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8900 }
8901
8902 // 1st vector became empty.
8903 if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)
8904 {
8905 suballocations1st.clear();
8906 m_1stNullItemsBeginCount = 0;
8907
8908 if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
8909 {
8910 // Swap 1st with 2nd. Now 2nd is empty.
8911 m_2ndVectorMode = SECOND_VECTOR_EMPTY;
8912 m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
8913 while (m_1stNullItemsBeginCount < suballocations2nd.size() &&
8914 suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)
8915 {
8916 ++m_1stNullItemsBeginCount;
8917 --m_1stNullItemsMiddleCount;
8918 }
8919 m_2ndNullItemsCount = 0;
8920 m_1stVectorIndex ^= 1;
8921 }
8922 }
8923 }
8924
8925 VMA_HEAVY_ASSERT(Validate());
8926 }
8927
CreateAllocationRequest_LowerAddress(VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)8928 bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(
8929 VkDeviceSize allocSize,
8930 VkDeviceSize allocAlignment,
8931 VmaSuballocationType allocType,
8932 uint32_t strategy,
8933 VmaAllocationRequest* pAllocationRequest)
8934 {
8935 const VkDeviceSize blockSize = GetSize();
8936 const VkDeviceSize debugMargin = GetDebugMargin();
8937 const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
8938 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
8939 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
8940
8941 if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8942 {
8943 // Try to allocate at the end of 1st vector.
8944
8945 VkDeviceSize resultBaseOffset = 0;
8946 if (!suballocations1st.empty())
8947 {
8948 const VmaSuballocation& lastSuballoc = suballocations1st.back();
8949 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
8950 }
8951
8952 // Start from offset equal to beginning of free space.
8953 VkDeviceSize resultOffset = resultBaseOffset;
8954
8955 // Apply alignment.
8956 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
8957
8958 // Check previous suballocations for BufferImageGranularity conflicts.
8959 // Make bigger alignment if necessary.
8960 if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())
8961 {
8962 bool bufferImageGranularityConflict = false;
8963 for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
8964 {
8965 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
8966 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
8967 {
8968 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
8969 {
8970 bufferImageGranularityConflict = true;
8971 break;
8972 }
8973 }
8974 else
8975 // Already on previous page.
8976 break;
8977 }
8978 if (bufferImageGranularityConflict)
8979 {
8980 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
8981 }
8982 }
8983
8984 const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
8985 suballocations2nd.back().offset : blockSize;
8986
8987 // There is enough free space at the end after alignment.
8988 if (resultOffset + allocSize + debugMargin <= freeSpaceEnd)
8989 {
8990 // Check next suballocations for BufferImageGranularity conflicts.
8991 // If conflict exists, allocation cannot be made here.
8992 if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
8993 {
8994 for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
8995 {
8996 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
8997 if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
8998 {
8999 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9000 {
9001 return false;
9002 }
9003 }
9004 else
9005 {
9006 // Already on previous page.
9007 break;
9008 }
9009 }
9010 }
9011
9012 // All tests passed: Success.
9013 pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
9014 // pAllocationRequest->item, customData unused.
9015 pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;
9016 return true;
9017 }
9018 }
9019
9020 // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
9021 // beginning of 1st vector as the end of free space.
9022 if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9023 {
9024 VMA_ASSERT(!suballocations1st.empty());
9025
9026 VkDeviceSize resultBaseOffset = 0;
9027 if (!suballocations2nd.empty())
9028 {
9029 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9030 resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;
9031 }
9032
9033 // Start from offset equal to beginning of free space.
9034 VkDeviceSize resultOffset = resultBaseOffset;
9035
9036 // Apply alignment.
9037 resultOffset = VmaAlignUp(resultOffset, allocAlignment);
9038
9039 // Check previous suballocations for BufferImageGranularity conflicts.
9040 // Make bigger alignment if necessary.
9041 if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
9042 {
9043 bool bufferImageGranularityConflict = false;
9044 for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
9045 {
9046 const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
9047 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9048 {
9049 if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
9050 {
9051 bufferImageGranularityConflict = true;
9052 break;
9053 }
9054 }
9055 else
9056 // Already on previous page.
9057 break;
9058 }
9059 if (bufferImageGranularityConflict)
9060 {
9061 resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
9062 }
9063 }
9064
9065 size_t index1st = m_1stNullItemsBeginCount;
9066
9067 // There is enough free space at the end after alignment.
9068 if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||
9069 (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))
9070 {
9071 // Check next suballocations for BufferImageGranularity conflicts.
9072 // If conflict exists, allocation cannot be made here.
9073 if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)
9074 {
9075 for (size_t nextSuballocIndex = index1st;
9076 nextSuballocIndex < suballocations1st.size();
9077 nextSuballocIndex++)
9078 {
9079 const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
9080 if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9081 {
9082 if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
9083 {
9084 return false;
9085 }
9086 }
9087 else
9088 {
9089 // Already on next page.
9090 break;
9091 }
9092 }
9093 }
9094
9095 // All tests passed: Success.
9096 pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
9097 pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;
9098 // pAllocationRequest->item, customData unused.
9099 return true;
9100 }
9101 }
9102
9103 return false;
9104 }
9105
CreateAllocationRequest_UpperAddress(VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)9106 bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(
9107 VkDeviceSize allocSize,
9108 VkDeviceSize allocAlignment,
9109 VmaSuballocationType allocType,
9110 uint32_t strategy,
9111 VmaAllocationRequest* pAllocationRequest)
9112 {
9113 const VkDeviceSize blockSize = GetSize();
9114 const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
9115 SuballocationVectorType& suballocations1st = AccessSuballocations1st();
9116 SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
9117
9118 if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
9119 {
9120 VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
9121 return false;
9122 }
9123
9124 // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
9125 if (allocSize > blockSize)
9126 {
9127 return false;
9128 }
9129 VkDeviceSize resultBaseOffset = blockSize - allocSize;
9130 if (!suballocations2nd.empty())
9131 {
9132 const VmaSuballocation& lastSuballoc = suballocations2nd.back();
9133 resultBaseOffset = lastSuballoc.offset - allocSize;
9134 if (allocSize > lastSuballoc.offset)
9135 {
9136 return false;
9137 }
9138 }
9139
9140 // Start from offset equal to end of free space.
9141 VkDeviceSize resultOffset = resultBaseOffset;
9142
9143 const VkDeviceSize debugMargin = GetDebugMargin();
9144
9145 // Apply debugMargin at the end.
9146 if (debugMargin > 0)
9147 {
9148 if (resultOffset < debugMargin)
9149 {
9150 return false;
9151 }
9152 resultOffset -= debugMargin;
9153 }
9154
9155 // Apply alignment.
9156 resultOffset = VmaAlignDown(resultOffset, allocAlignment);
9157
9158 // Check next suballocations from 2nd for BufferImageGranularity conflicts.
9159 // Make bigger alignment if necessary.
9160 if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())
9161 {
9162 bool bufferImageGranularityConflict = false;
9163 for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
9164 {
9165 const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
9166 if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
9167 {
9168 if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
9169 {
9170 bufferImageGranularityConflict = true;
9171 break;
9172 }
9173 }
9174 else
9175 // Already on previous page.
9176 break;
9177 }
9178 if (bufferImageGranularityConflict)
9179 {
9180 resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
9181 }
9182 }
9183
9184 // There is enough free space.
9185 const VkDeviceSize endOf1st = !suballocations1st.empty() ?
9186 suballocations1st.back().offset + suballocations1st.back().size :
9187 0;
9188 if (endOf1st + debugMargin <= resultOffset)
9189 {
9190 // Check previous suballocations for BufferImageGranularity conflicts.
9191 // If conflict exists, allocation cannot be made here.
9192 if (bufferImageGranularity > 1)
9193 {
9194 for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
9195 {
9196 const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
9197 if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
9198 {
9199 if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
9200 {
9201 return false;
9202 }
9203 }
9204 else
9205 {
9206 // Already on next page.
9207 break;
9208 }
9209 }
9210 }
9211
9212 // All tests passed: Success.
9213 pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);
9214 // pAllocationRequest->item unused.
9215 pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;
9216 return true;
9217 }
9218
9219 return false;
9220 }
9221 #endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS
9222 #endif // _VMA_BLOCK_METADATA_LINEAR
9223
9224 #if 0
9225 #ifndef _VMA_BLOCK_METADATA_BUDDY
9226 /*
9227 - GetSize() is the original size of allocated memory block.
9228 - m_UsableSize is this size aligned down to a power of two.
9229 All allocations and calculations happen relative to m_UsableSize.
9230 - GetUnusableSize() is the difference between them.
9231 It is reported as separate, unused range, not available for allocations.
9232
9233 Node at level 0 has size = m_UsableSize.
9234 Each next level contains nodes with size 2 times smaller than current level.
9235 m_LevelCount is the maximum number of levels to use in the current object.
9236 */
9237 class VmaBlockMetadata_Buddy : public VmaBlockMetadata
9238 {
9239 VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
9240 public:
9241 VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,
9242 VkDeviceSize bufferImageGranularity, bool isVirtual);
9243 virtual ~VmaBlockMetadata_Buddy();
9244
9245 size_t GetAllocationCount() const override { return m_AllocationCount; }
9246 VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); }
9247 bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; }
9248 VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; }
9249 VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; };
9250 void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); }
9251
9252 void Init(VkDeviceSize size) override;
9253 bool Validate() const override;
9254
9255 void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
9256 void AddStatistics(VmaStatistics& inoutStats) const override;
9257
9258 #if VMA_STATS_STRING_ENABLED
9259 void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
9260 #endif
9261
9262 bool CreateAllocationRequest(
9263 VkDeviceSize allocSize,
9264 VkDeviceSize allocAlignment,
9265 bool upperAddress,
9266 VmaSuballocationType allocType,
9267 uint32_t strategy,
9268 VmaAllocationRequest* pAllocationRequest) override;
9269
9270 void Alloc(
9271 const VmaAllocationRequest& request,
9272 VmaSuballocationType type,
9273 void* userData) override;
9274
9275 void Free(VmaAllocHandle allocHandle) override;
9276 void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
9277 void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
9278 VmaAllocHandle GetAllocationListBegin() const override;
9279 VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
9280 void Clear() override;
9281 void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
9282
9283 private:
9284 static const size_t MAX_LEVELS = 48;
9285
9286 struct ValidationContext
9287 {
9288 size_t calculatedAllocationCount = 0;
9289 size_t calculatedFreeCount = 0;
9290 VkDeviceSize calculatedSumFreeSize = 0;
9291 };
9292 struct Node
9293 {
9294 VkDeviceSize offset;
9295 enum TYPE
9296 {
9297 TYPE_FREE,
9298 TYPE_ALLOCATION,
9299 TYPE_SPLIT,
9300 TYPE_COUNT
9301 } type;
9302 Node* parent;
9303 Node* buddy;
9304
9305 union
9306 {
9307 struct
9308 {
9309 Node* prev;
9310 Node* next;
9311 } free;
9312 struct
9313 {
9314 void* userData;
9315 } allocation;
9316 struct
9317 {
9318 Node* leftChild;
9319 } split;
9320 };
9321 };
9322
9323 // Size of the memory block aligned down to a power of two.
9324 VkDeviceSize m_UsableSize;
9325 uint32_t m_LevelCount;
9326 VmaPoolAllocator<Node> m_NodeAllocator;
9327 Node* m_Root;
9328 struct
9329 {
9330 Node* front;
9331 Node* back;
9332 } m_FreeList[MAX_LEVELS];
9333
9334 // Number of nodes in the tree with type == TYPE_ALLOCATION.
9335 size_t m_AllocationCount;
9336 // Number of nodes in the tree with type == TYPE_FREE.
9337 size_t m_FreeCount;
9338 // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes.
9339 // Doesn't include unusable size.
9340 VkDeviceSize m_SumFreeSize;
9341
9342 VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
9343 VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
9344
9345 VkDeviceSize AlignAllocationSize(VkDeviceSize size) const
9346 {
9347 if (!IsVirtual())
9348 {
9349 size = VmaAlignUp(size, (VkDeviceSize)16);
9350 }
9351 return VmaNextPow2(size);
9352 }
9353 Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const;
9354 void DeleteNodeChildren(Node* node);
9355 bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
9356 uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
9357 void AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const;
9358 // Adds node to the front of FreeList at given level.
9359 // node->type must be FREE.
9360 // node->free.prev, next can be undefined.
9361 void AddToFreeListFront(uint32_t level, Node* node);
9362 // Removes node from FreeList at given level.
9363 // node->type must be FREE.
9364 // node->free.prev, next stay untouched.
9365 void RemoveFromFreeList(uint32_t level, Node* node);
9366 void DebugLogAllAllocationNode(Node* node, uint32_t level) const;
9367
9368 #if VMA_STATS_STRING_ENABLED
9369 void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
9370 #endif
9371 };
9372
9373 #ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS
9374 VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,
9375 VkDeviceSize bufferImageGranularity, bool isVirtual)
9376 : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
9377 m_NodeAllocator(pAllocationCallbacks, 32), // firstBlockCapacity
9378 m_Root(VMA_NULL),
9379 m_AllocationCount(0),
9380 m_FreeCount(1),
9381 m_SumFreeSize(0)
9382 {
9383 memset(m_FreeList, 0, sizeof(m_FreeList));
9384 }
9385
9386 VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
9387 {
9388 DeleteNodeChildren(m_Root);
9389 m_NodeAllocator.Free(m_Root);
9390 }
9391
9392 void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
9393 {
9394 VmaBlockMetadata::Init(size);
9395
9396 m_UsableSize = VmaPrevPow2(size);
9397 m_SumFreeSize = m_UsableSize;
9398
9399 // Calculate m_LevelCount.
9400 const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16;
9401 m_LevelCount = 1;
9402 while (m_LevelCount < MAX_LEVELS &&
9403 LevelToNodeSize(m_LevelCount) >= minNodeSize)
9404 {
9405 ++m_LevelCount;
9406 }
9407
9408 Node* rootNode = m_NodeAllocator.Alloc();
9409 rootNode->offset = 0;
9410 rootNode->type = Node::TYPE_FREE;
9411 rootNode->parent = VMA_NULL;
9412 rootNode->buddy = VMA_NULL;
9413
9414 m_Root = rootNode;
9415 AddToFreeListFront(0, rootNode);
9416 }
9417
9418 bool VmaBlockMetadata_Buddy::Validate() const
9419 {
9420 // Validate tree.
9421 ValidationContext ctx;
9422 if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
9423 {
9424 VMA_VALIDATE(false && "ValidateNode failed.");
9425 }
9426 VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
9427 VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
9428
9429 // Validate free node lists.
9430 for (uint32_t level = 0; level < m_LevelCount; ++level)
9431 {
9432 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
9433 m_FreeList[level].front->free.prev == VMA_NULL);
9434
9435 for (Node* node = m_FreeList[level].front;
9436 node != VMA_NULL;
9437 node = node->free.next)
9438 {
9439 VMA_VALIDATE(node->type == Node::TYPE_FREE);
9440
9441 if (node->free.next == VMA_NULL)
9442 {
9443 VMA_VALIDATE(m_FreeList[level].back == node);
9444 }
9445 else
9446 {
9447 VMA_VALIDATE(node->free.next->free.prev == node);
9448 }
9449 }
9450 }
9451
9452 // Validate that free lists ar higher levels are empty.
9453 for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
9454 {
9455 VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
9456 }
9457
9458 return true;
9459 }
9460
9461 void VmaBlockMetadata_Buddy::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
9462 {
9463 inoutStats.statistics.blockCount++;
9464 inoutStats.statistics.blockBytes += GetSize();
9465
9466 AddNodeToDetailedStatistics(inoutStats, m_Root, LevelToNodeSize(0));
9467
9468 const VkDeviceSize unusableSize = GetUnusableSize();
9469 if (unusableSize > 0)
9470 VmaAddDetailedStatisticsUnusedRange(inoutStats, unusableSize);
9471 }
9472
9473 void VmaBlockMetadata_Buddy::AddStatistics(VmaStatistics& inoutStats) const
9474 {
9475 inoutStats.blockCount++;
9476 inoutStats.allocationCount += (uint32_t)m_AllocationCount;
9477 inoutStats.blockBytes += GetSize();
9478 inoutStats.allocationBytes += GetSize() - m_SumFreeSize;
9479 }
9480
9481 #if VMA_STATS_STRING_ENABLED
9482 void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
9483 {
9484 VmaDetailedStatistics stats;
9485 VmaClearDetailedStatistics(stats);
9486 AddDetailedStatistics(stats);
9487
9488 PrintDetailedMap_Begin(
9489 json,
9490 stats.statistics.blockBytes - stats.statistics.allocationBytes,
9491 stats.statistics.allocationCount,
9492 stats.unusedRangeCount,
9493 mapRefCount);
9494
9495 PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
9496
9497 const VkDeviceSize unusableSize = GetUnusableSize();
9498 if (unusableSize > 0)
9499 {
9500 PrintDetailedMap_UnusedRange(json,
9501 m_UsableSize, // offset
9502 unusableSize); // size
9503 }
9504
9505 PrintDetailedMap_End(json);
9506 }
9507 #endif // VMA_STATS_STRING_ENABLED
9508
9509 bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
9510 VkDeviceSize allocSize,
9511 VkDeviceSize allocAlignment,
9512 bool upperAddress,
9513 VmaSuballocationType allocType,
9514 uint32_t strategy,
9515 VmaAllocationRequest* pAllocationRequest)
9516 {
9517 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
9518
9519 allocSize = AlignAllocationSize(allocSize);
9520
9521 // Simple way to respect bufferImageGranularity. May be optimized some day.
9522 // Whenever it might be an OPTIMAL image...
9523 if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
9524 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
9525 allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
9526 {
9527 allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity());
9528 allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity());
9529 }
9530
9531 if (allocSize > m_UsableSize)
9532 {
9533 return false;
9534 }
9535
9536 const uint32_t targetLevel = AllocSizeToLevel(allocSize);
9537 for (uint32_t level = targetLevel; level--; )
9538 {
9539 for (Node* freeNode = m_FreeList[level].front;
9540 freeNode != VMA_NULL;
9541 freeNode = freeNode->free.next)
9542 {
9543 if (freeNode->offset % allocAlignment == 0)
9544 {
9545 pAllocationRequest->type = VmaAllocationRequestType::Normal;
9546 pAllocationRequest->allocHandle = (VmaAllocHandle)(freeNode->offset + 1);
9547 pAllocationRequest->size = allocSize;
9548 pAllocationRequest->customData = (void*)(uintptr_t)level;
9549 return true;
9550 }
9551 }
9552 }
9553
9554 return false;
9555 }
9556
9557 void VmaBlockMetadata_Buddy::Alloc(
9558 const VmaAllocationRequest& request,
9559 VmaSuballocationType type,
9560 void* userData)
9561 {
9562 VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);
9563
9564 const uint32_t targetLevel = AllocSizeToLevel(request.size);
9565 uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
9566
9567 Node* currNode = m_FreeList[currLevel].front;
9568 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
9569 const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;
9570 while (currNode->offset != offset)
9571 {
9572 currNode = currNode->free.next;
9573 VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
9574 }
9575
9576 // Go down, splitting free nodes.
9577 while (currLevel < targetLevel)
9578 {
9579 // currNode is already first free node at currLevel.
9580 // Remove it from list of free nodes at this currLevel.
9581 RemoveFromFreeList(currLevel, currNode);
9582
9583 const uint32_t childrenLevel = currLevel + 1;
9584
9585 // Create two free sub-nodes.
9586 Node* leftChild = m_NodeAllocator.Alloc();
9587 Node* rightChild = m_NodeAllocator.Alloc();
9588
9589 leftChild->offset = currNode->offset;
9590 leftChild->type = Node::TYPE_FREE;
9591 leftChild->parent = currNode;
9592 leftChild->buddy = rightChild;
9593
9594 rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
9595 rightChild->type = Node::TYPE_FREE;
9596 rightChild->parent = currNode;
9597 rightChild->buddy = leftChild;
9598
9599 // Convert current currNode to split type.
9600 currNode->type = Node::TYPE_SPLIT;
9601 currNode->split.leftChild = leftChild;
9602
9603 // Add child nodes to free list. Order is important!
9604 AddToFreeListFront(childrenLevel, rightChild);
9605 AddToFreeListFront(childrenLevel, leftChild);
9606
9607 ++m_FreeCount;
9608 ++currLevel;
9609 currNode = m_FreeList[currLevel].front;
9610
9611 /*
9612 We can be sure that currNode, as left child of node previously split,
9613 also fulfills the alignment requirement.
9614 */
9615 }
9616
9617 // Remove from free list.
9618 VMA_ASSERT(currLevel == targetLevel &&
9619 currNode != VMA_NULL &&
9620 currNode->type == Node::TYPE_FREE);
9621 RemoveFromFreeList(currLevel, currNode);
9622
9623 // Convert to allocation node.
9624 currNode->type = Node::TYPE_ALLOCATION;
9625 currNode->allocation.userData = userData;
9626
9627 ++m_AllocationCount;
9628 --m_FreeCount;
9629 m_SumFreeSize -= request.size;
9630 }
9631
9632 void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
9633 {
9634 uint32_t level = 0;
9635 outInfo.offset = (VkDeviceSize)allocHandle - 1;
9636 const Node* const node = FindAllocationNode(outInfo.offset, level);
9637 outInfo.size = LevelToNodeSize(level);
9638 outInfo.pUserData = node->allocation.userData;
9639 }
9640
9641 void* VmaBlockMetadata_Buddy::GetAllocationUserData(VmaAllocHandle allocHandle) const
9642 {
9643 uint32_t level = 0;
9644 const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
9645 return node->allocation.userData;
9646 }
9647
9648 VmaAllocHandle VmaBlockMetadata_Buddy::GetAllocationListBegin() const
9649 {
9650 // Function only used for defragmentation, which is disabled for this algorithm
9651 return VK_NULL_HANDLE;
9652 }
9653
9654 VmaAllocHandle VmaBlockMetadata_Buddy::GetNextAllocation(VmaAllocHandle prevAlloc) const
9655 {
9656 // Function only used for defragmentation, which is disabled for this algorithm
9657 return VK_NULL_HANDLE;
9658 }
9659
9660 void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node)
9661 {
9662 if (node->type == Node::TYPE_SPLIT)
9663 {
9664 DeleteNodeChildren(node->split.leftChild->buddy);
9665 DeleteNodeChildren(node->split.leftChild);
9666 const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks();
9667 m_NodeAllocator.Free(node->split.leftChild->buddy);
9668 m_NodeAllocator.Free(node->split.leftChild);
9669 }
9670 }
9671
9672 void VmaBlockMetadata_Buddy::Clear()
9673 {
9674 DeleteNodeChildren(m_Root);
9675 m_Root->type = Node::TYPE_FREE;
9676 m_AllocationCount = 0;
9677 m_FreeCount = 1;
9678 m_SumFreeSize = m_UsableSize;
9679 }
9680
9681 void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
9682 {
9683 uint32_t level = 0;
9684 Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
9685 node->allocation.userData = userData;
9686 }
9687
9688 VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const
9689 {
9690 Node* node = m_Root;
9691 VkDeviceSize nodeOffset = 0;
9692 outLevel = 0;
9693 VkDeviceSize levelNodeSize = LevelToNodeSize(0);
9694 while (node->type == Node::TYPE_SPLIT)
9695 {
9696 const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1;
9697 if (offset < nodeOffset + nextLevelNodeSize)
9698 {
9699 node = node->split.leftChild;
9700 }
9701 else
9702 {
9703 node = node->split.leftChild->buddy;
9704 nodeOffset += nextLevelNodeSize;
9705 }
9706 ++outLevel;
9707 levelNodeSize = nextLevelNodeSize;
9708 }
9709
9710 VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
9711 return node;
9712 }
9713
9714 bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
9715 {
9716 VMA_VALIDATE(level < m_LevelCount);
9717 VMA_VALIDATE(curr->parent == parent);
9718 VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
9719 VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
9720 switch (curr->type)
9721 {
9722 case Node::TYPE_FREE:
9723 // curr->free.prev, next are validated separately.
9724 ctx.calculatedSumFreeSize += levelNodeSize;
9725 ++ctx.calculatedFreeCount;
9726 break;
9727 case Node::TYPE_ALLOCATION:
9728 ++ctx.calculatedAllocationCount;
9729 if (!IsVirtual())
9730 {
9731 VMA_VALIDATE(curr->allocation.userData != VMA_NULL);
9732 }
9733 break;
9734 case Node::TYPE_SPLIT:
9735 {
9736 const uint32_t childrenLevel = level + 1;
9737 const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1;
9738 const Node* const leftChild = curr->split.leftChild;
9739 VMA_VALIDATE(leftChild != VMA_NULL);
9740 VMA_VALIDATE(leftChild->offset == curr->offset);
9741 if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
9742 {
9743 VMA_VALIDATE(false && "ValidateNode for left child failed.");
9744 }
9745 const Node* const rightChild = leftChild->buddy;
9746 VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
9747 if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
9748 {
9749 VMA_VALIDATE(false && "ValidateNode for right child failed.");
9750 }
9751 }
9752 break;
9753 default:
9754 return false;
9755 }
9756
9757 return true;
9758 }
9759
9760 uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
9761 {
9762 // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
9763 uint32_t level = 0;
9764 VkDeviceSize currLevelNodeSize = m_UsableSize;
9765 VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
9766 while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
9767 {
9768 ++level;
9769 currLevelNodeSize >>= 1;
9770 nextLevelNodeSize >>= 1;
9771 }
9772 return level;
9773 }
9774
9775 void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle)
9776 {
9777 uint32_t level = 0;
9778 Node* node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);
9779
9780 ++m_FreeCount;
9781 --m_AllocationCount;
9782 m_SumFreeSize += LevelToNodeSize(level);
9783
9784 node->type = Node::TYPE_FREE;
9785
9786 // Join free nodes if possible.
9787 while (level > 0 && node->buddy->type == Node::TYPE_FREE)
9788 {
9789 RemoveFromFreeList(level, node->buddy);
9790 Node* const parent = node->parent;
9791
9792 m_NodeAllocator.Free(node->buddy);
9793 m_NodeAllocator.Free(node);
9794 parent->type = Node::TYPE_FREE;
9795
9796 node = parent;
9797 --level;
9798 --m_FreeCount;
9799 }
9800
9801 AddToFreeListFront(level, node);
9802 }
9803
9804 void VmaBlockMetadata_Buddy::AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const
9805 {
9806 switch (node->type)
9807 {
9808 case Node::TYPE_FREE:
9809 VmaAddDetailedStatisticsUnusedRange(inoutStats, levelNodeSize);
9810 break;
9811 case Node::TYPE_ALLOCATION:
9812 VmaAddDetailedStatisticsAllocation(inoutStats, levelNodeSize);
9813 break;
9814 case Node::TYPE_SPLIT:
9815 {
9816 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
9817 const Node* const leftChild = node->split.leftChild;
9818 AddNodeToDetailedStatistics(inoutStats, leftChild, childrenNodeSize);
9819 const Node* const rightChild = leftChild->buddy;
9820 AddNodeToDetailedStatistics(inoutStats, rightChild, childrenNodeSize);
9821 }
9822 break;
9823 default:
9824 VMA_ASSERT(0);
9825 }
9826 }
9827
9828 void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
9829 {
9830 VMA_ASSERT(node->type == Node::TYPE_FREE);
9831
9832 // List is empty.
9833 Node* const frontNode = m_FreeList[level].front;
9834 if (frontNode == VMA_NULL)
9835 {
9836 VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
9837 node->free.prev = node->free.next = VMA_NULL;
9838 m_FreeList[level].front = m_FreeList[level].back = node;
9839 }
9840 else
9841 {
9842 VMA_ASSERT(frontNode->free.prev == VMA_NULL);
9843 node->free.prev = VMA_NULL;
9844 node->free.next = frontNode;
9845 frontNode->free.prev = node;
9846 m_FreeList[level].front = node;
9847 }
9848 }
9849
9850 void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
9851 {
9852 VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
9853
9854 // It is at the front.
9855 if (node->free.prev == VMA_NULL)
9856 {
9857 VMA_ASSERT(m_FreeList[level].front == node);
9858 m_FreeList[level].front = node->free.next;
9859 }
9860 else
9861 {
9862 Node* const prevFreeNode = node->free.prev;
9863 VMA_ASSERT(prevFreeNode->free.next == node);
9864 prevFreeNode->free.next = node->free.next;
9865 }
9866
9867 // It is at the back.
9868 if (node->free.next == VMA_NULL)
9869 {
9870 VMA_ASSERT(m_FreeList[level].back == node);
9871 m_FreeList[level].back = node->free.prev;
9872 }
9873 else
9874 {
9875 Node* const nextFreeNode = node->free.next;
9876 VMA_ASSERT(nextFreeNode->free.prev == node);
9877 nextFreeNode->free.prev = node->free.prev;
9878 }
9879 }
9880
9881 void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const
9882 {
9883 switch (node->type)
9884 {
9885 case Node::TYPE_FREE:
9886 break;
9887 case Node::TYPE_ALLOCATION:
9888 DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData);
9889 break;
9890 case Node::TYPE_SPLIT:
9891 {
9892 ++level;
9893 DebugLogAllAllocationNode(node->split.leftChild, level);
9894 DebugLogAllAllocationNode(node->split.leftChild->buddy, level);
9895 }
9896 break;
9897 default:
9898 VMA_ASSERT(0);
9899 }
9900 }
9901
9902 #if VMA_STATS_STRING_ENABLED
9903 void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
9904 {
9905 switch (node->type)
9906 {
9907 case Node::TYPE_FREE:
9908 PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
9909 break;
9910 case Node::TYPE_ALLOCATION:
9911 PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData);
9912 break;
9913 case Node::TYPE_SPLIT:
9914 {
9915 const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
9916 const Node* const leftChild = node->split.leftChild;
9917 PrintDetailedMapNode(json, leftChild, childrenNodeSize);
9918 const Node* const rightChild = leftChild->buddy;
9919 PrintDetailedMapNode(json, rightChild, childrenNodeSize);
9920 }
9921 break;
9922 default:
9923 VMA_ASSERT(0);
9924 }
9925 }
9926 #endif // VMA_STATS_STRING_ENABLED
9927 #endif // _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS
9928 #endif // _VMA_BLOCK_METADATA_BUDDY
9929 #endif // #if 0
9930
9931 #ifndef _VMA_BLOCK_METADATA_TLSF
9932 // To not search current larger region if first allocation won't succeed and skip to smaller range
9933 // use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest().
9934 // When fragmentation and reusal of previous blocks doesn't matter then use with
9935 // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible.
9936 class VmaBlockMetadata_TLSF : public VmaBlockMetadata
9937 {
9938 VMA_CLASS_NO_COPY(VmaBlockMetadata_TLSF)
9939 public:
9940 VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
9941 VkDeviceSize bufferImageGranularity, bool isVirtual);
9942 virtual ~VmaBlockMetadata_TLSF();
9943
GetAllocationCount()9944 size_t GetAllocationCount() const override { return m_AllocCount; }
GetFreeRegionsCount()9945 size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }
GetSumFreeSize()9946 VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }
IsEmpty()9947 bool IsEmpty() const override { return m_NullBlock->offset == 0; }
GetAllocationOffset(VmaAllocHandle allocHandle)9948 VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; };
9949
9950 void Init(VkDeviceSize size) override;
9951 bool Validate() const override;
9952
9953 void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;
9954 void AddStatistics(VmaStatistics& inoutStats) const override;
9955
9956 #if VMA_STATS_STRING_ENABLED
9957 void PrintDetailedMap(class VmaJsonWriter& json) const override;
9958 #endif
9959
9960 bool CreateAllocationRequest(
9961 VkDeviceSize allocSize,
9962 VkDeviceSize allocAlignment,
9963 bool upperAddress,
9964 VmaSuballocationType allocType,
9965 uint32_t strategy,
9966 VmaAllocationRequest* pAllocationRequest) override;
9967
9968 VkResult CheckCorruption(const void* pBlockData) override;
9969 void Alloc(
9970 const VmaAllocationRequest& request,
9971 VmaSuballocationType type,
9972 void* userData) override;
9973
9974 void Free(VmaAllocHandle allocHandle) override;
9975 void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;
9976 void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;
9977 VmaAllocHandle GetAllocationListBegin() const override;
9978 VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;
9979 VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;
9980 void Clear() override;
9981 void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;
9982 void DebugLogAllAllocations() const override;
9983
9984 private:
9985 // According to original paper it should be preferable 4 or 5:
9986 // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"
9987 // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf
9988 static const uint8_t SECOND_LEVEL_INDEX = 5;
9989 static const uint16_t SMALL_BUFFER_SIZE = 256;
9990 static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16;
9991 static const uint8_t MEMORY_CLASS_SHIFT = 7;
9992 static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;
9993
9994 class Block
9995 {
9996 public:
9997 VkDeviceSize offset;
9998 VkDeviceSize size;
9999 Block* prevPhysical;
10000 Block* nextPhysical;
10001
MarkFree()10002 void MarkFree() { prevFree = VMA_NULL; }
MarkTaken()10003 void MarkTaken() { prevFree = this; }
IsFree()10004 bool IsFree() const { return prevFree != this; }
UserData()10005 void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; }
PrevFree()10006 Block*& PrevFree() { return prevFree; }
NextFree()10007 Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; }
10008
10009 private:
10010 Block* prevFree; // Address of the same block here indicates that block is taken
10011 union
10012 {
10013 Block* nextFree;
10014 void* userData;
10015 };
10016 };
10017
10018 size_t m_AllocCount;
10019 // Total number of free blocks besides null block
10020 size_t m_BlocksFreeCount;
10021 // Total size of free blocks excluding null block
10022 VkDeviceSize m_BlocksFreeSize;
10023 uint32_t m_IsFreeBitmap;
10024 uint8_t m_MemoryClasses;
10025 uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];
10026 uint32_t m_ListsCount;
10027 /*
10028 * 0: 0-3 lists for small buffers
10029 * 1+: 0-(2^SLI-1) lists for normal buffers
10030 */
10031 Block** m_FreeList;
10032 VmaPoolAllocator<Block> m_BlockAllocator;
10033 Block* m_NullBlock;
10034 VmaBlockBufferImageGranularity m_GranularityHandler;
10035
10036 uint8_t SizeToMemoryClass(VkDeviceSize size) const;
10037 uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const;
10038 uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const;
10039 uint32_t GetListIndex(VkDeviceSize size) const;
10040
10041 void RemoveFreeBlock(Block* block);
10042 void InsertFreeBlock(Block* block);
10043 void MergeBlock(Block* block, Block* prev);
10044
10045 Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const;
10046 bool CheckBlock(
10047 Block& block,
10048 uint32_t listIndex,
10049 VkDeviceSize allocSize,
10050 VkDeviceSize allocAlignment,
10051 VmaSuballocationType allocType,
10052 VmaAllocationRequest* pAllocationRequest);
10053 };
10054
10055 #ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
VmaBlockMetadata_TLSF(const VkAllocationCallbacks * pAllocationCallbacks,VkDeviceSize bufferImageGranularity,bool isVirtual)10056 VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,
10057 VkDeviceSize bufferImageGranularity, bool isVirtual)
10058 : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),
10059 m_AllocCount(0),
10060 m_BlocksFreeCount(0),
10061 m_BlocksFreeSize(0),
10062 m_IsFreeBitmap(0),
10063 m_MemoryClasses(0),
10064 m_ListsCount(0),
10065 m_FreeList(VMA_NULL),
10066 m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT),
10067 m_NullBlock(VMA_NULL),
10068 m_GranularityHandler(bufferImageGranularity) {}
10069
~VmaBlockMetadata_TLSF()10070 VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF()
10071 {
10072 if (m_FreeList)
10073 vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount);
10074 m_GranularityHandler.Destroy(GetAllocationCallbacks());
10075 }
10076
Init(VkDeviceSize size)10077 void VmaBlockMetadata_TLSF::Init(VkDeviceSize size)
10078 {
10079 VmaBlockMetadata::Init(size);
10080
10081 if (!IsVirtual())
10082 m_GranularityHandler.Init(GetAllocationCallbacks(), size);
10083
10084 m_NullBlock = m_BlockAllocator.Alloc();
10085 m_NullBlock->size = size;
10086 m_NullBlock->offset = 0;
10087 m_NullBlock->prevPhysical = VMA_NULL;
10088 m_NullBlock->nextPhysical = VMA_NULL;
10089 m_NullBlock->MarkFree();
10090 m_NullBlock->NextFree() = VMA_NULL;
10091 m_NullBlock->PrevFree() = VMA_NULL;
10092 uint8_t memoryClass = SizeToMemoryClass(size);
10093 uint16_t sli = SizeToSecondIndex(size, memoryClass);
10094 m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;
10095 if (IsVirtual())
10096 m_ListsCount += 1UL << SECOND_LEVEL_INDEX;
10097 else
10098 m_ListsCount += 4;
10099
10100 m_MemoryClasses = memoryClass + 2;
10101 memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t));
10102
10103 m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount);
10104 memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
10105 }
10106
Validate()10107 bool VmaBlockMetadata_TLSF::Validate() const
10108 {
10109 VMA_VALIDATE(GetSumFreeSize() <= GetSize());
10110
10111 VkDeviceSize calculatedSize = m_NullBlock->size;
10112 VkDeviceSize calculatedFreeSize = m_NullBlock->size;
10113 size_t allocCount = 0;
10114 size_t freeCount = 0;
10115
10116 // Check integrity of free lists
10117 for (uint32_t list = 0; list < m_ListsCount; ++list)
10118 {
10119 Block* block = m_FreeList[list];
10120 if (block != VMA_NULL)
10121 {
10122 VMA_VALIDATE(block->IsFree());
10123 VMA_VALIDATE(block->PrevFree() == VMA_NULL);
10124 while (block->NextFree())
10125 {
10126 VMA_VALIDATE(block->NextFree()->IsFree());
10127 VMA_VALIDATE(block->NextFree()->PrevFree() == block);
10128 block = block->NextFree();
10129 }
10130 }
10131 }
10132
10133 VkDeviceSize nextOffset = m_NullBlock->offset;
10134 auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual());
10135
10136 VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL);
10137 if (m_NullBlock->prevPhysical)
10138 {
10139 VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);
10140 }
10141 // Check all blocks
10142 for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical)
10143 {
10144 VMA_VALIDATE(prev->offset + prev->size == nextOffset);
10145 nextOffset = prev->offset;
10146 calculatedSize += prev->size;
10147
10148 uint32_t listIndex = GetListIndex(prev->size);
10149 if (prev->IsFree())
10150 {
10151 ++freeCount;
10152 // Check if free block belongs to free list
10153 Block* freeBlock = m_FreeList[listIndex];
10154 VMA_VALIDATE(freeBlock != VMA_NULL);
10155
10156 bool found = false;
10157 do
10158 {
10159 if (freeBlock == prev)
10160 found = true;
10161
10162 freeBlock = freeBlock->NextFree();
10163 } while (!found && freeBlock != VMA_NULL);
10164
10165 VMA_VALIDATE(found);
10166 calculatedFreeSize += prev->size;
10167 }
10168 else
10169 {
10170 ++allocCount;
10171 // Check if taken block is not on a free list
10172 Block* freeBlock = m_FreeList[listIndex];
10173 while (freeBlock)
10174 {
10175 VMA_VALIDATE(freeBlock != prev);
10176 freeBlock = freeBlock->NextFree();
10177 }
10178
10179 if (!IsVirtual())
10180 {
10181 VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size));
10182 }
10183 }
10184
10185 if (prev->prevPhysical)
10186 {
10187 VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev);
10188 }
10189 }
10190
10191 if (!IsVirtual())
10192 {
10193 VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx));
10194 }
10195
10196 VMA_VALIDATE(nextOffset == 0);
10197 VMA_VALIDATE(calculatedSize == GetSize());
10198 VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize());
10199 VMA_VALIDATE(allocCount == m_AllocCount);
10200 VMA_VALIDATE(freeCount == m_BlocksFreeCount);
10201
10202 return true;
10203 }
10204
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)10205 void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const
10206 {
10207 inoutStats.statistics.blockCount++;
10208 inoutStats.statistics.blockBytes += GetSize();
10209 if (m_NullBlock->size > 0)
10210 VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);
10211
10212 for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10213 {
10214 if (block->IsFree())
10215 VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size);
10216 else
10217 VmaAddDetailedStatisticsAllocation(inoutStats, block->size);
10218 }
10219 }
10220
AddStatistics(VmaStatistics & inoutStats)10221 void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const
10222 {
10223 inoutStats.blockCount++;
10224 inoutStats.allocationCount += (uint32_t)m_AllocCount;
10225 inoutStats.blockBytes += GetSize();
10226 inoutStats.allocationBytes += GetSize() - GetSumFreeSize();
10227 }
10228
10229 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)10230 void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const
10231 {
10232 size_t blockCount = m_AllocCount + m_BlocksFreeCount;
10233 VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
10234 VmaVector<Block*, VmaStlAllocator<Block*>> blockList(blockCount, allocator);
10235
10236 size_t i = blockCount;
10237 for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10238 {
10239 blockList[--i] = block;
10240 }
10241 VMA_ASSERT(i == 0);
10242
10243 VmaDetailedStatistics stats;
10244 VmaClearDetailedStatistics(stats);
10245 AddDetailedStatistics(stats);
10246
10247 PrintDetailedMap_Begin(json,
10248 stats.statistics.blockBytes - stats.statistics.allocationBytes,
10249 stats.statistics.allocationCount,
10250 stats.unusedRangeCount);
10251
10252 for (; i < blockCount; ++i)
10253 {
10254 Block* block = blockList[i];
10255 if (block->IsFree())
10256 PrintDetailedMap_UnusedRange(json, block->offset, block->size);
10257 else
10258 PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData());
10259 }
10260 if (m_NullBlock->size > 0)
10261 PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size);
10262
10263 PrintDetailedMap_End(json);
10264 }
10265 #endif
10266
CreateAllocationRequest(VkDeviceSize allocSize,VkDeviceSize allocAlignment,bool upperAddress,VmaSuballocationType allocType,uint32_t strategy,VmaAllocationRequest * pAllocationRequest)10267 bool VmaBlockMetadata_TLSF::CreateAllocationRequest(
10268 VkDeviceSize allocSize,
10269 VkDeviceSize allocAlignment,
10270 bool upperAddress,
10271 VmaSuballocationType allocType,
10272 uint32_t strategy,
10273 VmaAllocationRequest* pAllocationRequest)
10274 {
10275 VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");
10276 VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
10277
10278 // For small granularity round up
10279 if (!IsVirtual())
10280 m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment);
10281
10282 allocSize += GetDebugMargin();
10283 // Quick check for too small pool
10284 if (allocSize > GetSumFreeSize())
10285 return false;
10286
10287 // If no free blocks in pool then check only null block
10288 if (m_BlocksFreeCount == 0)
10289 return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest);
10290
10291 // Round up to the next block
10292 VkDeviceSize sizeForNextList = allocSize;
10293 VkDeviceSize smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4);
10294 if (allocSize > SMALL_BUFFER_SIZE)
10295 {
10296 sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX));
10297 }
10298 else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)
10299 sizeForNextList = SMALL_BUFFER_SIZE + 1;
10300 else
10301 sizeForNextList += smallSizeStep;
10302
10303 uint32_t nextListIndex = 0;
10304 uint32_t prevListIndex = 0;
10305 Block* nextListBlock = VMA_NULL;
10306 Block* prevListBlock = VMA_NULL;
10307
10308 // Check blocks according to strategies
10309 if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)
10310 {
10311 // Quick check for larger block first
10312 nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
10313 if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10314 return true;
10315
10316 // If not fitted then null block
10317 if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10318 return true;
10319
10320 // Null block failed, search larger bucket
10321 while (nextListBlock)
10322 {
10323 if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10324 return true;
10325 nextListBlock = nextListBlock->NextFree();
10326 }
10327
10328 // Failed again, check best fit bucket
10329 prevListBlock = FindFreeBlock(allocSize, prevListIndex);
10330 while (prevListBlock)
10331 {
10332 if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10333 return true;
10334 prevListBlock = prevListBlock->NextFree();
10335 }
10336 }
10337 else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)
10338 {
10339 // Check best fit bucket
10340 prevListBlock = FindFreeBlock(allocSize, prevListIndex);
10341 while (prevListBlock)
10342 {
10343 if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10344 return true;
10345 prevListBlock = prevListBlock->NextFree();
10346 }
10347
10348 // If failed check null block
10349 if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10350 return true;
10351
10352 // Check larger bucket
10353 nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
10354 while (nextListBlock)
10355 {
10356 if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10357 return true;
10358 nextListBlock = nextListBlock->NextFree();
10359 }
10360 }
10361 else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )
10362 {
10363 // Perform search from the start
10364 VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
10365 VmaVector<Block*, VmaStlAllocator<Block*>> blockList(m_BlocksFreeCount, allocator);
10366
10367 size_t i = m_BlocksFreeCount;
10368 for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10369 {
10370 if (block->IsFree() && block->size >= allocSize)
10371 blockList[--i] = block;
10372 }
10373
10374 for (; i < m_BlocksFreeCount; ++i)
10375 {
10376 Block& block = *blockList[i];
10377 if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest))
10378 return true;
10379 }
10380
10381 // If failed check null block
10382 if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10383 return true;
10384
10385 // Whole range searched, no more memory
10386 return false;
10387 }
10388 else
10389 {
10390 // Check larger bucket
10391 nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);
10392 while (nextListBlock)
10393 {
10394 if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10395 return true;
10396 nextListBlock = nextListBlock->NextFree();
10397 }
10398
10399 // If failed check null block
10400 if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))
10401 return true;
10402
10403 // Check best fit bucket
10404 prevListBlock = FindFreeBlock(allocSize, prevListIndex);
10405 while (prevListBlock)
10406 {
10407 if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10408 return true;
10409 prevListBlock = prevListBlock->NextFree();
10410 }
10411 }
10412
10413 // Worst case, full search has to be done
10414 while (++nextListIndex < m_ListsCount)
10415 {
10416 nextListBlock = m_FreeList[nextListIndex];
10417 while (nextListBlock)
10418 {
10419 if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))
10420 return true;
10421 nextListBlock = nextListBlock->NextFree();
10422 }
10423 }
10424
10425 // No more memory sadly
10426 return false;
10427 }
10428
CheckCorruption(const void * pBlockData)10429 VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData)
10430 {
10431 for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10432 {
10433 if (!block->IsFree())
10434 {
10435 if (!VmaValidateMagicValue(pBlockData, block->offset + block->size))
10436 {
10437 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
10438 return VK_ERROR_UNKNOWN_COPY;
10439 }
10440 }
10441 }
10442
10443 return VK_SUCCESS;
10444 }
10445
Alloc(const VmaAllocationRequest & request,VmaSuballocationType type,void * userData)10446 void VmaBlockMetadata_TLSF::Alloc(
10447 const VmaAllocationRequest& request,
10448 VmaSuballocationType type,
10449 void* userData)
10450 {
10451 VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF);
10452
10453 // Get block and pop it from the free list
10454 Block* currentBlock = (Block*)request.allocHandle;
10455 VkDeviceSize offset = request.algorithmData;
10456 VMA_ASSERT(currentBlock != VMA_NULL);
10457 VMA_ASSERT(currentBlock->offset <= offset);
10458
10459 if (currentBlock != m_NullBlock)
10460 RemoveFreeBlock(currentBlock);
10461
10462 VkDeviceSize debugMargin = GetDebugMargin();
10463 VkDeviceSize misssingAlignment = offset - currentBlock->offset;
10464
10465 // Append missing alignment to prev block or create new one
10466 if (misssingAlignment)
10467 {
10468 Block* prevBlock = currentBlock->prevPhysical;
10469 VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!");
10470
10471 if (prevBlock->IsFree() && prevBlock->size != debugMargin)
10472 {
10473 uint32_t oldList = GetListIndex(prevBlock->size);
10474 prevBlock->size += misssingAlignment;
10475 // Check if new size crosses list bucket
10476 if (oldList != GetListIndex(prevBlock->size))
10477 {
10478 prevBlock->size -= misssingAlignment;
10479 RemoveFreeBlock(prevBlock);
10480 prevBlock->size += misssingAlignment;
10481 InsertFreeBlock(prevBlock);
10482 }
10483 else
10484 m_BlocksFreeSize += misssingAlignment;
10485 }
10486 else
10487 {
10488 Block* newBlock = m_BlockAllocator.Alloc();
10489 currentBlock->prevPhysical = newBlock;
10490 prevBlock->nextPhysical = newBlock;
10491 newBlock->prevPhysical = prevBlock;
10492 newBlock->nextPhysical = currentBlock;
10493 newBlock->size = misssingAlignment;
10494 newBlock->offset = currentBlock->offset;
10495 newBlock->MarkTaken();
10496
10497 InsertFreeBlock(newBlock);
10498 }
10499
10500 currentBlock->size -= misssingAlignment;
10501 currentBlock->offset += misssingAlignment;
10502 }
10503
10504 VkDeviceSize size = request.size + debugMargin;
10505 if (currentBlock->size == size)
10506 {
10507 if (currentBlock == m_NullBlock)
10508 {
10509 // Setup new null block
10510 m_NullBlock = m_BlockAllocator.Alloc();
10511 m_NullBlock->size = 0;
10512 m_NullBlock->offset = currentBlock->offset + size;
10513 m_NullBlock->prevPhysical = currentBlock;
10514 m_NullBlock->nextPhysical = VMA_NULL;
10515 m_NullBlock->MarkFree();
10516 m_NullBlock->PrevFree() = VMA_NULL;
10517 m_NullBlock->NextFree() = VMA_NULL;
10518 currentBlock->nextPhysical = m_NullBlock;
10519 currentBlock->MarkTaken();
10520 }
10521 }
10522 else
10523 {
10524 VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");
10525
10526 // Create new free block
10527 Block* newBlock = m_BlockAllocator.Alloc();
10528 newBlock->size = currentBlock->size - size;
10529 newBlock->offset = currentBlock->offset + size;
10530 newBlock->prevPhysical = currentBlock;
10531 newBlock->nextPhysical = currentBlock->nextPhysical;
10532 currentBlock->nextPhysical = newBlock;
10533 currentBlock->size = size;
10534
10535 if (currentBlock == m_NullBlock)
10536 {
10537 m_NullBlock = newBlock;
10538 m_NullBlock->MarkFree();
10539 m_NullBlock->NextFree() = VMA_NULL;
10540 m_NullBlock->PrevFree() = VMA_NULL;
10541 currentBlock->MarkTaken();
10542 }
10543 else
10544 {
10545 newBlock->nextPhysical->prevPhysical = newBlock;
10546 newBlock->MarkTaken();
10547 InsertFreeBlock(newBlock);
10548 }
10549 }
10550 currentBlock->UserData() = userData;
10551
10552 if (debugMargin > 0)
10553 {
10554 currentBlock->size -= debugMargin;
10555 Block* newBlock = m_BlockAllocator.Alloc();
10556 newBlock->size = debugMargin;
10557 newBlock->offset = currentBlock->offset + currentBlock->size;
10558 newBlock->prevPhysical = currentBlock;
10559 newBlock->nextPhysical = currentBlock->nextPhysical;
10560 newBlock->MarkTaken();
10561 currentBlock->nextPhysical->prevPhysical = newBlock;
10562 currentBlock->nextPhysical = newBlock;
10563 InsertFreeBlock(newBlock);
10564 }
10565
10566 if (!IsVirtual())
10567 m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData,
10568 currentBlock->offset, currentBlock->size);
10569 ++m_AllocCount;
10570 }
10571
Free(VmaAllocHandle allocHandle)10572 void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle)
10573 {
10574 Block* block = (Block*)allocHandle;
10575 Block* next = block->nextPhysical;
10576 VMA_ASSERT(!block->IsFree() && "Block is already free!");
10577
10578 if (!IsVirtual())
10579 m_GranularityHandler.FreePages(block->offset, block->size);
10580 --m_AllocCount;
10581
10582 VkDeviceSize debugMargin = GetDebugMargin();
10583 if (debugMargin > 0)
10584 {
10585 RemoveFreeBlock(next);
10586 MergeBlock(next, block);
10587 block = next;
10588 next = next->nextPhysical;
10589 }
10590
10591 // Try merging
10592 Block* prev = block->prevPhysical;
10593 if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin)
10594 {
10595 RemoveFreeBlock(prev);
10596 MergeBlock(block, prev);
10597 }
10598
10599 if (!next->IsFree())
10600 InsertFreeBlock(block);
10601 else if (next == m_NullBlock)
10602 MergeBlock(m_NullBlock, block);
10603 else
10604 {
10605 RemoveFreeBlock(next);
10606 MergeBlock(next, block);
10607 InsertFreeBlock(next);
10608 }
10609 }
10610
GetAllocationInfo(VmaAllocHandle allocHandle,VmaVirtualAllocationInfo & outInfo)10611 void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)
10612 {
10613 Block* block = (Block*)allocHandle;
10614 VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");
10615 outInfo.offset = block->offset;
10616 outInfo.size = block->size;
10617 outInfo.pUserData = block->UserData();
10618 }
10619
GetAllocationUserData(VmaAllocHandle allocHandle)10620 void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const
10621 {
10622 Block* block = (Block*)allocHandle;
10623 VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");
10624 return block->UserData();
10625 }
10626
GetAllocationListBegin()10627 VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const
10628 {
10629 if (m_AllocCount == 0)
10630 return VK_NULL_HANDLE;
10631
10632 for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)
10633 {
10634 if (!block->IsFree())
10635 return (VmaAllocHandle)block;
10636 }
10637 VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");
10638 return VK_NULL_HANDLE;
10639 }
10640
GetNextAllocation(VmaAllocHandle prevAlloc)10641 VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const
10642 {
10643 Block* startBlock = (Block*)prevAlloc;
10644 VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!");
10645
10646 for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)
10647 {
10648 if (!block->IsFree())
10649 return (VmaAllocHandle)block;
10650 }
10651 return VK_NULL_HANDLE;
10652 }
10653
GetNextFreeRegionSize(VmaAllocHandle alloc)10654 VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const
10655 {
10656 Block* block = (Block*)alloc;
10657 VMA_ASSERT(!block->IsFree() && "Incorrect block!");
10658
10659 if (block->prevPhysical)
10660 return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;
10661 return 0;
10662 }
10663
Clear()10664 void VmaBlockMetadata_TLSF::Clear()
10665 {
10666 m_AllocCount = 0;
10667 m_BlocksFreeCount = 0;
10668 m_BlocksFreeSize = 0;
10669 m_IsFreeBitmap = 0;
10670 m_NullBlock->offset = 0;
10671 m_NullBlock->size = GetSize();
10672 Block* block = m_NullBlock->prevPhysical;
10673 m_NullBlock->prevPhysical = VMA_NULL;
10674 while (block)
10675 {
10676 Block* prev = block->prevPhysical;
10677 m_BlockAllocator.Free(block);
10678 block = prev;
10679 }
10680 memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));
10681 memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t));
10682 m_GranularityHandler.Clear();
10683 }
10684
SetAllocationUserData(VmaAllocHandle allocHandle,void * userData)10685 void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)
10686 {
10687 Block* block = (Block*)allocHandle;
10688 VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");
10689 block->UserData() = userData;
10690 }
10691
DebugLogAllAllocations()10692 void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const
10693 {
10694 for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)
10695 if (!block->IsFree())
10696 DebugLogAllocation(block->offset, block->size, block->UserData());
10697 }
10698
SizeToMemoryClass(VkDeviceSize size)10699 uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const
10700 {
10701 if (size > SMALL_BUFFER_SIZE)
10702 return VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT;
10703 return 0;
10704 }
10705
SizeToSecondIndex(VkDeviceSize size,uint8_t memoryClass)10706 uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const
10707 {
10708 if (memoryClass == 0)
10709 {
10710 if (IsVirtual())
10711 return static_cast<uint16_t>((size - 1) / 8);
10712 else
10713 return static_cast<uint16_t>((size - 1) / 64);
10714 }
10715 return static_cast<uint16_t>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));
10716 }
10717
GetListIndex(uint8_t memoryClass,uint16_t secondIndex)10718 uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const
10719 {
10720 if (memoryClass == 0)
10721 return secondIndex;
10722
10723 const uint32_t index = static_cast<uint32_t>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;
10724 if (IsVirtual())
10725 return index + (1 << SECOND_LEVEL_INDEX);
10726 else
10727 return index + 4;
10728 }
10729
GetListIndex(VkDeviceSize size)10730 uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const
10731 {
10732 uint8_t memoryClass = SizeToMemoryClass(size);
10733 return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));
10734 }
10735
RemoveFreeBlock(Block * block)10736 void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block)
10737 {
10738 VMA_ASSERT(block != m_NullBlock);
10739 VMA_ASSERT(block->IsFree());
10740
10741 if (block->NextFree() != VMA_NULL)
10742 block->NextFree()->PrevFree() = block->PrevFree();
10743 if (block->PrevFree() != VMA_NULL)
10744 block->PrevFree()->NextFree() = block->NextFree();
10745 else
10746 {
10747 uint8_t memClass = SizeToMemoryClass(block->size);
10748 uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
10749 uint32_t index = GetListIndex(memClass, secondIndex);
10750 VMA_ASSERT(m_FreeList[index] == block);
10751 m_FreeList[index] = block->NextFree();
10752 if (block->NextFree() == VMA_NULL)
10753 {
10754 m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);
10755 if (m_InnerIsFreeBitmap[memClass] == 0)
10756 m_IsFreeBitmap &= ~(1UL << memClass);
10757 }
10758 }
10759 block->MarkTaken();
10760 block->UserData() = VMA_NULL;
10761 --m_BlocksFreeCount;
10762 m_BlocksFreeSize -= block->size;
10763 }
10764
InsertFreeBlock(Block * block)10765 void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block)
10766 {
10767 VMA_ASSERT(block != m_NullBlock);
10768 VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!");
10769
10770 uint8_t memClass = SizeToMemoryClass(block->size);
10771 uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);
10772 uint32_t index = GetListIndex(memClass, secondIndex);
10773 VMA_ASSERT(index < m_ListsCount);
10774 block->PrevFree() = VMA_NULL;
10775 block->NextFree() = m_FreeList[index];
10776 m_FreeList[index] = block;
10777 if (block->NextFree() != VMA_NULL)
10778 block->NextFree()->PrevFree() = block;
10779 else
10780 {
10781 m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;
10782 m_IsFreeBitmap |= 1UL << memClass;
10783 }
10784 ++m_BlocksFreeCount;
10785 m_BlocksFreeSize += block->size;
10786 }
10787
MergeBlock(Block * block,Block * prev)10788 void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)
10789 {
10790 VMA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!");
10791 VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");
10792
10793 block->offset = prev->offset;
10794 block->size += prev->size;
10795 block->prevPhysical = prev->prevPhysical;
10796 if (block->prevPhysical)
10797 block->prevPhysical->nextPhysical = block;
10798 m_BlockAllocator.Free(prev);
10799 }
10800
FindFreeBlock(VkDeviceSize size,uint32_t & listIndex)10801 VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const
10802 {
10803 uint8_t memoryClass = SizeToMemoryClass(size);
10804 uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));
10805 if (!innerFreeMap)
10806 {
10807 // Check higher levels for avaiable blocks
10808 uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));
10809 if (!freeMap)
10810 return VMA_NULL; // No more memory avaible
10811
10812 // Find lowest free region
10813 memoryClass = VMA_BITSCAN_LSB(freeMap);
10814 innerFreeMap = m_InnerIsFreeBitmap[memoryClass];
10815 VMA_ASSERT(innerFreeMap != 0);
10816 }
10817 // Find lowest free subregion
10818 listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap));
10819 VMA_ASSERT(m_FreeList[listIndex]);
10820 return m_FreeList[listIndex];
10821 }
10822
CheckBlock(Block & block,uint32_t listIndex,VkDeviceSize allocSize,VkDeviceSize allocAlignment,VmaSuballocationType allocType,VmaAllocationRequest * pAllocationRequest)10823 bool VmaBlockMetadata_TLSF::CheckBlock(
10824 Block& block,
10825 uint32_t listIndex,
10826 VkDeviceSize allocSize,
10827 VkDeviceSize allocAlignment,
10828 VmaSuballocationType allocType,
10829 VmaAllocationRequest* pAllocationRequest)
10830 {
10831 VMA_ASSERT(block.IsFree() && "Block is already taken!");
10832
10833 VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment);
10834 if (block.size < allocSize + alignedOffset - block.offset)
10835 return false;
10836
10837 // Check for granularity conflicts
10838 if (!IsVirtual() &&
10839 m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType))
10840 return false;
10841
10842 // Alloc successful
10843 pAllocationRequest->type = VmaAllocationRequestType::TLSF;
10844 pAllocationRequest->allocHandle = (VmaAllocHandle)█
10845 pAllocationRequest->size = allocSize - GetDebugMargin();
10846 pAllocationRequest->customData = (void*)allocType;
10847 pAllocationRequest->algorithmData = alignedOffset;
10848
10849 // Place block at the start of list if it's normal block
10850 if (listIndex != m_ListsCount && block.PrevFree())
10851 {
10852 block.PrevFree()->NextFree() = block.NextFree();
10853 if (block.NextFree())
10854 block.NextFree()->PrevFree() = block.PrevFree();
10855 block.PrevFree() = VMA_NULL;
10856 block.NextFree() = m_FreeList[listIndex];
10857 m_FreeList[listIndex] = █
10858 if (block.NextFree())
10859 block.NextFree()->PrevFree() = █
10860 }
10861
10862 return true;
10863 }
10864 #endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS
10865 #endif // _VMA_BLOCK_METADATA_TLSF
10866
10867 #ifndef _VMA_BLOCK_VECTOR
10868 /*
10869 Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
10870 Vulkan memory type.
10871
10872 Synchronized internally with a mutex.
10873 */
10874 class VmaBlockVector
10875 {
10876 friend struct VmaDefragmentationContext_T;
10877 VMA_CLASS_NO_COPY(VmaBlockVector)
10878 public:
10879 VmaBlockVector(
10880 VmaAllocator hAllocator,
10881 VmaPool hParentPool,
10882 uint32_t memoryTypeIndex,
10883 VkDeviceSize preferredBlockSize,
10884 size_t minBlockCount,
10885 size_t maxBlockCount,
10886 VkDeviceSize bufferImageGranularity,
10887 bool explicitBlockSize,
10888 uint32_t algorithm,
10889 float priority,
10890 VkDeviceSize minAllocationAlignment,
10891 void* pMemoryAllocateNext);
10892 ~VmaBlockVector();
10893
GetAllocator()10894 VmaAllocator GetAllocator() const { return m_hAllocator; }
GetParentPool()10895 VmaPool GetParentPool() const { return m_hParentPool; }
IsCustomPool()10896 bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }
GetMemoryTypeIndex()10897 uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
GetPreferredBlockSize()10898 VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
GetBufferImageGranularity()10899 VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
GetAlgorithm()10900 uint32_t GetAlgorithm() const { return m_Algorithm; }
HasExplicitBlockSize()10901 bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }
GetPriority()10902 float GetPriority() const { return m_Priority; }
GetAllocationNextPtr()10903 const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }
10904 // To be used only while the m_Mutex is locked. Used during defragmentation.
GetBlockCount()10905 size_t GetBlockCount() const { return m_Blocks.size(); }
10906 // To be used only while the m_Mutex is locked. Used during defragmentation.
GetBlock(size_t index)10907 VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
GetMutex()10908 VMA_RW_MUTEX &GetMutex() { return m_Mutex; }
10909
10910 VkResult CreateMinBlocks();
10911 void AddStatistics(VmaStatistics& inoutStats);
10912 void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);
10913 bool IsEmpty();
10914 bool IsCorruptionDetectionEnabled() const;
10915
10916 VkResult Allocate(
10917 VkDeviceSize size,
10918 VkDeviceSize alignment,
10919 const VmaAllocationCreateInfo& createInfo,
10920 VmaSuballocationType suballocType,
10921 size_t allocationCount,
10922 VmaAllocation* pAllocations);
10923
10924 void Free(const VmaAllocation hAllocation);
10925
10926 void FreeEmptyBlock();
10927
10928 #if VMA_STATS_STRING_ENABLED
10929 void PrintDetailedMap(class VmaJsonWriter& json);
10930 #endif
10931
10932 VkResult CheckCorruption();
10933
10934 private:
10935 const VmaAllocator m_hAllocator;
10936 const VmaPool m_hParentPool;
10937 const uint32_t m_MemoryTypeIndex;
10938 const VkDeviceSize m_PreferredBlockSize;
10939 const size_t m_MinBlockCount;
10940 const size_t m_MaxBlockCount;
10941 const VkDeviceSize m_BufferImageGranularity;
10942 const bool m_ExplicitBlockSize;
10943 const uint32_t m_Algorithm;
10944 const float m_Priority;
10945 const VkDeviceSize m_MinAllocationAlignment;
10946
10947 void* const m_pMemoryAllocateNext;
10948 VMA_RW_MUTEX m_Mutex;
10949 // Incrementally sorted by sumFreeSize, ascending.
10950 VmaVector<VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*>> m_Blocks;
10951 uint32_t m_NextBlockId;
10952 bool m_IncrementalSort = true;
10953
SetIncrementalSort(bool val)10954 void SetIncrementalSort(bool val) { m_IncrementalSort = val; }
10955
10956 VkDeviceSize CalcMaxBlockSize() const;
10957 // Finds and removes given block from vector.
10958 void Remove(VmaDeviceMemoryBlock* pBlock);
10959 // Performs single step in sorting m_Blocks. They may not be fully sorted
10960 // after this call.
10961 void IncrementallySortBlocks();
10962 void SortByFreeSize();
10963
10964 VkResult AllocatePage(
10965 VkDeviceSize size,
10966 VkDeviceSize alignment,
10967 const VmaAllocationCreateInfo& createInfo,
10968 VmaSuballocationType suballocType,
10969 VmaAllocation* pAllocation);
10970
10971 VkResult AllocateFromBlock(
10972 VmaDeviceMemoryBlock* pBlock,
10973 VkDeviceSize size,
10974 VkDeviceSize alignment,
10975 VmaAllocationCreateFlags allocFlags,
10976 void* pUserData,
10977 VmaSuballocationType suballocType,
10978 uint32_t strategy,
10979 VmaAllocation* pAllocation);
10980
10981 VkResult CommitAllocationRequest(
10982 VmaAllocationRequest& allocRequest,
10983 VmaDeviceMemoryBlock* pBlock,
10984 VkDeviceSize alignment,
10985 VmaAllocationCreateFlags allocFlags,
10986 void* pUserData,
10987 VmaSuballocationType suballocType,
10988 VmaAllocation* pAllocation);
10989
10990 VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
10991 bool HasEmptyBlock();
10992 };
10993 #endif // _VMA_BLOCK_VECTOR
10994
10995 #ifndef _VMA_DEFRAGMENTATION_CONTEXT
10996 struct VmaDefragmentationContext_T
10997 {
10998 VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
10999 public:
11000 VmaDefragmentationContext_T(
11001 VmaAllocator hAllocator,
11002 const VmaDefragmentationInfo& info);
11003 ~VmaDefragmentationContext_T();
11004
GetStatsVmaDefragmentationContext_T11005 void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; }
11006
11007 VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo);
11008 VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo);
11009
11010 private:
11011 // Max number of allocations to ignore due to size constraints before ending single pass
11012 static const uint8_t MAX_ALLOCS_TO_IGNORE = 16;
11013 enum class CounterStatus { Pass, Ignore, End };
11014
11015 struct FragmentedBlock
11016 {
11017 uint32_t data;
11018 VmaDeviceMemoryBlock* block;
11019 };
11020 struct StateBalanced
11021 {
11022 VkDeviceSize avgFreeSize = 0;
11023 VkDeviceSize avgAllocSize = UINT64_MAX;
11024 };
11025 struct StateExtensive
11026 {
11027 enum class Operation : uint8_t
11028 {
11029 FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll,
11030 MoveBuffers, MoveTextures, MoveAll,
11031 Cleanup, Done
11032 };
11033
11034 Operation operation = Operation::FindFreeBlockTexture;
11035 size_t firstFreeBlock = SIZE_MAX;
11036 };
11037 struct MoveAllocationData
11038 {
11039 VkDeviceSize size;
11040 VkDeviceSize alignment;
11041 VmaSuballocationType type;
11042 VmaAllocationCreateFlags flags;
11043 VmaDefragmentationMove move = {};
11044 };
11045
11046 const VkDeviceSize m_MaxPassBytes;
11047 const uint32_t m_MaxPassAllocations;
11048
11049 VmaStlAllocator<VmaDefragmentationMove> m_MoveAllocator;
11050 VmaVector<VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove>> m_Moves;
11051
11052 uint8_t m_IgnoredAllocs = 0;
11053 uint32_t m_Algorithm;
11054 uint32_t m_BlockVectorCount;
11055 VmaBlockVector* m_PoolBlockVector;
11056 VmaBlockVector** m_pBlockVectors;
11057 size_t m_ImmovableBlockCount = 0;
11058 VmaDefragmentationStats m_GlobalStats = { 0 };
11059 VmaDefragmentationStats m_PassStats = { 0 };
11060 void* m_AlgorithmState = VMA_NULL;
11061
11062 static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata);
11063 CounterStatus CheckCounters(VkDeviceSize bytes);
11064 bool IncrementCounters(VkDeviceSize bytes);
11065 bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block);
11066 bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector);
11067
11068 bool ComputeDefragmentation(VmaBlockVector& vector, size_t index);
11069 bool ComputeDefragmentation_Fast(VmaBlockVector& vector);
11070 bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update);
11071 bool ComputeDefragmentation_Full(VmaBlockVector& vector);
11072 bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index);
11073
11074 void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state);
11075 bool MoveDataToFreeBlocks(VmaSuballocationType currentType,
11076 VmaBlockVector& vector, size_t firstFreeBlock,
11077 bool& texturePresent, bool& bufferPresent, bool& otherPresent);
11078 };
11079 #endif // _VMA_DEFRAGMENTATION_CONTEXT
11080
11081 #ifndef _VMA_POOL_T
11082 struct VmaPool_T
11083 {
11084 friend struct VmaPoolListItemTraits;
11085 VMA_CLASS_NO_COPY(VmaPool_T)
11086 public:
11087 VmaBlockVector m_BlockVector;
11088 VmaDedicatedAllocationList m_DedicatedAllocations;
11089
11090 VmaPool_T(
11091 VmaAllocator hAllocator,
11092 const VmaPoolCreateInfo& createInfo,
11093 VkDeviceSize preferredBlockSize);
11094 ~VmaPool_T();
11095
GetIdVmaPool_T11096 uint32_t GetId() const { return m_Id; }
SetIdVmaPool_T11097 void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
11098
GetNameVmaPool_T11099 const char* GetName() const { return m_Name; }
11100 void SetName(const char* pName);
11101
11102 #if VMA_STATS_STRING_ENABLED
11103 //void PrintDetailedMap(class VmaStringBuilder& sb);
11104 #endif
11105
11106 private:
11107 uint32_t m_Id;
11108 char* m_Name;
11109 VmaPool_T* m_PrevPool = VMA_NULL;
11110 VmaPool_T* m_NextPool = VMA_NULL;
11111 };
11112
11113 struct VmaPoolListItemTraits
11114 {
11115 typedef VmaPool_T ItemType;
11116
GetPrevVmaPoolListItemTraits11117 static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }
GetNextVmaPoolListItemTraits11118 static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }
AccessPrevVmaPoolListItemTraits11119 static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }
AccessNextVmaPoolListItemTraits11120 static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }
11121 };
11122 #endif // _VMA_POOL_T
11123
11124 #ifndef _VMA_CURRENT_BUDGET_DATA
11125 struct VmaCurrentBudgetData
11126 {
11127 VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS];
11128 VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS];
11129 VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];
11130 VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];
11131
11132 #if VMA_MEMORY_BUDGET
11133 VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;
11134 VMA_RW_MUTEX m_BudgetMutex;
11135 uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];
11136 uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];
11137 uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];
11138 #endif // VMA_MEMORY_BUDGET
11139
11140 VmaCurrentBudgetData();
11141
11142 void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
11143 void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);
11144 };
11145
11146 #ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
VmaCurrentBudgetData()11147 VmaCurrentBudgetData::VmaCurrentBudgetData()
11148 {
11149 for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)
11150 {
11151 m_BlockCount[heapIndex] = 0;
11152 m_AllocationCount[heapIndex] = 0;
11153 m_BlockBytes[heapIndex] = 0;
11154 m_AllocationBytes[heapIndex] = 0;
11155 #if VMA_MEMORY_BUDGET
11156 m_VulkanUsage[heapIndex] = 0;
11157 m_VulkanBudget[heapIndex] = 0;
11158 m_BlockBytesAtBudgetFetch[heapIndex] = 0;
11159 #endif
11160 }
11161
11162 #if VMA_MEMORY_BUDGET
11163 m_OperationsSinceBudgetFetch = 0;
11164 #endif
11165 }
11166
AddAllocation(uint32_t heapIndex,VkDeviceSize allocationSize)11167 void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
11168 {
11169 m_AllocationBytes[heapIndex] += allocationSize;
11170 ++m_AllocationCount[heapIndex];
11171 #if VMA_MEMORY_BUDGET
11172 ++m_OperationsSinceBudgetFetch;
11173 #endif
11174 }
11175
RemoveAllocation(uint32_t heapIndex,VkDeviceSize allocationSize)11176 void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)
11177 {
11178 VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);
11179 m_AllocationBytes[heapIndex] -= allocationSize;
11180 VMA_ASSERT(m_AllocationCount[heapIndex] > 0);
11181 --m_AllocationCount[heapIndex];
11182 #if VMA_MEMORY_BUDGET
11183 ++m_OperationsSinceBudgetFetch;
11184 #endif
11185 }
11186 #endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS
11187 #endif // _VMA_CURRENT_BUDGET_DATA
11188
11189 #ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR
11190 /*
11191 Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.
11192 */
11193 class VmaAllocationObjectAllocator
11194 {
VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)11195 VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator)
11196 public:
11197 VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks)
11198 : m_Allocator(pAllocationCallbacks, 1024) {}
11199
11200 template<typename... Types> VmaAllocation Allocate(Types&&... args);
11201 void Free(VmaAllocation hAlloc);
11202
11203 private:
11204 VMA_MUTEX m_Mutex;
11205 VmaPoolAllocator<VmaAllocation_T> m_Allocator;
11206 };
11207
11208 template<typename... Types>
Allocate(Types &&...args)11209 VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)
11210 {
11211 VmaMutexLock mutexLock(m_Mutex);
11212 return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);
11213 }
11214
Free(VmaAllocation hAlloc)11215 void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)
11216 {
11217 VmaMutexLock mutexLock(m_Mutex);
11218 m_Allocator.Free(hAlloc);
11219 }
11220 #endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR
11221
11222 #ifndef _VMA_VIRTUAL_BLOCK_T
11223 struct VmaVirtualBlock_T
11224 {
11225 VMA_CLASS_NO_COPY(VmaVirtualBlock_T)
11226 public:
11227 const bool m_AllocationCallbacksSpecified;
11228 const VkAllocationCallbacks m_AllocationCallbacks;
11229
11230 VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);
11231 ~VmaVirtualBlock_T();
11232
InitVmaVirtualBlock_T11233 VkResult Init() { return VK_SUCCESS; }
IsEmptyVmaVirtualBlock_T11234 bool IsEmpty() const { return m_Metadata->IsEmpty(); }
FreeVmaVirtualBlock_T11235 void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); }
SetAllocationUserDataVmaVirtualBlock_T11236 void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); }
ClearVmaVirtualBlock_T11237 void Clear() { m_Metadata->Clear(); }
11238
11239 const VkAllocationCallbacks* GetAllocationCallbacks() const;
11240 void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo);
11241 VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
11242 VkDeviceSize* outOffset);
11243 void GetStatistics(VmaStatistics& outStats) const;
11244 void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const;
11245 #if VMA_STATS_STRING_ENABLED
11246 void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;
11247 #endif
11248
11249 private:
11250 VmaBlockMetadata* m_Metadata;
11251 };
11252
11253 #ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo & createInfo)11254 VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo)
11255 : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),
11256 m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)
11257 {
11258 const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;
11259 switch (algorithm)
11260 {
11261 default:
11262 VMA_ASSERT(0);
11263 case 0:
11264 m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);
11265 break;
11266 case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:
11267 m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true);
11268 break;
11269 }
11270
11271 m_Metadata->Init(createInfo.size);
11272 }
11273
~VmaVirtualBlock_T()11274 VmaVirtualBlock_T::~VmaVirtualBlock_T()
11275 {
11276 // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations
11277 if (!m_Metadata->IsEmpty())
11278 m_Metadata->DebugLogAllAllocations();
11279 // This is the most important assert in the entire library.
11280 // Hitting it means you have some memory leak - unreleased virtual allocations.
11281 VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");
11282
11283 vma_delete(GetAllocationCallbacks(), m_Metadata);
11284 }
11285
GetAllocationCallbacks()11286 const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const
11287 {
11288 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
11289 }
11290
GetAllocationInfo(VmaVirtualAllocation allocation,VmaVirtualAllocationInfo & outInfo)11291 void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo)
11292 {
11293 m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo);
11294 }
11295
Allocate(const VmaVirtualAllocationCreateInfo & createInfo,VmaVirtualAllocation & outAllocation,VkDeviceSize * outOffset)11296 VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,
11297 VkDeviceSize* outOffset)
11298 {
11299 VmaAllocationRequest request = {};
11300 if (m_Metadata->CreateAllocationRequest(
11301 createInfo.size, // allocSize
11302 VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment
11303 (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress
11304 VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant
11305 createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy
11306 &request))
11307 {
11308 m_Metadata->Alloc(request,
11309 VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant
11310 createInfo.pUserData);
11311 outAllocation = (VmaVirtualAllocation)request.allocHandle;
11312 if(outOffset)
11313 *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle);
11314 return VK_SUCCESS;
11315 }
11316 outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE;
11317 if (outOffset)
11318 *outOffset = UINT64_MAX;
11319 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
11320 }
11321
GetStatistics(VmaStatistics & outStats)11322 void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const
11323 {
11324 VmaClearStatistics(outStats);
11325 m_Metadata->AddStatistics(outStats);
11326 }
11327
CalculateDetailedStatistics(VmaDetailedStatistics & outStats)11328 void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const
11329 {
11330 VmaClearDetailedStatistics(outStats);
11331 m_Metadata->AddDetailedStatistics(outStats);
11332 }
11333
11334 #if VMA_STATS_STRING_ENABLED
BuildStatsString(bool detailedMap,VmaStringBuilder & sb)11335 void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const
11336 {
11337 VmaJsonWriter json(GetAllocationCallbacks(), sb);
11338 json.BeginObject();
11339
11340 VmaDetailedStatistics stats;
11341 CalculateDetailedStatistics(stats);
11342
11343 json.WriteString("Stats");
11344 VmaPrintDetailedStatistics(json, stats);
11345
11346 if (detailedMap)
11347 {
11348 json.WriteString("Details");
11349 json.BeginObject();
11350 m_Metadata->PrintDetailedMap(json);
11351 json.EndObject();
11352 }
11353
11354 json.EndObject();
11355 }
11356 #endif // VMA_STATS_STRING_ENABLED
11357 #endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS
11358 #endif // _VMA_VIRTUAL_BLOCK_T
11359
11360
11361 // Main allocator object.
11362 struct VmaAllocator_T
11363 {
11364 VMA_CLASS_NO_COPY(VmaAllocator_T)
11365 public:
11366 bool m_UseMutex;
11367 uint32_t m_VulkanApiVersion;
11368 bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
11369 bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).
11370 bool m_UseExtMemoryBudget;
11371 bool m_UseAmdDeviceCoherentMemory;
11372 bool m_UseKhrBufferDeviceAddress;
11373 bool m_UseExtMemoryPriority;
11374 VkDevice m_hDevice;
11375 VkInstance m_hInstance;
11376 bool m_AllocationCallbacksSpecified;
11377 VkAllocationCallbacks m_AllocationCallbacks;
11378 VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
11379 VmaAllocationObjectAllocator m_AllocationObjectAllocator;
11380
11381 // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.
11382 uint32_t m_HeapSizeLimitMask;
11383
11384 VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
11385 VkPhysicalDeviceMemoryProperties m_MemProps;
11386
11387 // Default pools.
11388 VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
11389 VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
11390
11391 VmaCurrentBudgetData m_Budget;
11392 VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.
11393
11394 VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
11395 VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
11396 ~VmaAllocator_T();
11397
GetAllocationCallbacksVmaAllocator_T11398 const VkAllocationCallbacks* GetAllocationCallbacks() const
11399 {
11400 return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;
11401 }
GetVulkanFunctionsVmaAllocator_T11402 const VmaVulkanFunctions& GetVulkanFunctions() const
11403 {
11404 return m_VulkanFunctions;
11405 }
11406
GetPhysicalDeviceVmaAllocator_T11407 VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }
11408
GetBufferImageGranularityVmaAllocator_T11409 VkDeviceSize GetBufferImageGranularity() const
11410 {
11411 return VMA_MAX(
11412 static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
11413 m_PhysicalDeviceProperties.limits.bufferImageGranularity);
11414 }
11415
GetMemoryHeapCountVmaAllocator_T11416 uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
GetMemoryTypeCountVmaAllocator_T11417 uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
11418
MemoryTypeIndexToHeapIndexVmaAllocator_T11419 uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
11420 {
11421 VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
11422 return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
11423 }
11424 // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
IsMemoryTypeNonCoherentVmaAllocator_T11425 bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
11426 {
11427 return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
11428 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
11429 }
11430 // Minimum alignment for all allocations in specific memory type.
GetMemoryTypeMinAlignmentVmaAllocator_T11431 VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
11432 {
11433 return IsMemoryTypeNonCoherent(memTypeIndex) ?
11434 VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
11435 (VkDeviceSize)VMA_MIN_ALIGNMENT;
11436 }
11437
IsIntegratedGpuVmaAllocator_T11438 bool IsIntegratedGpu() const
11439 {
11440 return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
11441 }
11442
GetGlobalMemoryTypeBitsVmaAllocator_T11443 uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }
11444
11445 void GetBufferMemoryRequirements(
11446 VkBuffer hBuffer,
11447 VkMemoryRequirements& memReq,
11448 bool& requiresDedicatedAllocation,
11449 bool& prefersDedicatedAllocation) const;
11450 void GetImageMemoryRequirements(
11451 VkImage hImage,
11452 VkMemoryRequirements& memReq,
11453 bool& requiresDedicatedAllocation,
11454 bool& prefersDedicatedAllocation) const;
11455 VkResult FindMemoryTypeIndex(
11456 uint32_t memoryTypeBits,
11457 const VmaAllocationCreateInfo* pAllocationCreateInfo,
11458 VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown.
11459 uint32_t* pMemoryTypeIndex) const;
11460
11461 // Main allocation function.
11462 VkResult AllocateMemory(
11463 const VkMemoryRequirements& vkMemReq,
11464 bool requiresDedicatedAllocation,
11465 bool prefersDedicatedAllocation,
11466 VkBuffer dedicatedBuffer,
11467 VkImage dedicatedImage,
11468 VkFlags dedicatedBufferImageUsage, // UINT32_MAX if unknown.
11469 const VmaAllocationCreateInfo& createInfo,
11470 VmaSuballocationType suballocType,
11471 size_t allocationCount,
11472 VmaAllocation* pAllocations);
11473
11474 // Main deallocation function.
11475 void FreeMemory(
11476 size_t allocationCount,
11477 const VmaAllocation* pAllocations);
11478
11479 void CalculateStatistics(VmaTotalStatistics* pStats);
11480
11481 void GetHeapBudgets(
11482 VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount);
11483
11484 #if VMA_STATS_STRING_ENABLED
11485 void PrintDetailedMap(class VmaJsonWriter& json);
11486 #endif
11487
11488 void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
11489
11490 VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
11491 void DestroyPool(VmaPool pool);
11492 void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats);
11493 void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats);
11494
11495 void SetCurrentFrameIndex(uint32_t frameIndex);
GetCurrentFrameIndexVmaAllocator_T11496 uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
11497
11498 VkResult CheckPoolCorruption(VmaPool hPool);
11499 VkResult CheckCorruption(uint32_t memoryTypeBits);
11500
11501 // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.
11502 VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
11503 // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.
11504 void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
11505 // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.
11506 VkResult BindVulkanBuffer(
11507 VkDeviceMemory memory,
11508 VkDeviceSize memoryOffset,
11509 VkBuffer buffer,
11510 const void* pNext);
11511 // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.
11512 VkResult BindVulkanImage(
11513 VkDeviceMemory memory,
11514 VkDeviceSize memoryOffset,
11515 VkImage image,
11516 const void* pNext);
11517
11518 VkResult Map(VmaAllocation hAllocation, void** ppData);
11519 void Unmap(VmaAllocation hAllocation);
11520
11521 VkResult BindBufferMemory(
11522 VmaAllocation hAllocation,
11523 VkDeviceSize allocationLocalOffset,
11524 VkBuffer hBuffer,
11525 const void* pNext);
11526 VkResult BindImageMemory(
11527 VmaAllocation hAllocation,
11528 VkDeviceSize allocationLocalOffset,
11529 VkImage hImage,
11530 const void* pNext);
11531
11532 VkResult FlushOrInvalidateAllocation(
11533 VmaAllocation hAllocation,
11534 VkDeviceSize offset, VkDeviceSize size,
11535 VMA_CACHE_OPERATION op);
11536 VkResult FlushOrInvalidateAllocations(
11537 uint32_t allocationCount,
11538 const VmaAllocation* allocations,
11539 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
11540 VMA_CACHE_OPERATION op);
11541
11542 void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
11543
11544 /*
11545 Returns bit mask of memory types that can support defragmentation on GPU as
11546 they support creation of required buffer for copy operations.
11547 */
11548 uint32_t GetGpuDefragmentationMemoryTypeBits();
11549
11550 #if VMA_EXTERNAL_MEMORY
GetExternalMemoryHandleTypeFlagsVmaAllocator_T11551 VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const
11552 {
11553 return m_TypeExternalMemoryHandleTypes[memTypeIndex];
11554 }
11555 #endif // #if VMA_EXTERNAL_MEMORY
11556
11557 void FreeEmptyBlock();
11558
11559 private:
11560 VkDeviceSize m_PreferredLargeHeapBlockSize;
11561
11562 VkPhysicalDevice m_PhysicalDevice;
11563 VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
11564 VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.
11565 #if VMA_EXTERNAL_MEMORY
11566 VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];
11567 #endif // #if VMA_EXTERNAL_MEMORY
11568
11569 VMA_RW_MUTEX m_PoolsMutex;
11570 typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;
11571 // Protected by m_PoolsMutex.
11572 PoolList m_Pools;
11573 uint32_t m_NextPoolId;
11574
11575 VmaVulkanFunctions m_VulkanFunctions;
11576
11577 // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.
11578 uint32_t m_GlobalMemoryTypeBits;
11579
11580 void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
11581
11582 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
11583 void ImportVulkanFunctions_Static();
11584 #endif
11585
11586 void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);
11587
11588 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
11589 void ImportVulkanFunctions_Dynamic();
11590 #endif
11591
11592 void ValidateVulkanFunctions();
11593
11594 VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
11595
11596 VkResult AllocateMemoryOfType(
11597 VmaPool pool,
11598 VkDeviceSize size,
11599 VkDeviceSize alignment,
11600 bool dedicatedPreferred,
11601 VkBuffer dedicatedBuffer,
11602 VkImage dedicatedImage,
11603 VkFlags dedicatedBufferImageUsage,
11604 const VmaAllocationCreateInfo& createInfo,
11605 uint32_t memTypeIndex,
11606 VmaSuballocationType suballocType,
11607 VmaDedicatedAllocationList& dedicatedAllocations,
11608 VmaBlockVector& blockVector,
11609 size_t allocationCount,
11610 VmaAllocation* pAllocations);
11611
11612 // Helper function only to be used inside AllocateDedicatedMemory.
11613 VkResult AllocateDedicatedMemoryPage(
11614 VmaPool pool,
11615 VkDeviceSize size,
11616 VmaSuballocationType suballocType,
11617 uint32_t memTypeIndex,
11618 const VkMemoryAllocateInfo& allocInfo,
11619 bool map,
11620 bool isUserDataString,
11621 bool isMappingAllowed,
11622 void* pUserData,
11623 VmaAllocation* pAllocation);
11624
11625 // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
11626 VkResult AllocateDedicatedMemory(
11627 VmaPool pool,
11628 VkDeviceSize size,
11629 VmaSuballocationType suballocType,
11630 VmaDedicatedAllocationList& dedicatedAllocations,
11631 uint32_t memTypeIndex,
11632 bool map,
11633 bool isUserDataString,
11634 bool isMappingAllowed,
11635 bool canAliasMemory,
11636 void* pUserData,
11637 float priority,
11638 VkBuffer dedicatedBuffer,
11639 VkImage dedicatedImage,
11640 VkFlags dedicatedBufferImageUsage,
11641 size_t allocationCount,
11642 VmaAllocation* pAllocations,
11643 const void* pNextChain = nullptr);
11644
11645 void FreeDedicatedMemory(const VmaAllocation allocation);
11646
11647 VkResult CalcMemTypeParams(
11648 VmaAllocationCreateInfo& outCreateInfo,
11649 uint32_t memTypeIndex,
11650 VkDeviceSize size,
11651 size_t allocationCount);
11652 VkResult CalcAllocationParams(
11653 VmaAllocationCreateInfo& outCreateInfo,
11654 bool dedicatedRequired,
11655 bool dedicatedPreferred);
11656
11657 /*
11658 Calculates and returns bit mask of memory types that can support defragmentation
11659 on GPU as they support creation of required buffer for copy operations.
11660 */
11661 uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;
11662 uint32_t CalculateGlobalMemoryTypeBits() const;
11663
11664 bool GetFlushOrInvalidateRange(
11665 VmaAllocation allocation,
11666 VkDeviceSize offset, VkDeviceSize size,
11667 VkMappedMemoryRange& outRange) const;
11668
11669 #if VMA_MEMORY_BUDGET
11670 void UpdateVulkanBudget();
11671 #endif // #if VMA_MEMORY_BUDGET
11672 };
11673
11674
11675 #ifndef _VMA_MEMORY_FUNCTIONS
VmaMalloc(VmaAllocator hAllocator,size_t size,size_t alignment)11676 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
11677 {
11678 return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
11679 }
11680
VmaFree(VmaAllocator hAllocator,void * ptr)11681 static void VmaFree(VmaAllocator hAllocator, void* ptr)
11682 {
11683 VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
11684 }
11685
11686 template<typename T>
VmaAllocate(VmaAllocator hAllocator)11687 static T* VmaAllocate(VmaAllocator hAllocator)
11688 {
11689 return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
11690 }
11691
11692 template<typename T>
VmaAllocateArray(VmaAllocator hAllocator,size_t count)11693 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
11694 {
11695 return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
11696 }
11697
11698 template<typename T>
vma_delete(VmaAllocator hAllocator,T * ptr)11699 static void vma_delete(VmaAllocator hAllocator, T* ptr)
11700 {
11701 if(ptr != VMA_NULL)
11702 {
11703 ptr->~T();
11704 VmaFree(hAllocator, ptr);
11705 }
11706 }
11707
11708 template<typename T>
vma_delete_array(VmaAllocator hAllocator,T * ptr,size_t count)11709 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
11710 {
11711 if(ptr != VMA_NULL)
11712 {
11713 for(size_t i = count; i--; )
11714 ptr[i].~T();
11715 VmaFree(hAllocator, ptr);
11716 }
11717 }
11718 #endif // _VMA_MEMORY_FUNCTIONS
11719
11720 #ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
VmaDeviceMemoryBlock(VmaAllocator hAllocator)11721 VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)
11722 : m_pMetadata(VMA_NULL),
11723 m_MemoryTypeIndex(UINT32_MAX),
11724 m_Id(0),
11725 m_hMemory(VK_NULL_HANDLE),
11726 m_MapCount(0),
11727 m_pMappedData(VMA_NULL) {}
11728
~VmaDeviceMemoryBlock()11729 VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()
11730 {
11731 VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
11732 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11733 }
11734
Init(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t newMemoryTypeIndex,VkDeviceMemory newMemory,VkDeviceSize newSize,uint32_t id,uint32_t algorithm,VkDeviceSize bufferImageGranularity)11735 void VmaDeviceMemoryBlock::Init(
11736 VmaAllocator hAllocator,
11737 VmaPool hParentPool,
11738 uint32_t newMemoryTypeIndex,
11739 VkDeviceMemory newMemory,
11740 VkDeviceSize newSize,
11741 uint32_t id,
11742 uint32_t algorithm,
11743 VkDeviceSize bufferImageGranularity)
11744 {
11745 VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
11746
11747 m_hParentPool = hParentPool;
11748 m_MemoryTypeIndex = newMemoryTypeIndex;
11749 m_Id = id;
11750 m_hMemory = newMemory;
11751
11752 switch (algorithm)
11753 {
11754 case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
11755 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),
11756 bufferImageGranularity, false); // isVirtual
11757 break;
11758 default:
11759 VMA_ASSERT(0);
11760 // Fall-through.
11761 case 0:
11762 m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),
11763 bufferImageGranularity, false); // isVirtual
11764 }
11765 m_pMetadata->Init(newSize);
11766 }
11767
Destroy(VmaAllocator allocator)11768 void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
11769 {
11770 // Define macro VMA_DEBUG_LOG to receive the list of the unfreed allocations
11771 if (!m_pMetadata->IsEmpty())
11772 m_pMetadata->DebugLogAllAllocations();
11773 // This is the most important assert in the entire library.
11774 // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
11775 VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
11776
11777 VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
11778 allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
11779 m_hMemory = VK_NULL_HANDLE;
11780
11781 vma_delete(allocator, m_pMetadata);
11782 m_pMetadata = VMA_NULL;
11783 }
11784
PostFree(VmaAllocator hAllocator)11785 void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator)
11786 {
11787 if(m_MappingHysteresis.PostFree())
11788 {
11789 VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0);
11790 if (m_MapCount == 0)
11791 {
11792 m_pMappedData = VMA_NULL;
11793 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11794 }
11795 }
11796 }
11797
Validate()11798 bool VmaDeviceMemoryBlock::Validate() const
11799 {
11800 VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
11801 (m_pMetadata->GetSize() != 0));
11802
11803 return m_pMetadata->Validate();
11804 }
11805
CheckCorruption(VmaAllocator hAllocator)11806 VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
11807 {
11808 void* pData = nullptr;
11809 VkResult res = Map(hAllocator, 1, &pData);
11810 if (res != VK_SUCCESS)
11811 {
11812 return res;
11813 }
11814
11815 res = m_pMetadata->CheckCorruption(pData);
11816
11817 Unmap(hAllocator, 1);
11818
11819 return res;
11820 }
11821
Map(VmaAllocator hAllocator,uint32_t count,void ** ppData)11822 VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
11823 {
11824 if (count == 0)
11825 {
11826 return VK_SUCCESS;
11827 }
11828
11829 VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11830 const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
11831 m_MappingHysteresis.PostMap();
11832 if (oldTotalMapCount != 0)
11833 {
11834 m_MapCount += count;
11835 VMA_ASSERT(m_pMappedData != VMA_NULL);
11836 if (ppData != VMA_NULL)
11837 {
11838 *ppData = m_pMappedData;
11839 }
11840 return VK_SUCCESS;
11841 }
11842 else
11843 {
11844 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
11845 hAllocator->m_hDevice,
11846 m_hMemory,
11847 0, // offset
11848 VK_WHOLE_SIZE,
11849 0, // flags
11850 &m_pMappedData);
11851 if (result == VK_SUCCESS)
11852 {
11853 if (ppData != VMA_NULL)
11854 {
11855 *ppData = m_pMappedData;
11856 }
11857 m_MapCount = count;
11858 }
11859 return result;
11860 }
11861 }
11862
Unmap(VmaAllocator hAllocator,uint32_t count)11863 void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
11864 {
11865 if (count == 0)
11866 {
11867 return;
11868 }
11869
11870 VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11871 if (m_MapCount >= count)
11872 {
11873 m_MapCount -= count;
11874 const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();
11875 if (totalMapCount == 0)
11876 {
11877 m_pMappedData = VMA_NULL;
11878 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
11879 }
11880 m_MappingHysteresis.PostUnmap();
11881 }
11882 else
11883 {
11884 VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
11885 }
11886 }
11887
WriteMagicValueAfterAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11888 VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11889 {
11890 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11891
11892 void* pData;
11893 VkResult res = Map(hAllocator, 1, &pData);
11894 if (res != VK_SUCCESS)
11895 {
11896 return res;
11897 }
11898
11899 VmaWriteMagicValue(pData, allocOffset + allocSize);
11900
11901 Unmap(hAllocator, 1);
11902 return VK_SUCCESS;
11903 }
11904
ValidateMagicValueAfterAllocation(VmaAllocator hAllocator,VkDeviceSize allocOffset,VkDeviceSize allocSize)11905 VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
11906 {
11907 VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
11908
11909 void* pData;
11910 VkResult res = Map(hAllocator, 1, &pData);
11911 if (res != VK_SUCCESS)
11912 {
11913 return res;
11914 }
11915
11916 if (!VmaValidateMagicValue(pData, allocOffset + allocSize))
11917 {
11918 VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
11919 }
11920
11921 Unmap(hAllocator, 1);
11922 return VK_SUCCESS;
11923 }
11924
BindBufferMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)11925 VkResult VmaDeviceMemoryBlock::BindBufferMemory(
11926 const VmaAllocator hAllocator,
11927 const VmaAllocation hAllocation,
11928 VkDeviceSize allocationLocalOffset,
11929 VkBuffer hBuffer,
11930 const void* pNext)
11931 {
11932 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11933 hAllocation->GetBlock() == this);
11934 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11935 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11936 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11937 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11938 VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11939 return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);
11940 }
11941
BindImageMemory(const VmaAllocator hAllocator,const VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)11942 VkResult VmaDeviceMemoryBlock::BindImageMemory(
11943 const VmaAllocator hAllocator,
11944 const VmaAllocation hAllocation,
11945 VkDeviceSize allocationLocalOffset,
11946 VkImage hImage,
11947 const void* pNext)
11948 {
11949 VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
11950 hAllocation->GetBlock() == this);
11951 VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&
11952 "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");
11953 const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;
11954 // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
11955 VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);
11956 return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);
11957 }
11958 #endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS
11959
11960 #ifndef _VMA_ALLOCATION_T_FUNCTIONS
VmaAllocation_T(bool mappingAllowed)11961 VmaAllocation_T::VmaAllocation_T(bool mappingAllowed)
11962 : m_Alignment{ 1 },
11963 m_Size{ 0 },
11964 m_pUserData{ VMA_NULL },
11965 m_pName{ VMA_NULL },
11966 m_MemoryTypeIndex{ 0 },
11967 m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },
11968 m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },
11969 m_MapCount{ 0 },
11970 m_Flags{ 0 }
11971 {
11972 if(mappingAllowed)
11973 m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED;
11974
11975 #if VMA_STATS_STRING_ENABLED
11976 m_BufferImageUsage = 0;
11977 #endif
11978 }
11979
~VmaAllocation_T()11980 VmaAllocation_T::~VmaAllocation_T()
11981 {
11982 VMA_ASSERT(m_MapCount == 0 && "Allocation was not unmapped before destruction.");
11983
11984 // Check if owned string was freed.
11985 VMA_ASSERT(m_pName == VMA_NULL);
11986 }
11987
InitBlockAllocation(VmaDeviceMemoryBlock * block,VmaAllocHandle allocHandle,VkDeviceSize alignment,VkDeviceSize size,uint32_t memoryTypeIndex,VmaSuballocationType suballocationType,bool mapped)11988 void VmaAllocation_T::InitBlockAllocation(
11989 VmaDeviceMemoryBlock* block,
11990 VmaAllocHandle allocHandle,
11991 VkDeviceSize alignment,
11992 VkDeviceSize size,
11993 uint32_t memoryTypeIndex,
11994 VmaSuballocationType suballocationType,
11995 bool mapped)
11996 {
11997 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
11998 VMA_ASSERT(block != VMA_NULL);
11999 m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
12000 m_Alignment = alignment;
12001 m_Size = size;
12002 m_MemoryTypeIndex = memoryTypeIndex;
12003 if(mapped)
12004 {
12005 VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12006 m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
12007 }
12008 m_SuballocationType = (uint8_t)suballocationType;
12009 m_BlockAllocation.m_Block = block;
12010 m_BlockAllocation.m_AllocHandle = allocHandle;
12011 }
12012
InitDedicatedAllocation(VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceMemory hMemory,VmaSuballocationType suballocationType,void * pMappedData,VkDeviceSize size)12013 void VmaAllocation_T::InitDedicatedAllocation(
12014 VmaPool hParentPool,
12015 uint32_t memoryTypeIndex,
12016 VkDeviceMemory hMemory,
12017 VmaSuballocationType suballocationType,
12018 void* pMappedData,
12019 VkDeviceSize size)
12020 {
12021 VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
12022 VMA_ASSERT(hMemory != VK_NULL_HANDLE);
12023 m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
12024 m_Alignment = 0;
12025 m_Size = size;
12026 m_MemoryTypeIndex = memoryTypeIndex;
12027 m_SuballocationType = (uint8_t)suballocationType;
12028 if(pMappedData != VMA_NULL)
12029 {
12030 VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12031 m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;
12032 }
12033 m_DedicatedAllocation.m_hParentPool = hParentPool;
12034 m_DedicatedAllocation.m_hMemory = hMemory;
12035 m_DedicatedAllocation.m_pMappedData = pMappedData;
12036 m_DedicatedAllocation.m_Prev = VMA_NULL;
12037 m_DedicatedAllocation.m_Next = VMA_NULL;
12038 }
12039
SetName(VmaAllocator hAllocator,const char * pName)12040 void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)
12041 {
12042 VMA_ASSERT(pName == VMA_NULL || pName != m_pName);
12043
12044 FreeName(hAllocator);
12045
12046 if (pName != VMA_NULL)
12047 m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName);
12048 }
12049
SwapBlockAllocation(VmaAllocator hAllocator,VmaAllocation allocation)12050 uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation)
12051 {
12052 VMA_ASSERT(allocation != VMA_NULL);
12053 VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
12054 VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK);
12055
12056 if (m_MapCount != 0)
12057 m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount);
12058
12059 m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation);
12060 VMA_SWAP(m_BlockAllocation, allocation->m_BlockAllocation);
12061 m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this);
12062
12063 #if VMA_STATS_STRING_ENABLED
12064 VMA_SWAP(m_BufferImageUsage, allocation->m_BufferImageUsage);
12065 #endif
12066 return m_MapCount;
12067 }
12068
GetAllocHandle()12069 VmaAllocHandle VmaAllocation_T::GetAllocHandle() const
12070 {
12071 switch (m_Type)
12072 {
12073 case ALLOCATION_TYPE_BLOCK:
12074 return m_BlockAllocation.m_AllocHandle;
12075 case ALLOCATION_TYPE_DEDICATED:
12076 return VK_NULL_HANDLE;
12077 default:
12078 VMA_ASSERT(0);
12079 return VK_NULL_HANDLE;
12080 }
12081 }
12082
GetOffset()12083 VkDeviceSize VmaAllocation_T::GetOffset() const
12084 {
12085 switch (m_Type)
12086 {
12087 case ALLOCATION_TYPE_BLOCK:
12088 return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle);
12089 case ALLOCATION_TYPE_DEDICATED:
12090 return 0;
12091 default:
12092 VMA_ASSERT(0);
12093 return 0;
12094 }
12095 }
12096
GetParentPool()12097 VmaPool VmaAllocation_T::GetParentPool() const
12098 {
12099 switch (m_Type)
12100 {
12101 case ALLOCATION_TYPE_BLOCK:
12102 return m_BlockAllocation.m_Block->GetParentPool();
12103 case ALLOCATION_TYPE_DEDICATED:
12104 return m_DedicatedAllocation.m_hParentPool;
12105 default:
12106 VMA_ASSERT(0);
12107 return VK_NULL_HANDLE;
12108 }
12109 }
12110
GetMemory()12111 VkDeviceMemory VmaAllocation_T::GetMemory() const
12112 {
12113 switch (m_Type)
12114 {
12115 case ALLOCATION_TYPE_BLOCK:
12116 return m_BlockAllocation.m_Block->GetDeviceMemory();
12117 case ALLOCATION_TYPE_DEDICATED:
12118 return m_DedicatedAllocation.m_hMemory;
12119 default:
12120 VMA_ASSERT(0);
12121 return VK_NULL_HANDLE;
12122 }
12123 }
12124
GetMappedData()12125 void* VmaAllocation_T::GetMappedData() const
12126 {
12127 switch (m_Type)
12128 {
12129 case ALLOCATION_TYPE_BLOCK:
12130 if (m_MapCount != 0 || IsPersistentMap())
12131 {
12132 void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
12133 VMA_ASSERT(pBlockData != VMA_NULL);
12134 return (char*)pBlockData + GetOffset();
12135 }
12136 else
12137 {
12138 return VMA_NULL;
12139 }
12140 break;
12141 case ALLOCATION_TYPE_DEDICATED:
12142 VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));
12143 return m_DedicatedAllocation.m_pMappedData;
12144 default:
12145 VMA_ASSERT(0);
12146 return VMA_NULL;
12147 }
12148 }
12149
BlockAllocMap()12150 void VmaAllocation_T::BlockAllocMap()
12151 {
12152 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
12153 VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12154
12155 if (m_MapCount < 0xFF)
12156 {
12157 ++m_MapCount;
12158 }
12159 else
12160 {
12161 VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
12162 }
12163 }
12164
BlockAllocUnmap()12165 void VmaAllocation_T::BlockAllocUnmap()
12166 {
12167 VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
12168
12169 if (m_MapCount > 0)
12170 {
12171 --m_MapCount;
12172 }
12173 else
12174 {
12175 VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
12176 }
12177 }
12178
DedicatedAllocMap(VmaAllocator hAllocator,void ** ppData)12179 VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
12180 {
12181 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
12182 VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");
12183
12184 if (m_MapCount != 0 || IsPersistentMap())
12185 {
12186 if (m_MapCount < 0xFF)
12187 {
12188 VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
12189 *ppData = m_DedicatedAllocation.m_pMappedData;
12190 ++m_MapCount;
12191 return VK_SUCCESS;
12192 }
12193 else
12194 {
12195 VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
12196 return VK_ERROR_MEMORY_MAP_FAILED;
12197 }
12198 }
12199 else
12200 {
12201 VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
12202 hAllocator->m_hDevice,
12203 m_DedicatedAllocation.m_hMemory,
12204 0, // offset
12205 VK_WHOLE_SIZE,
12206 0, // flags
12207 ppData);
12208 if (result == VK_SUCCESS)
12209 {
12210 m_DedicatedAllocation.m_pMappedData = *ppData;
12211 m_MapCount = 1;
12212 }
12213 return result;
12214 }
12215 }
12216
DedicatedAllocUnmap(VmaAllocator hAllocator)12217 void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
12218 {
12219 VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
12220
12221 if (m_MapCount > 0)
12222 {
12223 --m_MapCount;
12224 if (m_MapCount == 0 && !IsPersistentMap())
12225 {
12226 m_DedicatedAllocation.m_pMappedData = VMA_NULL;
12227 (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
12228 hAllocator->m_hDevice,
12229 m_DedicatedAllocation.m_hMemory);
12230 }
12231 }
12232 else
12233 {
12234 VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
12235 }
12236 }
12237
12238 #if VMA_STATS_STRING_ENABLED
InitBufferImageUsage(uint32_t bufferImageUsage)12239 void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage)
12240 {
12241 VMA_ASSERT(m_BufferImageUsage == 0);
12242 m_BufferImageUsage = bufferImageUsage;
12243 }
12244
PrintParameters(class VmaJsonWriter & json)12245 void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
12246 {
12247 json.WriteString("Type");
12248 json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
12249
12250 json.WriteString("Size");
12251 json.WriteNumber(m_Size);
12252 json.WriteString("Usage");
12253 json.WriteNumber(m_BufferImageUsage);
12254
12255 if (m_pUserData != VMA_NULL)
12256 {
12257 json.WriteString("CustomData");
12258 json.BeginString();
12259 json.ContinueString_Pointer(m_pUserData);
12260 json.EndString();
12261 }
12262 if (m_pName != VMA_NULL)
12263 {
12264 json.WriteString("Name");
12265 json.WriteString(m_pName);
12266 }
12267 }
12268 #endif // VMA_STATS_STRING_ENABLED
12269
FreeName(VmaAllocator hAllocator)12270 void VmaAllocation_T::FreeName(VmaAllocator hAllocator)
12271 {
12272 if(m_pName)
12273 {
12274 VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName);
12275 m_pName = VMA_NULL;
12276 }
12277 }
12278 #endif // _VMA_ALLOCATION_T_FUNCTIONS
12279
12280 #ifndef _VMA_BLOCK_VECTOR_FUNCTIONS
VmaBlockVector(VmaAllocator hAllocator,VmaPool hParentPool,uint32_t memoryTypeIndex,VkDeviceSize preferredBlockSize,size_t minBlockCount,size_t maxBlockCount,VkDeviceSize bufferImageGranularity,bool explicitBlockSize,uint32_t algorithm,float priority,VkDeviceSize minAllocationAlignment,void * pMemoryAllocateNext)12281 VmaBlockVector::VmaBlockVector(
12282 VmaAllocator hAllocator,
12283 VmaPool hParentPool,
12284 uint32_t memoryTypeIndex,
12285 VkDeviceSize preferredBlockSize,
12286 size_t minBlockCount,
12287 size_t maxBlockCount,
12288 VkDeviceSize bufferImageGranularity,
12289 bool explicitBlockSize,
12290 uint32_t algorithm,
12291 float priority,
12292 VkDeviceSize minAllocationAlignment,
12293 void* pMemoryAllocateNext)
12294 : m_hAllocator(hAllocator),
12295 m_hParentPool(hParentPool),
12296 m_MemoryTypeIndex(memoryTypeIndex),
12297 m_PreferredBlockSize(preferredBlockSize),
12298 m_MinBlockCount(minBlockCount),
12299 m_MaxBlockCount(maxBlockCount),
12300 m_BufferImageGranularity(bufferImageGranularity),
12301 m_ExplicitBlockSize(explicitBlockSize),
12302 m_Algorithm(algorithm),
12303 m_Priority(priority),
12304 m_MinAllocationAlignment(minAllocationAlignment),
12305 m_pMemoryAllocateNext(pMemoryAllocateNext),
12306 m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
12307 m_NextBlockId(0) {}
12308
~VmaBlockVector()12309 VmaBlockVector::~VmaBlockVector()
12310 {
12311 for (size_t i = m_Blocks.size(); i--; )
12312 {
12313 m_Blocks[i]->Destroy(m_hAllocator);
12314 vma_delete(m_hAllocator, m_Blocks[i]);
12315 }
12316 }
12317
CreateMinBlocks()12318 VkResult VmaBlockVector::CreateMinBlocks()
12319 {
12320 for (size_t i = 0; i < m_MinBlockCount; ++i)
12321 {
12322 VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
12323 if (res != VK_SUCCESS)
12324 {
12325 return res;
12326 }
12327 }
12328 return VK_SUCCESS;
12329 }
12330
AddStatistics(VmaStatistics & inoutStats)12331 void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats)
12332 {
12333 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12334
12335 const size_t blockCount = m_Blocks.size();
12336 for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12337 {
12338 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12339 VMA_ASSERT(pBlock);
12340 VMA_HEAVY_ASSERT(pBlock->Validate());
12341 pBlock->m_pMetadata->AddStatistics(inoutStats);
12342 }
12343 }
12344
AddDetailedStatistics(VmaDetailedStatistics & inoutStats)12345 void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
12346 {
12347 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12348
12349 const size_t blockCount = m_Blocks.size();
12350 for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
12351 {
12352 const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12353 VMA_ASSERT(pBlock);
12354 VMA_HEAVY_ASSERT(pBlock->Validate());
12355 pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);
12356 }
12357 }
12358
IsEmpty()12359 bool VmaBlockVector::IsEmpty()
12360 {
12361 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12362 return m_Blocks.empty();
12363 }
12364
IsCorruptionDetectionEnabled()12365 bool VmaBlockVector::IsCorruptionDetectionEnabled() const
12366 {
12367 const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
12368 return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
12369 (VMA_DEBUG_MARGIN > 0) &&
12370 (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&
12371 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
12372 }
12373
Allocate(VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)12374 VkResult VmaBlockVector::Allocate(
12375 VkDeviceSize size,
12376 VkDeviceSize alignment,
12377 const VmaAllocationCreateInfo& createInfo,
12378 VmaSuballocationType suballocType,
12379 size_t allocationCount,
12380 VmaAllocation* pAllocations)
12381 {
12382 size_t allocIndex;
12383 VkResult res = VK_SUCCESS;
12384
12385 alignment = VMA_MAX(alignment, m_MinAllocationAlignment);
12386
12387 if (IsCorruptionDetectionEnabled())
12388 {
12389 size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12390 alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));
12391 }
12392
12393 {
12394 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12395 for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
12396 {
12397 res = AllocatePage(
12398 size,
12399 alignment,
12400 createInfo,
12401 suballocType,
12402 pAllocations + allocIndex);
12403 if (res != VK_SUCCESS)
12404 {
12405 break;
12406 }
12407 }
12408 }
12409
12410 if (res != VK_SUCCESS)
12411 {
12412 // Free all already created allocations.
12413 while (allocIndex--)
12414 Free(pAllocations[allocIndex]);
12415 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
12416 }
12417
12418 return res;
12419 }
12420
AllocatePage(VkDeviceSize size,VkDeviceSize alignment,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12421 VkResult VmaBlockVector::AllocatePage(
12422 VkDeviceSize size,
12423 VkDeviceSize alignment,
12424 const VmaAllocationCreateInfo& createInfo,
12425 VmaSuballocationType suballocType,
12426 VmaAllocation* pAllocation)
12427 {
12428 const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12429
12430 VkDeviceSize freeMemory;
12431 {
12432 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12433 VmaBudget heapBudget = {};
12434 m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
12435 freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;
12436 }
12437
12438 const bool canFallbackToDedicated = !HasExplicitBlockSize() &&
12439 (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0;
12440 const bool canCreateNewBlock =
12441 ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
12442 (m_Blocks.size() < m_MaxBlockCount) &&
12443 (freeMemory >= size || !canFallbackToDedicated);
12444 uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
12445
12446 // Upper address can only be used with linear allocator and within single memory block.
12447 if (isUpperAddress &&
12448 (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
12449 {
12450 return VK_ERROR_FEATURE_NOT_PRESENT;
12451 }
12452
12453 // Early reject: requested allocation size is larger that maximum block size for this block vector.
12454 if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize)
12455 {
12456 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12457 }
12458
12459 // 1. Search existing allocations. Try to allocate.
12460 if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12461 {
12462 // Use only last block.
12463 if (!m_Blocks.empty())
12464 {
12465 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
12466 VMA_ASSERT(pCurrBlock);
12467 VkResult res = AllocateFromBlock(
12468 pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12469 if (res == VK_SUCCESS)
12470 {
12471 VMA_DEBUG_LOG(" Returned from last block #%u", pCurrBlock->GetId());
12472 IncrementallySortBlocks();
12473 return VK_SUCCESS;
12474 }
12475 }
12476 }
12477 else
12478 {
12479 if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default
12480 {
12481 const bool isHostVisible =
12482 (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
12483 if(isHostVisible)
12484 {
12485 const bool isMappingAllowed = (createInfo.flags &
12486 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
12487 /*
12488 For non-mappable allocations, check blocks that are not mapped first.
12489 For mappable allocations, check blocks that are already mapped first.
12490 This way, having many blocks, we will separate mappable and non-mappable allocations,
12491 hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc.
12492 */
12493 for(size_t mappingI = 0; mappingI < 2; ++mappingI)
12494 {
12495 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12496 for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12497 {
12498 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12499 VMA_ASSERT(pCurrBlock);
12500 const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL;
12501 if((mappingI == 0) == (isMappingAllowed == isBlockMapped))
12502 {
12503 VkResult res = AllocateFromBlock(
12504 pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12505 if (res == VK_SUCCESS)
12506 {
12507 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12508 IncrementallySortBlocks();
12509 return VK_SUCCESS;
12510 }
12511 }
12512 }
12513 }
12514 }
12515 else
12516 {
12517 // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
12518 for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12519 {
12520 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12521 VMA_ASSERT(pCurrBlock);
12522 VkResult res = AllocateFromBlock(
12523 pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12524 if (res == VK_SUCCESS)
12525 {
12526 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12527 IncrementallySortBlocks();
12528 return VK_SUCCESS;
12529 }
12530 }
12531 }
12532 }
12533 else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT
12534 {
12535 // Backward order in m_Blocks - prefer blocks with largest amount of free space.
12536 for (size_t blockIndex = m_Blocks.size(); blockIndex--; )
12537 {
12538 VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
12539 VMA_ASSERT(pCurrBlock);
12540 VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12541 if (res == VK_SUCCESS)
12542 {
12543 VMA_DEBUG_LOG(" Returned from existing block #%u", pCurrBlock->GetId());
12544 IncrementallySortBlocks();
12545 return VK_SUCCESS;
12546 }
12547 }
12548 }
12549 }
12550
12551 // 2. Try to create new block.
12552 if (canCreateNewBlock)
12553 {
12554 // Calculate optimal size for new block.
12555 VkDeviceSize newBlockSize = m_PreferredBlockSize;
12556 uint32_t newBlockSizeShift = 0;
12557 const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
12558
12559 if (!m_ExplicitBlockSize)
12560 {
12561 // Allocate 1/8, 1/4, 1/2 as first blocks.
12562 const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
12563 for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
12564 {
12565 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12566 if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
12567 {
12568 newBlockSize = smallerNewBlockSize;
12569 ++newBlockSizeShift;
12570 }
12571 else
12572 {
12573 break;
12574 }
12575 }
12576 }
12577
12578 size_t newBlockIndex = 0;
12579 VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12580 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12581 // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
12582 if (!m_ExplicitBlockSize)
12583 {
12584 while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
12585 {
12586 const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
12587 if (smallerNewBlockSize >= size)
12588 {
12589 newBlockSize = smallerNewBlockSize;
12590 ++newBlockSizeShift;
12591 res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?
12592 CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;
12593 }
12594 else
12595 {
12596 break;
12597 }
12598 }
12599 }
12600
12601 if (res == VK_SUCCESS)
12602 {
12603 VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
12604 VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
12605
12606 res = AllocateFromBlock(
12607 pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);
12608 if (res == VK_SUCCESS)
12609 {
12610 VMA_DEBUG_LOG(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);
12611 IncrementallySortBlocks();
12612 return VK_SUCCESS;
12613 }
12614 else
12615 {
12616 // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
12617 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12618 }
12619 }
12620 }
12621
12622 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12623 }
12624
Free(const VmaAllocation hAllocation)12625 void VmaBlockVector::Free(const VmaAllocation hAllocation)
12626 {
12627 VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
12628
12629 bool budgetExceeded = false;
12630 {
12631 const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);
12632 VmaBudget heapBudget = {};
12633 m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);
12634 budgetExceeded = heapBudget.usage >= heapBudget.budget;
12635 }
12636
12637 // Scope for lock.
12638 {
12639 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12640
12641 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
12642
12643 if (IsCorruptionDetectionEnabled())
12644 {
12645 VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
12646 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
12647 }
12648
12649 if (hAllocation->IsPersistentMap())
12650 {
12651 pBlock->Unmap(m_hAllocator, 1);
12652 }
12653
12654 const bool hadEmptyBlockBeforeFree = HasEmptyBlock();
12655 pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());
12656 pBlock->PostFree(m_hAllocator);
12657 VMA_HEAVY_ASSERT(pBlock->Validate());
12658
12659 VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);
12660
12661 const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;
12662 // pBlock became empty after this deallocation.
12663 if (pBlock->m_pMetadata->IsEmpty())
12664 {
12665 // Already had empty block. We don't want to have two, so delete this one.
12666 if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock)
12667 {
12668 pBlockToDelete = pBlock;
12669 Remove(pBlock);
12670 }
12671 // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth.
12672 }
12673 // pBlock didn't become empty, but we have another empty block - find and free that one.
12674 // (This is optional, heuristics.)
12675 else if (hadEmptyBlockBeforeFree && canDeleteBlock)
12676 {
12677 VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
12678 if (pLastBlock->m_pMetadata->IsEmpty())
12679 {
12680 pBlockToDelete = pLastBlock;
12681 m_Blocks.pop_back();
12682 }
12683 }
12684
12685 IncrementallySortBlocks();
12686 }
12687
12688 // Destruction of a free block. Deferred until this point, outside of mutex
12689 // lock, for performance reason.
12690 if (pBlockToDelete != VMA_NULL)
12691 {
12692 VMA_DEBUG_LOG(" Deleted empty block #%u", pBlockToDelete->GetId());
12693 pBlockToDelete->Destroy(m_hAllocator);
12694 vma_delete(m_hAllocator, pBlockToDelete);
12695 }
12696
12697 m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());
12698 m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);
12699 }
12700
CalcMaxBlockSize()12701 VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
12702 {
12703 VkDeviceSize result = 0;
12704 for (size_t i = m_Blocks.size(); i--; )
12705 {
12706 result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
12707 if (result >= m_PreferredBlockSize)
12708 {
12709 break;
12710 }
12711 }
12712 return result;
12713 }
12714
Remove(VmaDeviceMemoryBlock * pBlock)12715 void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
12716 {
12717 for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12718 {
12719 if (m_Blocks[blockIndex] == pBlock)
12720 {
12721 VmaVectorRemove(m_Blocks, blockIndex);
12722 return;
12723 }
12724 }
12725 VMA_ASSERT(0);
12726 }
12727
IncrementallySortBlocks()12728 void VmaBlockVector::IncrementallySortBlocks()
12729 {
12730 if (!m_IncrementalSort)
12731 return;
12732 if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
12733 {
12734 // Bubble sort only until first swap.
12735 for (size_t i = 1; i < m_Blocks.size(); ++i)
12736 {
12737 if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
12738 {
12739 VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
12740 return;
12741 }
12742 }
12743 }
12744 }
12745
SortByFreeSize()12746 void VmaBlockVector::SortByFreeSize()
12747 {
12748 VMA_SORT(m_Blocks.begin(), m_Blocks.end(),
12749 [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool
12750 {
12751 return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();
12752 });
12753 }
12754
AllocateFromBlock(VmaDeviceMemoryBlock * pBlock,VkDeviceSize size,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,uint32_t strategy,VmaAllocation * pAllocation)12755 VkResult VmaBlockVector::AllocateFromBlock(
12756 VmaDeviceMemoryBlock* pBlock,
12757 VkDeviceSize size,
12758 VkDeviceSize alignment,
12759 VmaAllocationCreateFlags allocFlags,
12760 void* pUserData,
12761 VmaSuballocationType suballocType,
12762 uint32_t strategy,
12763 VmaAllocation* pAllocation)
12764 {
12765 const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
12766
12767 VmaAllocationRequest currRequest = {};
12768 if (pBlock->m_pMetadata->CreateAllocationRequest(
12769 size,
12770 alignment,
12771 isUpperAddress,
12772 suballocType,
12773 strategy,
12774 &currRequest))
12775 {
12776 return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation);
12777 }
12778 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
12779 }
12780
CommitAllocationRequest(VmaAllocationRequest & allocRequest,VmaDeviceMemoryBlock * pBlock,VkDeviceSize alignment,VmaAllocationCreateFlags allocFlags,void * pUserData,VmaSuballocationType suballocType,VmaAllocation * pAllocation)12781 VkResult VmaBlockVector::CommitAllocationRequest(
12782 VmaAllocationRequest& allocRequest,
12783 VmaDeviceMemoryBlock* pBlock,
12784 VkDeviceSize alignment,
12785 VmaAllocationCreateFlags allocFlags,
12786 void* pUserData,
12787 VmaSuballocationType suballocType,
12788 VmaAllocation* pAllocation)
12789 {
12790 const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
12791 const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
12792 const bool isMappingAllowed = (allocFlags &
12793 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;
12794
12795 pBlock->PostAlloc();
12796 // Allocate from pCurrBlock.
12797 if (mapped)
12798 {
12799 VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
12800 if (res != VK_SUCCESS)
12801 {
12802 return res;
12803 }
12804 }
12805
12806 *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed);
12807 pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation);
12808 (*pAllocation)->InitBlockAllocation(
12809 pBlock,
12810 allocRequest.allocHandle,
12811 alignment,
12812 allocRequest.size, // Not size, as actual allocation size may be larger than requested!
12813 m_MemoryTypeIndex,
12814 suballocType,
12815 mapped);
12816 VMA_HEAVY_ASSERT(pBlock->Validate());
12817 if (isUserDataString)
12818 (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData);
12819 else
12820 (*pAllocation)->SetUserData(m_hAllocator, pUserData);
12821 m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size);
12822 if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)
12823 {
12824 m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
12825 }
12826 if (IsCorruptionDetectionEnabled())
12827 {
12828 VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size);
12829 VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
12830 }
12831 return VK_SUCCESS;
12832 }
12833
CreateBlock(VkDeviceSize blockSize,size_t * pNewBlockIndex)12834 VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
12835 {
12836 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
12837 allocInfo.pNext = m_pMemoryAllocateNext;
12838 allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
12839 allocInfo.allocationSize = blockSize;
12840
12841 #if VMA_BUFFER_DEVICE_ADDRESS
12842 // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.
12843 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
12844 if (m_hAllocator->m_UseKhrBufferDeviceAddress)
12845 {
12846 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
12847 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
12848 }
12849 #endif // VMA_BUFFER_DEVICE_ADDRESS
12850
12851 #if VMA_MEMORY_PRIORITY
12852 VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
12853 if (m_hAllocator->m_UseExtMemoryPriority)
12854 {
12855 VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f);
12856 priorityInfo.priority = m_Priority;
12857 VmaPnextChainPushFront(&allocInfo, &priorityInfo);
12858 }
12859 #endif // VMA_MEMORY_PRIORITY
12860
12861 #if VMA_EXTERNAL_MEMORY
12862 // Attach VkExportMemoryAllocateInfoKHR if necessary.
12863 VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
12864 exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);
12865 if (exportMemoryAllocInfo.handleTypes != 0)
12866 {
12867 VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
12868 }
12869 #endif // VMA_EXTERNAL_MEMORY
12870
12871 VkDeviceMemory mem = VK_NULL_HANDLE;
12872 VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
12873 if (res < 0)
12874 {
12875 return res;
12876 }
12877
12878 // New VkDeviceMemory successfully created.
12879
12880 // Create new Allocation for it.
12881 VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
12882 pBlock->Init(
12883 m_hAllocator,
12884 m_hParentPool,
12885 m_MemoryTypeIndex,
12886 mem,
12887 allocInfo.allocationSize,
12888 m_NextBlockId++,
12889 m_Algorithm,
12890 m_BufferImageGranularity);
12891
12892 m_Blocks.push_back(pBlock);
12893 if (pNewBlockIndex != VMA_NULL)
12894 {
12895 *pNewBlockIndex = m_Blocks.size() - 1;
12896 }
12897
12898 return VK_SUCCESS;
12899 }
12900
HasEmptyBlock()12901 bool VmaBlockVector::HasEmptyBlock()
12902 {
12903 for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)
12904 {
12905 VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
12906 if (pBlock->m_pMetadata->IsEmpty())
12907 {
12908 return true;
12909 }
12910 }
12911 return false;
12912 }
12913
12914 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(class VmaJsonWriter & json)12915 void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
12916 {
12917 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12918
12919
12920 json.BeginObject();
12921 for (size_t i = 0; i < m_Blocks.size(); ++i)
12922 {
12923 json.BeginString();
12924 json.ContinueString(m_Blocks[i]->GetId());
12925 json.EndString();
12926
12927 json.BeginObject();
12928 json.WriteString("MapRefCount");
12929 json.WriteNumber(m_Blocks[i]->GetMapRefCount());
12930
12931 m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
12932 json.EndObject();
12933 }
12934 json.EndObject();
12935 }
12936 #endif // VMA_STATS_STRING_ENABLED
12937
CheckCorruption()12938 VkResult VmaBlockVector::CheckCorruption()
12939 {
12940 if (!IsCorruptionDetectionEnabled())
12941 {
12942 return VK_ERROR_FEATURE_NOT_PRESENT;
12943 }
12944
12945 VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
12946 for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
12947 {
12948 VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
12949 VMA_ASSERT(pBlock);
12950 VkResult res = pBlock->CheckCorruption(m_hAllocator);
12951 if (res != VK_SUCCESS)
12952 {
12953 return res;
12954 }
12955 }
12956 return VK_SUCCESS;
12957 }
12958
FreeEmptyBlock()12959 void VmaBlockVector::FreeEmptyBlock()
12960 {
12961 // Force deletion of empty blocks.
12962 VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
12963 for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
12964 {
12965 VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
12966 if(pBlock->m_pMetadata->IsEmpty())
12967 {
12968 if(m_Blocks.size() > m_MinBlockCount)
12969 {
12970 VmaVectorRemove(m_Blocks, blockIndex);
12971 pBlock->Destroy(m_hAllocator);
12972 vma_delete(m_hAllocator, pBlock);
12973 }
12974 else
12975 {
12976 break;
12977 }
12978 }
12979 }
12980 }
12981
12982 #endif // _VMA_BLOCK_VECTOR_FUNCTIONS
12983
12984 #ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
VmaDefragmentationContext_T(VmaAllocator hAllocator,const VmaDefragmentationInfo & info)12985 VmaDefragmentationContext_T::VmaDefragmentationContext_T(
12986 VmaAllocator hAllocator,
12987 const VmaDefragmentationInfo& info)
12988 : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass),
12989 m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass),
12990 m_MoveAllocator(hAllocator->GetAllocationCallbacks()),
12991 m_Moves(m_MoveAllocator)
12992 {
12993 m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK;
12994
12995 if (info.pool != VMA_NULL)
12996 {
12997 m_BlockVectorCount = 1;
12998 m_PoolBlockVector = &info.pool->m_BlockVector;
12999 m_pBlockVectors = &m_PoolBlockVector;
13000 m_PoolBlockVector->SetIncrementalSort(false);
13001 m_PoolBlockVector->SortByFreeSize();
13002 }
13003 else
13004 {
13005 m_BlockVectorCount = hAllocator->GetMemoryTypeCount();
13006 m_PoolBlockVector = VMA_NULL;
13007 m_pBlockVectors = hAllocator->m_pBlockVectors;
13008 for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
13009 {
13010 VmaBlockVector* vector = m_pBlockVectors[i];
13011 if (vector != VMA_NULL)
13012 {
13013 vector->SetIncrementalSort(false);
13014 vector->SortByFreeSize();
13015 }
13016 }
13017 }
13018
13019 switch (m_Algorithm)
13020 {
13021 case 0: // Default algorithm
13022 m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT;
13023 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
13024 {
13025 m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);
13026 break;
13027 }
13028 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13029 {
13030 if (hAllocator->GetBufferImageGranularity() > 1)
13031 {
13032 m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount);
13033 }
13034 break;
13035 }
13036 }
13037 }
13038
~VmaDefragmentationContext_T()13039 VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
13040 {
13041 if (m_PoolBlockVector != VMA_NULL)
13042 {
13043 m_PoolBlockVector->SetIncrementalSort(true);
13044 }
13045 else
13046 {
13047 for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
13048 {
13049 VmaBlockVector* vector = m_pBlockVectors[i];
13050 if (vector != VMA_NULL)
13051 vector->SetIncrementalSort(true);
13052 }
13053 }
13054
13055 if (m_AlgorithmState)
13056 {
13057 switch (m_Algorithm)
13058 {
13059 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
13060 vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);
13061 break;
13062 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13063 vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateExtensive*>(m_AlgorithmState), m_BlockVectorCount);
13064 break;
13065 default:
13066 VMA_ASSERT(0);
13067 }
13068 }
13069 }
13070
DefragmentPassBegin(VmaDefragmentationPassMoveInfo & moveInfo)13071 VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo)
13072 {
13073 if (m_PoolBlockVector != VMA_NULL)
13074 {
13075 VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex);
13076
13077 if (m_PoolBlockVector->GetBlockCount() > 1)
13078 ComputeDefragmentation(*m_PoolBlockVector, 0);
13079 else if (m_PoolBlockVector->GetBlockCount() == 1)
13080 ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));
13081 }
13082 else
13083 {
13084 for (uint32_t i = 0; i < m_BlockVectorCount; ++i)
13085 {
13086 if (m_pBlockVectors[i] != VMA_NULL)
13087 {
13088 VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex);
13089
13090 if (m_pBlockVectors[i]->GetBlockCount() > 1)
13091 {
13092 if (ComputeDefragmentation(*m_pBlockVectors[i], i))
13093 break;
13094 }
13095 else if (m_pBlockVectors[i]->GetBlockCount() == 1)
13096 {
13097 if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)))
13098 break;
13099 }
13100 }
13101 }
13102 }
13103
13104 moveInfo.moveCount = static_cast<uint32_t>(m_Moves.size());
13105 if (moveInfo.moveCount > 0)
13106 {
13107 moveInfo.pMoves = m_Moves.data();
13108 return VK_INCOMPLETE;
13109 }
13110
13111 moveInfo.pMoves = VMA_NULL;
13112 return VK_SUCCESS;
13113 }
13114
DefragmentPassEnd(VmaDefragmentationPassMoveInfo & moveInfo)13115 VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo)
13116 {
13117 VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true);
13118
13119 VkResult result = VK_SUCCESS;
13120 VmaStlAllocator<FragmentedBlock> blockAllocator(m_MoveAllocator.m_pCallbacks);
13121 VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> immovableBlocks(blockAllocator);
13122 VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> mappedBlocks(blockAllocator);
13123
13124 VmaAllocator allocator = VMA_NULL;
13125 for (uint32_t i = 0; i < moveInfo.moveCount; ++i)
13126 {
13127 VmaDefragmentationMove& move = moveInfo.pMoves[i];
13128 size_t prevCount = 0, currentCount = 0;
13129 VkDeviceSize freedBlockSize = 0;
13130
13131 uint32_t vectorIndex;
13132 VmaBlockVector* vector;
13133 if (m_PoolBlockVector != VMA_NULL)
13134 {
13135 vectorIndex = 0;
13136 vector = m_PoolBlockVector;
13137 }
13138 else
13139 {
13140 vectorIndex = move.srcAllocation->GetMemoryTypeIndex();
13141 vector = m_pBlockVectors[vectorIndex];
13142 VMA_ASSERT(vector != VMA_NULL);
13143 }
13144
13145 switch (move.operation)
13146 {
13147 case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY:
13148 {
13149 uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation);
13150 if (mapCount > 0)
13151 {
13152 allocator = vector->m_hAllocator;
13153 VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock();
13154 bool notPresent = true;
13155 for (FragmentedBlock& block : mappedBlocks)
13156 {
13157 if (block.block == newMapBlock)
13158 {
13159 notPresent = false;
13160 block.data += mapCount;
13161 break;
13162 }
13163 }
13164 if (notPresent)
13165 mappedBlocks.push_back({ mapCount, newMapBlock });
13166 }
13167
13168 // Scope for locks, Free have it's own lock
13169 {
13170 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13171 prevCount = vector->GetBlockCount();
13172 freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
13173 }
13174 vector->Free(move.dstTmpAllocation);
13175 {
13176 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13177 currentCount = vector->GetBlockCount();
13178 }
13179
13180 result = VK_INCOMPLETE;
13181 break;
13182 }
13183 case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE:
13184 {
13185 m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
13186 --m_PassStats.allocationsMoved;
13187 vector->Free(move.dstTmpAllocation);
13188
13189 VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock();
13190 bool notPresent = true;
13191 for (const FragmentedBlock& block : immovableBlocks)
13192 {
13193 if (block.block == newBlock)
13194 {
13195 notPresent = false;
13196 break;
13197 }
13198 }
13199 if (notPresent)
13200 immovableBlocks.push_back({ vectorIndex, newBlock });
13201 break;
13202 }
13203 case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY:
13204 {
13205 m_PassStats.bytesMoved -= move.srcAllocation->GetSize();
13206 --m_PassStats.allocationsMoved;
13207 // Scope for locks, Free have it's own lock
13208 {
13209 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13210 prevCount = vector->GetBlockCount();
13211 freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize();
13212 }
13213 vector->Free(move.srcAllocation);
13214 {
13215 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13216 currentCount = vector->GetBlockCount();
13217 }
13218 freedBlockSize *= prevCount - currentCount;
13219
13220 VkDeviceSize dstBlockSize;
13221 {
13222 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13223 dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();
13224 }
13225 vector->Free(move.dstTmpAllocation);
13226 {
13227 VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13228 freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());
13229 currentCount = vector->GetBlockCount();
13230 }
13231
13232 result = VK_INCOMPLETE;
13233 break;
13234 }
13235 default:
13236 VMA_ASSERT(0);
13237 }
13238
13239 if (prevCount > currentCount)
13240 {
13241 size_t freedBlocks = prevCount - currentCount;
13242 m_PassStats.deviceMemoryBlocksFreed += static_cast<uint32_t>(freedBlocks);
13243 m_PassStats.bytesFreed += freedBlockSize;
13244 }
13245
13246 switch (m_Algorithm)
13247 {
13248 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13249 {
13250 if (m_AlgorithmState != VMA_NULL)
13251 {
13252 // Avoid unnecessary tries to allocate when new free block is avaiable
13253 StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];
13254 if (state.firstFreeBlock != SIZE_MAX)
13255 {
13256 const size_t diff = prevCount - currentCount;
13257 if (state.firstFreeBlock >= diff)
13258 {
13259 state.firstFreeBlock -= diff;
13260 if (state.firstFreeBlock != 0)
13261 state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();
13262 }
13263 else
13264 state.firstFreeBlock = 0;
13265 }
13266 }
13267 }
13268 }
13269 }
13270 moveInfo.moveCount = 0;
13271 moveInfo.pMoves = VMA_NULL;
13272 m_Moves.clear();
13273
13274 // Update stats
13275 m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved;
13276 m_GlobalStats.bytesFreed += m_PassStats.bytesFreed;
13277 m_GlobalStats.bytesMoved += m_PassStats.bytesMoved;
13278 m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed;
13279 m_PassStats = { 0 };
13280
13281 // Move blocks with immovable allocations according to algorithm
13282 if (immovableBlocks.size() > 0)
13283 {
13284 switch (m_Algorithm)
13285 {
13286 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13287 {
13288 if (m_AlgorithmState != VMA_NULL)
13289 {
13290 bool swapped = false;
13291 // Move to the start of free blocks range
13292 for (const FragmentedBlock& block : immovableBlocks)
13293 {
13294 StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[block.data];
13295 if (state.operation != StateExtensive::Operation::Cleanup)
13296 {
13297 VmaBlockVector* vector = m_pBlockVectors[block.data];
13298 VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13299
13300 for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i)
13301 {
13302 if (vector->GetBlock(i) == block.block)
13303 {
13304 VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]);
13305 if (state.firstFreeBlock != SIZE_MAX)
13306 {
13307 if (i + 1 < state.firstFreeBlock)
13308 {
13309 if (state.firstFreeBlock > 1)
13310 VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);
13311 else
13312 --state.firstFreeBlock;
13313 }
13314 }
13315 swapped = true;
13316 break;
13317 }
13318 }
13319 }
13320 }
13321 if (swapped)
13322 result = VK_INCOMPLETE;
13323 break;
13324 }
13325 }
13326 default:
13327 {
13328 // Move to the begining
13329 for (const FragmentedBlock& block : immovableBlocks)
13330 {
13331 VmaBlockVector* vector = m_pBlockVectors[block.data];
13332 VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);
13333
13334 for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)
13335 {
13336 if (vector->GetBlock(i) == block.block)
13337 {
13338 VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);
13339 break;
13340 }
13341 }
13342 }
13343 break;
13344 }
13345 }
13346 }
13347
13348 // Bulk-map destination blocks
13349 for (const FragmentedBlock& block : mappedBlocks)
13350 {
13351 VkResult res = block.block->Map(allocator, block.data, VMA_NULL);
13352 VMA_ASSERT(res == VK_SUCCESS);
13353 }
13354 return result;
13355 }
13356
ComputeDefragmentation(VmaBlockVector & vector,size_t index)13357 bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index)
13358 {
13359 switch (m_Algorithm)
13360 {
13361 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT:
13362 return ComputeDefragmentation_Fast(vector);
13363 default:
13364 VMA_ASSERT(0);
13365 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:
13366 return ComputeDefragmentation_Balanced(vector, index, true);
13367 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT:
13368 return ComputeDefragmentation_Full(vector);
13369 case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:
13370 return ComputeDefragmentation_Extensive(vector, index);
13371 }
13372 }
13373
GetMoveData(VmaAllocHandle handle,VmaBlockMetadata * metadata)13374 VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData(
13375 VmaAllocHandle handle, VmaBlockMetadata* metadata)
13376 {
13377 MoveAllocationData moveData;
13378 moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle);
13379 moveData.size = moveData.move.srcAllocation->GetSize();
13380 moveData.alignment = moveData.move.srcAllocation->GetAlignment();
13381 moveData.type = moveData.move.srcAllocation->GetSuballocationType();
13382 moveData.flags = 0;
13383
13384 if (moveData.move.srcAllocation->IsPersistentMap())
13385 moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
13386 if (moveData.move.srcAllocation->IsMappingAllowed())
13387 moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
13388
13389 return moveData;
13390 }
13391
CheckCounters(VkDeviceSize bytes)13392 VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes)
13393 {
13394 // Ignore allocation if will exceed max size for copy
13395 if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes)
13396 {
13397 if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)
13398 return CounterStatus::Ignore;
13399 else
13400 return CounterStatus::End;
13401 }
13402 return CounterStatus::Pass;
13403 }
13404
IncrementCounters(VkDeviceSize bytes)13405 bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes)
13406 {
13407 m_PassStats.bytesMoved += bytes;
13408 // Early return when max found
13409 if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes)
13410 {
13411 VMA_ASSERT(m_PassStats.allocationsMoved == m_MaxPassAllocations ||
13412 m_PassStats.bytesMoved == m_MaxPassBytes && "Exceeded maximal pass threshold!");
13413 return true;
13414 }
13415 return false;
13416 }
13417
ReallocWithinBlock(VmaBlockVector & vector,VmaDeviceMemoryBlock * block)13418 bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block)
13419 {
13420 VmaBlockMetadata* metadata = block->m_pMetadata;
13421
13422 for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13423 handle != VK_NULL_HANDLE;
13424 handle = metadata->GetNextAllocation(handle))
13425 {
13426 MoveAllocationData moveData = GetMoveData(handle, metadata);
13427 // Ignore newly created allocations by defragmentation algorithm
13428 if (moveData.move.srcAllocation->GetUserData() == this)
13429 continue;
13430 switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13431 {
13432 case CounterStatus::Ignore:
13433 continue;
13434 case CounterStatus::End:
13435 return true;
13436 default:
13437 VMA_ASSERT(0);
13438 case CounterStatus::Pass:
13439 break;
13440 }
13441
13442 VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
13443 if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
13444 {
13445 VmaAllocationRequest request = {};
13446 if (metadata->CreateAllocationRequest(
13447 moveData.size,
13448 moveData.alignment,
13449 false,
13450 moveData.type,
13451 VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
13452 &request))
13453 {
13454 if (metadata->GetAllocationOffset(request.allocHandle) < offset)
13455 {
13456 if (vector.CommitAllocationRequest(
13457 request,
13458 block,
13459 moveData.alignment,
13460 moveData.flags,
13461 this,
13462 moveData.type,
13463 &moveData.move.dstTmpAllocation) == VK_SUCCESS)
13464 {
13465 m_Moves.push_back(moveData.move);
13466 if (IncrementCounters(moveData.size))
13467 return true;
13468 }
13469 }
13470 }
13471 }
13472 }
13473 return false;
13474 }
13475
AllocInOtherBlock(size_t start,size_t end,MoveAllocationData & data,VmaBlockVector & vector)13476 bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector)
13477 {
13478 for (; start < end; ++start)
13479 {
13480 VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start);
13481 if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)
13482 {
13483 if (vector.AllocateFromBlock(dstBlock,
13484 data.size,
13485 data.alignment,
13486 data.flags,
13487 this,
13488 data.type,
13489 0,
13490 &data.move.dstTmpAllocation) == VK_SUCCESS)
13491 {
13492 m_Moves.push_back(data.move);
13493 if (IncrementCounters(data.size))
13494 return true;
13495 break;
13496 }
13497 }
13498 }
13499 return false;
13500 }
13501
ComputeDefragmentation_Fast(VmaBlockVector & vector)13502 bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector)
13503 {
13504 // Move only between blocks
13505
13506 // Go through allocations in last blocks and try to fit them inside first ones
13507 for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
13508 {
13509 VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
13510
13511 for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13512 handle != VK_NULL_HANDLE;
13513 handle = metadata->GetNextAllocation(handle))
13514 {
13515 MoveAllocationData moveData = GetMoveData(handle, metadata);
13516 // Ignore newly created allocations by defragmentation algorithm
13517 if (moveData.move.srcAllocation->GetUserData() == this)
13518 continue;
13519 switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13520 {
13521 case CounterStatus::Ignore:
13522 continue;
13523 case CounterStatus::End:
13524 return true;
13525 default:
13526 VMA_ASSERT(0);
13527 case CounterStatus::Pass:
13528 break;
13529 }
13530
13531 // Check all previous blocks for free space
13532 if (AllocInOtherBlock(0, i, moveData, vector))
13533 return true;
13534 }
13535 }
13536 return false;
13537 }
13538
ComputeDefragmentation_Balanced(VmaBlockVector & vector,size_t index,bool update)13539 bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update)
13540 {
13541 // Go over every allocation and try to fit it in previous blocks at lowest offsets,
13542 // if not possible: realloc within single block to minimize offset (exclude offset == 0),
13543 // but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block)
13544 VMA_ASSERT(m_AlgorithmState != VMA_NULL);
13545
13546 StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];
13547 if (update && vectorState.avgAllocSize == UINT64_MAX)
13548 UpdateVectorStatistics(vector, vectorState);
13549
13550 const size_t startMoveCount = m_Moves.size();
13551 VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2;
13552 for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
13553 {
13554 VmaDeviceMemoryBlock* block = vector.GetBlock(i);
13555 VmaBlockMetadata* metadata = block->m_pMetadata;
13556 VkDeviceSize prevFreeRegionSize = 0;
13557
13558 for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13559 handle != VK_NULL_HANDLE;
13560 handle = metadata->GetNextAllocation(handle))
13561 {
13562 MoveAllocationData moveData = GetMoveData(handle, metadata);
13563 // Ignore newly created allocations by defragmentation algorithm
13564 if (moveData.move.srcAllocation->GetUserData() == this)
13565 continue;
13566 switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13567 {
13568 case CounterStatus::Ignore:
13569 continue;
13570 case CounterStatus::End:
13571 return true;
13572 default:
13573 VMA_ASSERT(0);
13574 case CounterStatus::Pass:
13575 break;
13576 }
13577
13578 // Check all previous blocks for free space
13579 const size_t prevMoveCount = m_Moves.size();
13580 if (AllocInOtherBlock(0, i, moveData, vector))
13581 return true;
13582
13583 VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);
13584 // If no room found then realloc within block for lower offset
13585 VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
13586 if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
13587 {
13588 // Check if realloc will make sense
13589 if (prevFreeRegionSize >= minimalFreeRegion ||
13590 nextFreeRegionSize >= minimalFreeRegion ||
13591 moveData.size <= vectorState.avgFreeSize ||
13592 moveData.size <= vectorState.avgAllocSize)
13593 {
13594 VmaAllocationRequest request = {};
13595 if (metadata->CreateAllocationRequest(
13596 moveData.size,
13597 moveData.alignment,
13598 false,
13599 moveData.type,
13600 VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
13601 &request))
13602 {
13603 if (metadata->GetAllocationOffset(request.allocHandle) < offset)
13604 {
13605 if (vector.CommitAllocationRequest(
13606 request,
13607 block,
13608 moveData.alignment,
13609 moveData.flags,
13610 this,
13611 moveData.type,
13612 &moveData.move.dstTmpAllocation) == VK_SUCCESS)
13613 {
13614 m_Moves.push_back(moveData.move);
13615 if (IncrementCounters(moveData.size))
13616 return true;
13617 }
13618 }
13619 }
13620 }
13621 }
13622 prevFreeRegionSize = nextFreeRegionSize;
13623 }
13624 }
13625
13626 // No moves perfomed, update statistics to current vector state
13627 if (startMoveCount == m_Moves.size() && !update)
13628 {
13629 vectorState.avgAllocSize = UINT64_MAX;
13630 return ComputeDefragmentation_Balanced(vector, index, false);
13631 }
13632 return false;
13633 }
13634
ComputeDefragmentation_Full(VmaBlockVector & vector)13635 bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector)
13636 {
13637 // Go over every allocation and try to fit it in previous blocks at lowest offsets,
13638 // if not possible: realloc within single block to minimize offset (exclude offset == 0)
13639
13640 for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)
13641 {
13642 VmaDeviceMemoryBlock* block = vector.GetBlock(i);
13643 VmaBlockMetadata* metadata = block->m_pMetadata;
13644
13645 for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13646 handle != VK_NULL_HANDLE;
13647 handle = metadata->GetNextAllocation(handle))
13648 {
13649 MoveAllocationData moveData = GetMoveData(handle, metadata);
13650 // Ignore newly created allocations by defragmentation algorithm
13651 if (moveData.move.srcAllocation->GetUserData() == this)
13652 continue;
13653 switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13654 {
13655 case CounterStatus::Ignore:
13656 continue;
13657 case CounterStatus::End:
13658 return true;
13659 default:
13660 VMA_ASSERT(0);
13661 case CounterStatus::Pass:
13662 break;
13663 }
13664
13665 // Check all previous blocks for free space
13666 const size_t prevMoveCount = m_Moves.size();
13667 if (AllocInOtherBlock(0, i, moveData, vector))
13668 return true;
13669
13670 // If no room found then realloc within block for lower offset
13671 VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();
13672 if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)
13673 {
13674 VmaAllocationRequest request = {};
13675 if (metadata->CreateAllocationRequest(
13676 moveData.size,
13677 moveData.alignment,
13678 false,
13679 moveData.type,
13680 VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,
13681 &request))
13682 {
13683 if (metadata->GetAllocationOffset(request.allocHandle) < offset)
13684 {
13685 if (vector.CommitAllocationRequest(
13686 request,
13687 block,
13688 moveData.alignment,
13689 moveData.flags,
13690 this,
13691 moveData.type,
13692 &moveData.move.dstTmpAllocation) == VK_SUCCESS)
13693 {
13694 m_Moves.push_back(moveData.move);
13695 if (IncrementCounters(moveData.size))
13696 return true;
13697 }
13698 }
13699 }
13700 }
13701 }
13702 }
13703 return false;
13704 }
13705
ComputeDefragmentation_Extensive(VmaBlockVector & vector,size_t index)13706 bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index)
13707 {
13708 // First free single block, then populate it to the brim, then free another block, and so on
13709
13710 // Fallback to previous algorithm since without granularity conflicts it can achieve max packing
13711 if (vector.m_BufferImageGranularity == 1)
13712 return ComputeDefragmentation_Full(vector);
13713
13714 VMA_ASSERT(m_AlgorithmState != VMA_NULL);
13715
13716 StateExtensive& vectorState = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[index];
13717
13718 bool texturePresent = false, bufferPresent = false, otherPresent = false;
13719 switch (vectorState.operation)
13720 {
13721 case StateExtensive::Operation::Done: // Vector defragmented
13722 return false;
13723 case StateExtensive::Operation::FindFreeBlockBuffer:
13724 case StateExtensive::Operation::FindFreeBlockTexture:
13725 case StateExtensive::Operation::FindFreeBlockAll:
13726 {
13727 // No more blocks to free, just perform fast realloc and move to cleanup
13728 if (vectorState.firstFreeBlock == 0)
13729 {
13730 vectorState.operation = StateExtensive::Operation::Cleanup;
13731 return ComputeDefragmentation_Fast(vector);
13732 }
13733
13734 // No free blocks, have to clear last one
13735 size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1;
13736 VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata;
13737
13738 const size_t prevMoveCount = m_Moves.size();
13739 for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin();
13740 handle != VK_NULL_HANDLE;
13741 handle = freeMetadata->GetNextAllocation(handle))
13742 {
13743 MoveAllocationData moveData = GetMoveData(handle, freeMetadata);
13744 switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13745 {
13746 case CounterStatus::Ignore:
13747 continue;
13748 case CounterStatus::End:
13749 return true;
13750 default:
13751 VMA_ASSERT(0);
13752 case CounterStatus::Pass:
13753 break;
13754 }
13755
13756 // Check all previous blocks for free space
13757 if (AllocInOtherBlock(0, last, moveData, vector))
13758 {
13759 // Full clear performed already
13760 if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE)
13761 reinterpret_cast<size_t*>(m_AlgorithmState)[index] = last;
13762 return true;
13763 }
13764 }
13765
13766 if (prevMoveCount == m_Moves.size())
13767 {
13768 // Cannot perform full clear, have to move data in other blocks around
13769 if (last != 0)
13770 {
13771 for (size_t i = last - 1; i; --i)
13772 {
13773 if (ReallocWithinBlock(vector, vector.GetBlock(i)))
13774 return true;
13775 }
13776 }
13777
13778 if (prevMoveCount == m_Moves.size())
13779 {
13780 // No possible reallocs within blocks, try to move them around fast
13781 return ComputeDefragmentation_Fast(vector);
13782 }
13783 }
13784 else
13785 {
13786 switch (vectorState.operation)
13787 {
13788 case StateExtensive::Operation::FindFreeBlockBuffer:
13789 vectorState.operation = StateExtensive::Operation::MoveBuffers;
13790 break;
13791 default:
13792 VMA_ASSERT(0);
13793 case StateExtensive::Operation::FindFreeBlockTexture:
13794 vectorState.operation = StateExtensive::Operation::MoveTextures;
13795 break;
13796 case StateExtensive::Operation::FindFreeBlockAll:
13797 vectorState.operation = StateExtensive::Operation::MoveAll;
13798 break;
13799 }
13800 vectorState.firstFreeBlock = last;
13801 // Nothing done, block found without reallocations, can perform another reallocs in same pass
13802 return ComputeDefragmentation_Extensive(vector, index);
13803 }
13804 break;
13805 }
13806 case StateExtensive::Operation::MoveTextures:
13807 {
13808 if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector,
13809 vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
13810 {
13811 if (texturePresent)
13812 {
13813 vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture;
13814 return ComputeDefragmentation_Extensive(vector, index);
13815 }
13816
13817 if (!bufferPresent && !otherPresent)
13818 {
13819 vectorState.operation = StateExtensive::Operation::Cleanup;
13820 break;
13821 }
13822
13823 // No more textures to move, check buffers
13824 vectorState.operation = StateExtensive::Operation::MoveBuffers;
13825 bufferPresent = false;
13826 otherPresent = false;
13827 }
13828 else
13829 break;
13830 }
13831 case StateExtensive::Operation::MoveBuffers:
13832 {
13833 if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector,
13834 vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
13835 {
13836 if (bufferPresent)
13837 {
13838 vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
13839 return ComputeDefragmentation_Extensive(vector, index);
13840 }
13841
13842 if (!otherPresent)
13843 {
13844 vectorState.operation = StateExtensive::Operation::Cleanup;
13845 break;
13846 }
13847
13848 // No more buffers to move, check all others
13849 vectorState.operation = StateExtensive::Operation::MoveAll;
13850 otherPresent = false;
13851 }
13852 else
13853 break;
13854 }
13855 case StateExtensive::Operation::MoveAll:
13856 {
13857 if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector,
13858 vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))
13859 {
13860 if (otherPresent)
13861 {
13862 vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;
13863 return ComputeDefragmentation_Extensive(vector, index);
13864 }
13865 // Everything moved
13866 vectorState.operation = StateExtensive::Operation::Cleanup;
13867 }
13868 break;
13869 }
13870 case StateExtensive::Operation::Cleanup:
13871 // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062).
13872 break;
13873 }
13874
13875 if (vectorState.operation == StateExtensive::Operation::Cleanup)
13876 {
13877 // All other work done, pack data in blocks even tighter if possible
13878 const size_t prevMoveCount = m_Moves.size();
13879 for (size_t i = 0; i < vector.GetBlockCount(); ++i)
13880 {
13881 if (ReallocWithinBlock(vector, vector.GetBlock(i)))
13882 return true;
13883 }
13884
13885 if (prevMoveCount == m_Moves.size())
13886 vectorState.operation = StateExtensive::Operation::Done;
13887 }
13888 return false;
13889 }
13890
UpdateVectorStatistics(VmaBlockVector & vector,StateBalanced & state)13891 void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state)
13892 {
13893 size_t allocCount = 0;
13894 size_t freeCount = 0;
13895 state.avgFreeSize = 0;
13896 state.avgAllocSize = 0;
13897
13898 for (size_t i = 0; i < vector.GetBlockCount(); ++i)
13899 {
13900 VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;
13901
13902 allocCount += metadata->GetAllocationCount();
13903 freeCount += metadata->GetFreeRegionsCount();
13904 state.avgFreeSize += metadata->GetSumFreeSize();
13905 state.avgAllocSize += metadata->GetSize();
13906 }
13907
13908 state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;
13909 state.avgFreeSize /= freeCount;
13910 }
13911
MoveDataToFreeBlocks(VmaSuballocationType currentType,VmaBlockVector & vector,size_t firstFreeBlock,bool & texturePresent,bool & bufferPresent,bool & otherPresent)13912 bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType,
13913 VmaBlockVector& vector, size_t firstFreeBlock,
13914 bool& texturePresent, bool& bufferPresent, bool& otherPresent)
13915 {
13916 const size_t prevMoveCount = m_Moves.size();
13917 for (size_t i = firstFreeBlock ; i;)
13918 {
13919 VmaDeviceMemoryBlock* block = vector.GetBlock(--i);
13920 VmaBlockMetadata* metadata = block->m_pMetadata;
13921
13922 for (VmaAllocHandle handle = metadata->GetAllocationListBegin();
13923 handle != VK_NULL_HANDLE;
13924 handle = metadata->GetNextAllocation(handle))
13925 {
13926 MoveAllocationData moveData = GetMoveData(handle, metadata);
13927 // Ignore newly created allocations by defragmentation algorithm
13928 if (moveData.move.srcAllocation->GetUserData() == this)
13929 continue;
13930 switch (CheckCounters(moveData.move.srcAllocation->GetSize()))
13931 {
13932 case CounterStatus::Ignore:
13933 continue;
13934 case CounterStatus::End:
13935 return true;
13936 default:
13937 VMA_ASSERT(0);
13938 case CounterStatus::Pass:
13939 break;
13940 }
13941
13942 // Move only single type of resources at once
13943 if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType))
13944 {
13945 // Try to fit allocation into free blocks
13946 if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector))
13947 return false;
13948 }
13949
13950 if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL))
13951 texturePresent = true;
13952 else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER))
13953 bufferPresent = true;
13954 else
13955 otherPresent = true;
13956 }
13957 }
13958 return prevMoveCount == m_Moves.size();
13959 }
13960 #endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS
13961
13962 #ifndef _VMA_POOL_T_FUNCTIONS
VmaPool_T(VmaAllocator hAllocator,const VmaPoolCreateInfo & createInfo,VkDeviceSize preferredBlockSize)13963 VmaPool_T::VmaPool_T(
13964 VmaAllocator hAllocator,
13965 const VmaPoolCreateInfo& createInfo,
13966 VkDeviceSize preferredBlockSize)
13967 : m_BlockVector(
13968 hAllocator,
13969 this, // hParentPool
13970 createInfo.memoryTypeIndex,
13971 createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
13972 createInfo.minBlockCount,
13973 createInfo.maxBlockCount,
13974 (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
13975 createInfo.blockSize != 0, // explicitBlockSize
13976 createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
13977 createInfo.priority,
13978 VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
13979 createInfo.pMemoryAllocateNext),
13980 m_Id(0),
13981 m_Name(VMA_NULL) {}
13982
~VmaPool_T()13983 VmaPool_T::~VmaPool_T()
13984 {
13985 VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
13986 }
13987
SetName(const char * pName)13988 void VmaPool_T::SetName(const char* pName)
13989 {
13990 const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
13991 VmaFreeString(allocs, m_Name);
13992
13993 if (pName != VMA_NULL)
13994 {
13995 m_Name = VmaCreateStringCopy(allocs, pName);
13996 }
13997 else
13998 {
13999 m_Name = VMA_NULL;
14000 }
14001 }
14002 #endif // _VMA_POOL_T_FUNCTIONS
14003
14004 #ifndef _VMA_ALLOCATOR_T_FUNCTIONS
VmaAllocator_T(const VmaAllocatorCreateInfo * pCreateInfo)14005 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
14006 m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
14007 m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),
14008 m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
14009 m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),
14010 m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),
14011 m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),
14012 m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),
14013 m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),
14014 m_hDevice(pCreateInfo->device),
14015 m_hInstance(pCreateInfo->instance),
14016 m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
14017 m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
14018 *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
14019 m_AllocationObjectAllocator(&m_AllocationCallbacks),
14020 m_HeapSizeLimitMask(0),
14021 m_DeviceMemoryCount(0),
14022 m_PreferredLargeHeapBlockSize(0),
14023 m_PhysicalDevice(pCreateInfo->physicalDevice),
14024 m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),
14025 m_NextPoolId(0),
14026 m_GlobalMemoryTypeBits(UINT32_MAX)
14027 {
14028 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14029 {
14030 m_UseKhrDedicatedAllocation = false;
14031 m_UseKhrBindMemory2 = false;
14032 }
14033
14034 if(VMA_DEBUG_DETECT_CORRUPTION)
14035 {
14036 // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
14037 VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
14038 }
14039
14040 VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);
14041
14042 if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))
14043 {
14044 #if !(VMA_DEDICATED_ALLOCATION)
14045 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
14046 {
14047 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
14048 }
14049 #endif
14050 #if !(VMA_BIND_MEMORY2)
14051 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)
14052 {
14053 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");
14054 }
14055 #endif
14056 }
14057 #if !(VMA_MEMORY_BUDGET)
14058 if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)
14059 {
14060 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");
14061 }
14062 #endif
14063 #if !(VMA_BUFFER_DEVICE_ADDRESS)
14064 if(m_UseKhrBufferDeviceAddress)
14065 {
14066 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
14067 }
14068 #endif
14069 #if VMA_VULKAN_VERSION < 1002000
14070 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))
14071 {
14072 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");
14073 }
14074 #endif
14075 #if VMA_VULKAN_VERSION < 1001000
14076 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14077 {
14078 VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");
14079 }
14080 #endif
14081 #if !(VMA_MEMORY_PRIORITY)
14082 if(m_UseExtMemoryPriority)
14083 {
14084 VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");
14085 }
14086 #endif
14087
14088 memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
14089 memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
14090 memset(&m_MemProps, 0, sizeof(m_MemProps));
14091
14092 memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
14093 memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));
14094
14095 #if VMA_EXTERNAL_MEMORY
14096 memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));
14097 #endif // #if VMA_EXTERNAL_MEMORY
14098
14099 if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
14100 {
14101 m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;
14102 m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
14103 m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
14104 }
14105
14106 ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
14107
14108 (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
14109 (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
14110
14111 VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));
14112 VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
14113 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
14114 VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
14115
14116 m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
14117 pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
14118
14119 m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();
14120
14121 #if VMA_EXTERNAL_MEMORY
14122 if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)
14123 {
14124 memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,
14125 sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());
14126 }
14127 #endif // #if VMA_EXTERNAL_MEMORY
14128
14129 if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
14130 {
14131 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
14132 {
14133 const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
14134 if(limit != VK_WHOLE_SIZE)
14135 {
14136 m_HeapSizeLimitMask |= 1u << heapIndex;
14137 if(limit < m_MemProps.memoryHeaps[heapIndex].size)
14138 {
14139 m_MemProps.memoryHeaps[heapIndex].size = limit;
14140 }
14141 }
14142 }
14143 }
14144
14145 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
14146 {
14147 // Create only supported types
14148 if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
14149 {
14150 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
14151 m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
14152 this,
14153 VK_NULL_HANDLE, // hParentPool
14154 memTypeIndex,
14155 preferredBlockSize,
14156 0,
14157 pCreateInfo->maxBlockCount,
14158 GetBufferImageGranularity(),
14159 false, // explicitBlockSize
14160 0, // algorithm
14161 0.5f, // priority (0.5 is the default per Vulkan spec)
14162 GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment
14163 VMA_NULL); // // pMemoryAllocateNext
14164 // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
14165 // becase minBlockCount is 0.
14166 }
14167 }
14168 }
14169
Init(const VmaAllocatorCreateInfo * pCreateInfo)14170 VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
14171 {
14172 VkResult res = VK_SUCCESS;
14173
14174 #if VMA_MEMORY_BUDGET
14175 if(m_UseExtMemoryBudget)
14176 {
14177 UpdateVulkanBudget();
14178 }
14179 #endif // #if VMA_MEMORY_BUDGET
14180
14181 return res;
14182 }
14183
~VmaAllocator_T()14184 VmaAllocator_T::~VmaAllocator_T()
14185 {
14186 VMA_ASSERT(m_Pools.IsEmpty());
14187
14188 for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
14189 {
14190 vma_delete(this, m_pBlockVectors[memTypeIndex]);
14191 }
14192 }
14193
ImportVulkanFunctions(const VmaVulkanFunctions * pVulkanFunctions)14194 void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
14195 {
14196 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14197 ImportVulkanFunctions_Static();
14198 #endif
14199
14200 if(pVulkanFunctions != VMA_NULL)
14201 {
14202 ImportVulkanFunctions_Custom(pVulkanFunctions);
14203 }
14204
14205 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14206 ImportVulkanFunctions_Dynamic();
14207 #endif
14208
14209 ValidateVulkanFunctions();
14210 }
14211
14212 #if VMA_STATIC_VULKAN_FUNCTIONS == 1
14213
ImportVulkanFunctions_Static()14214 void VmaAllocator_T::ImportVulkanFunctions_Static()
14215 {
14216 // Vulkan 1.0
14217 m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr;
14218 m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr;
14219 m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;
14220 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;
14221 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
14222 m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;
14223 m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;
14224 m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;
14225 m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;
14226 m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;
14227 m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;
14228 m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;
14229 m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;
14230 m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;
14231 m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;
14232 m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;
14233 m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;
14234 m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;
14235 m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;
14236
14237 // Vulkan 1.1
14238 #if VMA_VULKAN_VERSION >= 1001000
14239 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14240 {
14241 m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;
14242 m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;
14243 m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;
14244 m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;
14245 m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;
14246 }
14247 #endif
14248
14249 #if VMA_VULKAN_VERSION >= 1003000
14250 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
14251 {
14252 m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements;
14253 m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements;
14254 }
14255 #endif
14256 }
14257
14258 #endif // VMA_STATIC_VULKAN_FUNCTIONS == 1
14259
ImportVulkanFunctions_Custom(const VmaVulkanFunctions * pVulkanFunctions)14260 void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)
14261 {
14262 VMA_ASSERT(pVulkanFunctions != VMA_NULL);
14263
14264 #define VMA_COPY_IF_NOT_NULL(funcName) \
14265 if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
14266
14267 VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr);
14268 VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr);
14269 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
14270 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
14271 VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
14272 VMA_COPY_IF_NOT_NULL(vkFreeMemory);
14273 VMA_COPY_IF_NOT_NULL(vkMapMemory);
14274 VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
14275 VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
14276 VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
14277 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
14278 VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
14279 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
14280 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
14281 VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
14282 VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
14283 VMA_COPY_IF_NOT_NULL(vkCreateImage);
14284 VMA_COPY_IF_NOT_NULL(vkDestroyImage);
14285 VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
14286
14287 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14288 VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
14289 VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
14290 #endif
14291
14292 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
14293 VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);
14294 VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);
14295 #endif
14296
14297 #if VMA_MEMORY_BUDGET
14298 VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);
14299 #endif
14300
14301 #if VMA_VULKAN_VERSION >= 1003000
14302 VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);
14303 VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);
14304 #endif
14305
14306 #undef VMA_COPY_IF_NOT_NULL
14307 }
14308
14309 #if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14310
ImportVulkanFunctions_Dynamic()14311 void VmaAllocator_T::ImportVulkanFunctions_Dynamic()
14312 {
14313 VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr &&
14314 "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass "
14315 "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. "
14316 "Other members can be null.");
14317
14318 #define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \
14319 if(m_VulkanFunctions.memberName == VMA_NULL) \
14320 m_VulkanFunctions.memberName = \
14321 (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString);
14322 #define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \
14323 if(m_VulkanFunctions.memberName == VMA_NULL) \
14324 m_VulkanFunctions.memberName = \
14325 (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString);
14326
14327 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
14328 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");
14329 VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");
14330 VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");
14331 VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");
14332 VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");
14333 VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");
14334 VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");
14335 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");
14336 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");
14337 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");
14338 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");
14339 VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");
14340 VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");
14341 VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");
14342 VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");
14343 VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");
14344
14345 #if VMA_VULKAN_VERSION >= 1001000
14346 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14347 {
14348 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");
14349 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");
14350 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");
14351 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");
14352 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");
14353 }
14354 #endif
14355
14356 #if VMA_DEDICATED_ALLOCATION
14357 if(m_UseKhrDedicatedAllocation)
14358 {
14359 VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");
14360 VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");
14361 }
14362 #endif
14363
14364 #if VMA_BIND_MEMORY2
14365 if(m_UseKhrBindMemory2)
14366 {
14367 VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");
14368 VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");
14369 }
14370 #endif // #if VMA_BIND_MEMORY2
14371
14372 #if VMA_MEMORY_BUDGET
14373 if(m_UseExtMemoryBudget)
14374 {
14375 VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");
14376 }
14377 #endif // #if VMA_MEMORY_BUDGET
14378
14379 #if VMA_VULKAN_VERSION >= 1003000
14380 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
14381 {
14382 VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements");
14383 VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements");
14384 }
14385 #endif
14386
14387 #undef VMA_FETCH_DEVICE_FUNC
14388 #undef VMA_FETCH_INSTANCE_FUNC
14389 }
14390
14391 #endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1
14392
ValidateVulkanFunctions()14393 void VmaAllocator_T::ValidateVulkanFunctions()
14394 {
14395 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
14396 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
14397 VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
14398 VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
14399 VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
14400 VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
14401 VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
14402 VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
14403 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
14404 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
14405 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
14406 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
14407 VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
14408 VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
14409 VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
14410 VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
14411 VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
14412
14413 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14414 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)
14415 {
14416 VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
14417 VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
14418 }
14419 #endif
14420
14421 #if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000
14422 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)
14423 {
14424 VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);
14425 VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);
14426 }
14427 #endif
14428
14429 #if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
14430 if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14431 {
14432 VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);
14433 }
14434 #endif
14435
14436 #if VMA_VULKAN_VERSION >= 1003000
14437 if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))
14438 {
14439 VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL);
14440 VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL);
14441 }
14442 #endif
14443 }
14444
CalcPreferredBlockSize(uint32_t memTypeIndex)14445 VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
14446 {
14447 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14448 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
14449 const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
14450 return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);
14451 }
14452
AllocateMemoryOfType(VmaPool pool,VkDeviceSize size,VkDeviceSize alignment,bool dedicatedPreferred,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VkFlags dedicatedBufferImageUsage,const VmaAllocationCreateInfo & createInfo,uint32_t memTypeIndex,VmaSuballocationType suballocType,VmaDedicatedAllocationList & dedicatedAllocations,VmaBlockVector & blockVector,size_t allocationCount,VmaAllocation * pAllocations)14453 VkResult VmaAllocator_T::AllocateMemoryOfType(
14454 VmaPool pool,
14455 VkDeviceSize size,
14456 VkDeviceSize alignment,
14457 bool dedicatedPreferred,
14458 VkBuffer dedicatedBuffer,
14459 VkImage dedicatedImage,
14460 VkFlags dedicatedBufferImageUsage,
14461 const VmaAllocationCreateInfo& createInfo,
14462 uint32_t memTypeIndex,
14463 VmaSuballocationType suballocType,
14464 VmaDedicatedAllocationList& dedicatedAllocations,
14465 VmaBlockVector& blockVector,
14466 size_t allocationCount,
14467 VmaAllocation* pAllocations)
14468 {
14469 VMA_ASSERT(pAllocations != VMA_NULL);
14470 VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);
14471
14472 VmaAllocationCreateInfo finalCreateInfo = createInfo;
14473 VkResult res = CalcMemTypeParams(
14474 finalCreateInfo,
14475 memTypeIndex,
14476 size,
14477 allocationCount);
14478 if(res != VK_SUCCESS)
14479 return res;
14480
14481 if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14482 {
14483 return AllocateDedicatedMemory(
14484 pool,
14485 size,
14486 suballocType,
14487 dedicatedAllocations,
14488 memTypeIndex,
14489 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14490 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14491 (finalCreateInfo.flags &
14492 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
14493 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
14494 finalCreateInfo.pUserData,
14495 finalCreateInfo.priority,
14496 dedicatedBuffer,
14497 dedicatedImage,
14498 dedicatedBufferImageUsage,
14499 allocationCount,
14500 pAllocations,
14501 blockVector.GetAllocationNextPtr());
14502 }
14503 else
14504 {
14505 const bool canAllocateDedicated =
14506 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
14507 (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize());
14508
14509 if(canAllocateDedicated)
14510 {
14511 // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
14512 if(size > blockVector.GetPreferredBlockSize() / 2)
14513 {
14514 dedicatedPreferred = true;
14515 }
14516 // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,
14517 // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above
14518 // 3/4 of the maximum allocation count.
14519 if(m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)
14520 {
14521 dedicatedPreferred = false;
14522 }
14523
14524 if(dedicatedPreferred)
14525 {
14526 res = AllocateDedicatedMemory(
14527 pool,
14528 size,
14529 suballocType,
14530 dedicatedAllocations,
14531 memTypeIndex,
14532 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14533 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14534 (finalCreateInfo.flags &
14535 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
14536 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
14537 finalCreateInfo.pUserData,
14538 finalCreateInfo.priority,
14539 dedicatedBuffer,
14540 dedicatedImage,
14541 dedicatedBufferImageUsage,
14542 allocationCount,
14543 pAllocations,
14544 blockVector.GetAllocationNextPtr());
14545 if(res == VK_SUCCESS)
14546 {
14547 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14548 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14549 return VK_SUCCESS;
14550 }
14551 }
14552 }
14553
14554 res = blockVector.Allocate(
14555 size,
14556 alignment,
14557 finalCreateInfo,
14558 suballocType,
14559 allocationCount,
14560 pAllocations);
14561 if(res == VK_SUCCESS)
14562 return VK_SUCCESS;
14563
14564 // Try dedicated memory.
14565 if(canAllocateDedicated && !dedicatedPreferred)
14566 {
14567 res = AllocateDedicatedMemory(
14568 pool,
14569 size,
14570 suballocType,
14571 dedicatedAllocations,
14572 memTypeIndex,
14573 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
14574 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
14575 (finalCreateInfo.flags &
14576 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,
14577 (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,
14578 finalCreateInfo.pUserData,
14579 finalCreateInfo.priority,
14580 dedicatedBuffer,
14581 dedicatedImage,
14582 dedicatedBufferImageUsage,
14583 allocationCount,
14584 pAllocations,
14585 blockVector.GetAllocationNextPtr());
14586 if(res == VK_SUCCESS)
14587 {
14588 // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
14589 VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
14590 return VK_SUCCESS;
14591 }
14592 }
14593 // Everything failed: Return error code.
14594 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14595 return res;
14596 }
14597 }
14598
AllocateDedicatedMemory(VmaPool pool,VkDeviceSize size,VmaSuballocationType suballocType,VmaDedicatedAllocationList & dedicatedAllocations,uint32_t memTypeIndex,bool map,bool isUserDataString,bool isMappingAllowed,bool canAliasMemory,void * pUserData,float priority,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VkFlags dedicatedBufferImageUsage,size_t allocationCount,VmaAllocation * pAllocations,const void * pNextChain)14599 VkResult VmaAllocator_T::AllocateDedicatedMemory(
14600 VmaPool pool,
14601 VkDeviceSize size,
14602 VmaSuballocationType suballocType,
14603 VmaDedicatedAllocationList& dedicatedAllocations,
14604 uint32_t memTypeIndex,
14605 bool map,
14606 bool isUserDataString,
14607 bool isMappingAllowed,
14608 bool canAliasMemory,
14609 void* pUserData,
14610 float priority,
14611 VkBuffer dedicatedBuffer,
14612 VkImage dedicatedImage,
14613 VkFlags dedicatedBufferImageUsage,
14614 size_t allocationCount,
14615 VmaAllocation* pAllocations,
14616 const void* pNextChain)
14617 {
14618 VMA_ASSERT(allocationCount > 0 && pAllocations);
14619
14620 VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
14621 allocInfo.memoryTypeIndex = memTypeIndex;
14622 allocInfo.allocationSize = size;
14623 allocInfo.pNext = pNextChain;
14624
14625 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14626 VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
14627 if(!canAliasMemory)
14628 {
14629 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14630 {
14631 if(dedicatedBuffer != VK_NULL_HANDLE)
14632 {
14633 VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
14634 dedicatedAllocInfo.buffer = dedicatedBuffer;
14635 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
14636 }
14637 else if(dedicatedImage != VK_NULL_HANDLE)
14638 {
14639 dedicatedAllocInfo.image = dedicatedImage;
14640 VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);
14641 }
14642 }
14643 }
14644 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14645
14646 #if VMA_BUFFER_DEVICE_ADDRESS
14647 VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };
14648 if(m_UseKhrBufferDeviceAddress)
14649 {
14650 bool canContainBufferWithDeviceAddress = true;
14651 if(dedicatedBuffer != VK_NULL_HANDLE)
14652 {
14653 canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == UINT32_MAX || // Usage flags unknown
14654 (dedicatedBufferImageUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;
14655 }
14656 else if(dedicatedImage != VK_NULL_HANDLE)
14657 {
14658 canContainBufferWithDeviceAddress = false;
14659 }
14660 if(canContainBufferWithDeviceAddress)
14661 {
14662 allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;
14663 VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);
14664 }
14665 }
14666 #endif // #if VMA_BUFFER_DEVICE_ADDRESS
14667
14668 #if VMA_MEMORY_PRIORITY
14669 VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
14670 if(m_UseExtMemoryPriority)
14671 {
14672 VMA_ASSERT(priority >= 0.f && priority <= 1.f);
14673 priorityInfo.priority = priority;
14674 VmaPnextChainPushFront(&allocInfo, &priorityInfo);
14675 }
14676 #endif // #if VMA_MEMORY_PRIORITY
14677
14678 #if VMA_EXTERNAL_MEMORY
14679 // Attach VkExportMemoryAllocateInfoKHR if necessary.
14680 VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
14681 exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);
14682 if(exportMemoryAllocInfo.handleTypes != 0)
14683 {
14684 VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);
14685 }
14686 #endif // #if VMA_EXTERNAL_MEMORY
14687
14688 size_t allocIndex;
14689 VkResult res = VK_SUCCESS;
14690 for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14691 {
14692 res = AllocateDedicatedMemoryPage(
14693 pool,
14694 size,
14695 suballocType,
14696 memTypeIndex,
14697 allocInfo,
14698 map,
14699 isUserDataString,
14700 isMappingAllowed,
14701 pUserData,
14702 pAllocations + allocIndex);
14703 if(res != VK_SUCCESS)
14704 {
14705 break;
14706 }
14707 }
14708
14709 if(res == VK_SUCCESS)
14710 {
14711 for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
14712 {
14713 dedicatedAllocations.Register(pAllocations[allocIndex]);
14714 }
14715 VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
14716 }
14717 else
14718 {
14719 // Free all already created allocations.
14720 while(allocIndex--)
14721 {
14722 VmaAllocation currAlloc = pAllocations[allocIndex];
14723 VkDeviceMemory hMemory = currAlloc->GetMemory();
14724
14725 /*
14726 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
14727 before vkFreeMemory.
14728
14729 if(currAlloc->GetMappedData() != VMA_NULL)
14730 {
14731 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
14732 }
14733 */
14734
14735 FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
14736 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());
14737 m_AllocationObjectAllocator.Free(currAlloc);
14738 }
14739
14740 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
14741 }
14742
14743 return res;
14744 }
14745
AllocateDedicatedMemoryPage(VmaPool pool,VkDeviceSize size,VmaSuballocationType suballocType,uint32_t memTypeIndex,const VkMemoryAllocateInfo & allocInfo,bool map,bool isUserDataString,bool isMappingAllowed,void * pUserData,VmaAllocation * pAllocation)14746 VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
14747 VmaPool pool,
14748 VkDeviceSize size,
14749 VmaSuballocationType suballocType,
14750 uint32_t memTypeIndex,
14751 const VkMemoryAllocateInfo& allocInfo,
14752 bool map,
14753 bool isUserDataString,
14754 bool isMappingAllowed,
14755 void* pUserData,
14756 VmaAllocation* pAllocation)
14757 {
14758 VkDeviceMemory hMemory = VK_NULL_HANDLE;
14759 VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
14760 if(res < 0)
14761 {
14762 VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
14763 return res;
14764 }
14765
14766 void* pMappedData = VMA_NULL;
14767 if(map)
14768 {
14769 res = (*m_VulkanFunctions.vkMapMemory)(
14770 m_hDevice,
14771 hMemory,
14772 0,
14773 VK_WHOLE_SIZE,
14774 0,
14775 &pMappedData);
14776 if(res < 0)
14777 {
14778 VMA_DEBUG_LOG(" vkMapMemory FAILED");
14779 FreeVulkanMemory(memTypeIndex, size, hMemory);
14780 return res;
14781 }
14782 }
14783
14784 *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);
14785 (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);
14786 if (isUserDataString)
14787 (*pAllocation)->SetName(this, (const char*)pUserData);
14788 else
14789 (*pAllocation)->SetUserData(this, pUserData);
14790 m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);
14791 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
14792 {
14793 FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
14794 }
14795
14796 return VK_SUCCESS;
14797 }
14798
GetBufferMemoryRequirements(VkBuffer hBuffer,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14799 void VmaAllocator_T::GetBufferMemoryRequirements(
14800 VkBuffer hBuffer,
14801 VkMemoryRequirements& memReq,
14802 bool& requiresDedicatedAllocation,
14803 bool& prefersDedicatedAllocation) const
14804 {
14805 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14806 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14807 {
14808 VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
14809 memReqInfo.buffer = hBuffer;
14810
14811 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14812
14813 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14814 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
14815
14816 (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14817
14818 memReq = memReq2.memoryRequirements;
14819 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14820 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14821 }
14822 else
14823 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14824 {
14825 (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
14826 requiresDedicatedAllocation = false;
14827 prefersDedicatedAllocation = false;
14828 }
14829 }
14830
GetImageMemoryRequirements(VkImage hImage,VkMemoryRequirements & memReq,bool & requiresDedicatedAllocation,bool & prefersDedicatedAllocation)14831 void VmaAllocator_T::GetImageMemoryRequirements(
14832 VkImage hImage,
14833 VkMemoryRequirements& memReq,
14834 bool& requiresDedicatedAllocation,
14835 bool& prefersDedicatedAllocation) const
14836 {
14837 #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14838 if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))
14839 {
14840 VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
14841 memReqInfo.image = hImage;
14842
14843 VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
14844
14845 VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
14846 VmaPnextChainPushFront(&memReq2, &memDedicatedReq);
14847
14848 (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
14849
14850 memReq = memReq2.memoryRequirements;
14851 requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
14852 prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
14853 }
14854 else
14855 #endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000
14856 {
14857 (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
14858 requiresDedicatedAllocation = false;
14859 prefersDedicatedAllocation = false;
14860 }
14861 }
14862
FindMemoryTypeIndex(uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkFlags bufImgUsage,uint32_t * pMemoryTypeIndex)14863 VkResult VmaAllocator_T::FindMemoryTypeIndex(
14864 uint32_t memoryTypeBits,
14865 const VmaAllocationCreateInfo* pAllocationCreateInfo,
14866 VkFlags bufImgUsage,
14867 uint32_t* pMemoryTypeIndex) const
14868 {
14869 memoryTypeBits &= GetGlobalMemoryTypeBits();
14870
14871 if(pAllocationCreateInfo->memoryTypeBits != 0)
14872 {
14873 memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
14874 }
14875
14876 VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0;
14877 if(!FindMemoryPreferences(
14878 IsIntegratedGpu(),
14879 *pAllocationCreateInfo,
14880 bufImgUsage,
14881 requiredFlags, preferredFlags, notPreferredFlags))
14882 {
14883 return VK_ERROR_FEATURE_NOT_PRESENT;
14884 }
14885
14886 *pMemoryTypeIndex = UINT32_MAX;
14887 uint32_t minCost = UINT32_MAX;
14888 for(uint32_t memTypeIndex = 0, memTypeBit = 1;
14889 memTypeIndex < GetMemoryTypeCount();
14890 ++memTypeIndex, memTypeBit <<= 1)
14891 {
14892 // This memory type is acceptable according to memoryTypeBits bitmask.
14893 if((memTypeBit & memoryTypeBits) != 0)
14894 {
14895 const VkMemoryPropertyFlags currFlags =
14896 m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
14897 // This memory type contains requiredFlags.
14898 if((requiredFlags & ~currFlags) == 0)
14899 {
14900 // Calculate cost as number of bits from preferredFlags not present in this memory type.
14901 uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) +
14902 VMA_COUNT_BITS_SET(currFlags & notPreferredFlags);
14903 // Remember memory type with lowest cost.
14904 if(currCost < minCost)
14905 {
14906 *pMemoryTypeIndex = memTypeIndex;
14907 if(currCost == 0)
14908 {
14909 return VK_SUCCESS;
14910 }
14911 minCost = currCost;
14912 }
14913 }
14914 }
14915 }
14916 return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
14917 }
14918
CalcMemTypeParams(VmaAllocationCreateInfo & inoutCreateInfo,uint32_t memTypeIndex,VkDeviceSize size,size_t allocationCount)14919 VkResult VmaAllocator_T::CalcMemTypeParams(
14920 VmaAllocationCreateInfo& inoutCreateInfo,
14921 uint32_t memTypeIndex,
14922 VkDeviceSize size,
14923 size_t allocationCount)
14924 {
14925 // If memory type is not HOST_VISIBLE, disable MAPPED.
14926 if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
14927 (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
14928 {
14929 inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
14930 }
14931
14932 if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14933 (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0)
14934 {
14935 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
14936 VmaBudget heapBudget = {};
14937 GetHeapBudgets(&heapBudget, heapIndex, 1);
14938 if(heapBudget.usage + size * allocationCount > heapBudget.budget)
14939 {
14940 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
14941 }
14942 }
14943 return VK_SUCCESS;
14944 }
14945
CalcAllocationParams(VmaAllocationCreateInfo & inoutCreateInfo,bool dedicatedRequired,bool dedicatedPreferred)14946 VkResult VmaAllocator_T::CalcAllocationParams(
14947 VmaAllocationCreateInfo& inoutCreateInfo,
14948 bool dedicatedRequired,
14949 bool dedicatedPreferred)
14950 {
14951 VMA_ASSERT((inoutCreateInfo.flags &
14952 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) !=
14953 (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) &&
14954 "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect.");
14955 VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 ||
14956 (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) &&
14957 "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
14958 if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
14959 {
14960 if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0)
14961 {
14962 VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 &&
14963 "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");
14964 }
14965 }
14966
14967 // If memory is lazily allocated, it should be always dedicated.
14968 if(dedicatedRequired ||
14969 inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)
14970 {
14971 inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14972 }
14973
14974 if(inoutCreateInfo.pool != VK_NULL_HANDLE)
14975 {
14976 if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&
14977 (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
14978 {
14979 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
14980 return VK_ERROR_FEATURE_NOT_PRESENT;
14981 }
14982 inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();
14983 }
14984
14985 if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
14986 (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14987 {
14988 VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
14989 return VK_ERROR_FEATURE_NOT_PRESENT;
14990 }
14991
14992 if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY &&
14993 (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
14994 {
14995 inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
14996 }
14997
14998 // Non-auto USAGE values imply HOST_ACCESS flags.
14999 // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools.
15000 // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*.
15001 // Otherwise they just protect from assert on mapping.
15002 if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO &&
15003 inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE &&
15004 inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST)
15005 {
15006 if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0)
15007 {
15008 inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
15009 }
15010 }
15011
15012 return VK_SUCCESS;
15013 }
15014
AllocateMemory(const VkMemoryRequirements & vkMemReq,bool requiresDedicatedAllocation,bool prefersDedicatedAllocation,VkBuffer dedicatedBuffer,VkImage dedicatedImage,VkFlags dedicatedBufferImageUsage,const VmaAllocationCreateInfo & createInfo,VmaSuballocationType suballocType,size_t allocationCount,VmaAllocation * pAllocations)15015 VkResult VmaAllocator_T::AllocateMemory(
15016 const VkMemoryRequirements& vkMemReq,
15017 bool requiresDedicatedAllocation,
15018 bool prefersDedicatedAllocation,
15019 VkBuffer dedicatedBuffer,
15020 VkImage dedicatedImage,
15021 VkFlags dedicatedBufferImageUsage,
15022 const VmaAllocationCreateInfo& createInfo,
15023 VmaSuballocationType suballocType,
15024 size_t allocationCount,
15025 VmaAllocation* pAllocations)
15026 {
15027 memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
15028
15029 VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
15030
15031 if(vkMemReq.size == 0)
15032 {
15033 return VK_ERROR_INITIALIZATION_FAILED;
15034 }
15035
15036 VmaAllocationCreateInfo createInfoFinal = createInfo;
15037 VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation);
15038 if(res != VK_SUCCESS)
15039 return res;
15040
15041 if(createInfoFinal.pool != VK_NULL_HANDLE)
15042 {
15043 VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
15044 return AllocateMemoryOfType(
15045 createInfoFinal.pool,
15046 vkMemReq.size,
15047 vkMemReq.alignment,
15048 prefersDedicatedAllocation,
15049 dedicatedBuffer,
15050 dedicatedImage,
15051 dedicatedBufferImageUsage,
15052 createInfoFinal,
15053 blockVector.GetMemoryTypeIndex(),
15054 suballocType,
15055 createInfoFinal.pool->m_DedicatedAllocations,
15056 blockVector,
15057 allocationCount,
15058 pAllocations);
15059 }
15060 else
15061 {
15062 // Bit mask of memory Vulkan types acceptable for this allocation.
15063 uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
15064 uint32_t memTypeIndex = UINT32_MAX;
15065 res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
15066 // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
15067 if(res != VK_SUCCESS)
15068 return res;
15069 do
15070 {
15071 VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
15072 VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
15073 res = AllocateMemoryOfType(
15074 VK_NULL_HANDLE,
15075 vkMemReq.size,
15076 vkMemReq.alignment,
15077 requiresDedicatedAllocation || prefersDedicatedAllocation,
15078 dedicatedBuffer,
15079 dedicatedImage,
15080 dedicatedBufferImageUsage,
15081 createInfoFinal,
15082 memTypeIndex,
15083 suballocType,
15084 m_DedicatedAllocations[memTypeIndex],
15085 *blockVector,
15086 allocationCount,
15087 pAllocations);
15088 // Allocation succeeded
15089 if(res == VK_SUCCESS)
15090 return VK_SUCCESS;
15091
15092 // Remove old memTypeIndex from list of possibilities.
15093 memoryTypeBits &= ~(1u << memTypeIndex);
15094 // Find alternative memTypeIndex.
15095 res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);
15096 } while(res == VK_SUCCESS);
15097
15098 // No other matching memory type index could be found.
15099 // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
15100 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15101 }
15102 }
15103
FreeMemory(size_t allocationCount,const VmaAllocation * pAllocations)15104 void VmaAllocator_T::FreeMemory(
15105 size_t allocationCount,
15106 const VmaAllocation* pAllocations)
15107 {
15108 VMA_ASSERT(pAllocations);
15109
15110 for(size_t allocIndex = allocationCount; allocIndex--; )
15111 {
15112 VmaAllocation allocation = pAllocations[allocIndex];
15113
15114 if(allocation != VK_NULL_HANDLE)
15115 {
15116 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
15117 {
15118 FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
15119 }
15120
15121 allocation->FreeName(this);
15122
15123 switch(allocation->GetType())
15124 {
15125 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15126 {
15127 VmaBlockVector* pBlockVector = VMA_NULL;
15128 VmaPool hPool = allocation->GetParentPool();
15129 if(hPool != VK_NULL_HANDLE)
15130 {
15131 pBlockVector = &hPool->m_BlockVector;
15132 }
15133 else
15134 {
15135 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15136 pBlockVector = m_pBlockVectors[memTypeIndex];
15137 VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
15138 }
15139 pBlockVector->Free(allocation);
15140 }
15141 break;
15142 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15143 FreeDedicatedMemory(allocation);
15144 break;
15145 default:
15146 VMA_ASSERT(0);
15147 }
15148 }
15149 }
15150 }
15151
CalculateStatistics(VmaTotalStatistics * pStats)15152 void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats)
15153 {
15154 // Initialize.
15155 VmaClearDetailedStatistics(pStats->total);
15156 for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
15157 VmaClearDetailedStatistics(pStats->memoryType[i]);
15158 for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
15159 VmaClearDetailedStatistics(pStats->memoryHeap[i]);
15160
15161 // Process default pools.
15162 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15163 {
15164 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15165 if (pBlockVector != VMA_NULL)
15166 pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15167 }
15168
15169 // Process custom pools.
15170 {
15171 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15172 for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15173 {
15174 VmaBlockVector& blockVector = pool->m_BlockVector;
15175 const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
15176 blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15177 pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15178 }
15179 }
15180
15181 // Process dedicated allocations.
15182 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15183 {
15184 m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]);
15185 }
15186
15187 // Sum from memory types to memory heaps.
15188 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15189 {
15190 const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;
15191 VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]);
15192 }
15193
15194 // Sum from memory heaps to total.
15195 for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex)
15196 VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]);
15197
15198 VMA_ASSERT(pStats->total.statistics.allocationCount == 0 ||
15199 pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin);
15200 VMA_ASSERT(pStats->total.unusedRangeCount == 0 ||
15201 pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin);
15202 }
15203
FreeEmptyBlock()15204 void VmaAllocator_T::FreeEmptyBlock()
15205 {
15206 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) {
15207 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15208 VMA_ASSERT(pBlockVector);
15209 pBlockVector->FreeEmptyBlock();
15210 }
15211 }
15212
GetHeapBudgets(VmaBudget * outBudgets,uint32_t firstHeap,uint32_t heapCount)15213 void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount)
15214 {
15215 #if VMA_MEMORY_BUDGET
15216 if(m_UseExtMemoryBudget)
15217 {
15218 if(m_Budget.m_OperationsSinceBudgetFetch < 30)
15219 {
15220 VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);
15221 for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
15222 {
15223 const uint32_t heapIndex = firstHeap + i;
15224
15225 outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
15226 outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
15227 outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
15228 outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15229
15230 if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])
15231 {
15232 outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] +
15233 outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
15234 }
15235 else
15236 {
15237 outBudgets->usage = 0;
15238 }
15239
15240 // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.
15241 outBudgets->budget = VMA_MIN(
15242 m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);
15243 }
15244 }
15245 else
15246 {
15247 UpdateVulkanBudget(); // Outside of mutex lock
15248 GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion
15249 }
15250 }
15251 else
15252 #endif
15253 {
15254 for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)
15255 {
15256 const uint32_t heapIndex = firstHeap + i;
15257
15258 outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];
15259 outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];
15260 outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];
15261 outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];
15262
15263 outBudgets->usage = outBudgets->statistics.blockBytes;
15264 outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
15265 }
15266 }
15267 }
15268
GetAllocationInfo(VmaAllocation hAllocation,VmaAllocationInfo * pAllocationInfo)15269 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
15270 {
15271 pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
15272 pAllocationInfo->deviceMemory = hAllocation->GetMemory();
15273 pAllocationInfo->offset = hAllocation->GetOffset();
15274 pAllocationInfo->size = hAllocation->GetSize();
15275 pAllocationInfo->pMappedData = hAllocation->GetMappedData();
15276 pAllocationInfo->pUserData = hAllocation->GetUserData();
15277 pAllocationInfo->pName = hAllocation->GetName();
15278 }
15279
CreatePool(const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)15280 VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
15281 {
15282 VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
15283
15284 VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
15285
15286 // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.
15287 if(pCreateInfo->pMemoryAllocateNext)
15288 {
15289 VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);
15290 }
15291
15292 if(newCreateInfo.maxBlockCount == 0)
15293 {
15294 newCreateInfo.maxBlockCount = SIZE_MAX;
15295 }
15296 if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
15297 {
15298 return VK_ERROR_INITIALIZATION_FAILED;
15299 }
15300 // Memory type index out of range or forbidden.
15301 if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
15302 ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
15303 {
15304 return VK_ERROR_FEATURE_NOT_PRESENT;
15305 }
15306 if(newCreateInfo.minAllocationAlignment > 0)
15307 {
15308 VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
15309 }
15310
15311 const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
15312
15313 *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
15314
15315 VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
15316 if(res != VK_SUCCESS)
15317 {
15318 vma_delete(this, *pPool);
15319 *pPool = VMA_NULL;
15320 return res;
15321 }
15322
15323 // Add to m_Pools.
15324 {
15325 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15326 (*pPool)->SetId(m_NextPoolId++);
15327 m_Pools.PushBack(*pPool);
15328 }
15329
15330 return VK_SUCCESS;
15331 }
15332
DestroyPool(VmaPool pool)15333 void VmaAllocator_T::DestroyPool(VmaPool pool)
15334 {
15335 // Remove from m_Pools.
15336 {
15337 VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
15338 m_Pools.Remove(pool);
15339 }
15340
15341 vma_delete(this, pool);
15342 }
15343
GetPoolStatistics(VmaPool pool,VmaStatistics * pPoolStats)15344 void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats)
15345 {
15346 VmaClearStatistics(*pPoolStats);
15347 pool->m_BlockVector.AddStatistics(*pPoolStats);
15348 pool->m_DedicatedAllocations.AddStatistics(*pPoolStats);
15349 }
15350
CalculatePoolStatistics(VmaPool pool,VmaDetailedStatistics * pPoolStats)15351 void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats)
15352 {
15353 VmaClearDetailedStatistics(*pPoolStats);
15354 pool->m_BlockVector.AddDetailedStatistics(*pPoolStats);
15355 pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats);
15356 }
15357
SetCurrentFrameIndex(uint32_t frameIndex)15358 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
15359 {
15360 m_CurrentFrameIndex.store(frameIndex);
15361
15362 #if VMA_MEMORY_BUDGET
15363 if(m_UseExtMemoryBudget)
15364 {
15365 UpdateVulkanBudget();
15366 }
15367 #endif // #if VMA_MEMORY_BUDGET
15368 }
15369
CheckPoolCorruption(VmaPool hPool)15370 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
15371 {
15372 return hPool->m_BlockVector.CheckCorruption();
15373 }
15374
CheckCorruption(uint32_t memoryTypeBits)15375 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
15376 {
15377 VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
15378
15379 // Process default pools.
15380 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15381 {
15382 VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
15383 if(pBlockVector != VMA_NULL)
15384 {
15385 VkResult localRes = pBlockVector->CheckCorruption();
15386 switch(localRes)
15387 {
15388 case VK_ERROR_FEATURE_NOT_PRESENT:
15389 break;
15390 case VK_SUCCESS:
15391 finalRes = VK_SUCCESS;
15392 break;
15393 default:
15394 return localRes;
15395 }
15396 }
15397 }
15398
15399 // Process custom pools.
15400 {
15401 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15402 for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15403 {
15404 if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
15405 {
15406 VkResult localRes = pool->m_BlockVector.CheckCorruption();
15407 switch(localRes)
15408 {
15409 case VK_ERROR_FEATURE_NOT_PRESENT:
15410 break;
15411 case VK_SUCCESS:
15412 finalRes = VK_SUCCESS;
15413 break;
15414 default:
15415 return localRes;
15416 }
15417 }
15418 }
15419 }
15420
15421 return finalRes;
15422 }
15423
AllocateVulkanMemory(const VkMemoryAllocateInfo * pAllocateInfo,VkDeviceMemory * pMemory)15424 VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
15425 {
15426 AtomicTransactionalIncrement<uint32_t> deviceMemoryCountIncrement;
15427 const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);
15428 #if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT
15429 if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)
15430 {
15431 return VK_ERROR_TOO_MANY_OBJECTS;
15432 }
15433 #endif
15434
15435 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
15436
15437 // HeapSizeLimit is in effect for this heap.
15438 if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)
15439 {
15440 const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
15441 VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];
15442 for(;;)
15443 {
15444 const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;
15445 if(blockBytesAfterAllocation > heapSize)
15446 {
15447 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
15448 }
15449 if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))
15450 {
15451 break;
15452 }
15453 }
15454 }
15455 else
15456 {
15457 m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;
15458 }
15459 ++m_Budget.m_BlockCount[heapIndex];
15460
15461 // VULKAN CALL vkAllocateMemory.
15462 VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
15463
15464 if(res == VK_SUCCESS)
15465 {
15466 #if VMA_MEMORY_BUDGET
15467 ++m_Budget.m_OperationsSinceBudgetFetch;
15468 #endif
15469
15470 // Informative callback.
15471 if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
15472 {
15473 (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);
15474 }
15475
15476 deviceMemoryCountIncrement.Commit();
15477 }
15478 else
15479 {
15480 --m_Budget.m_BlockCount[heapIndex];
15481 m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;
15482 }
15483
15484 return res;
15485 }
15486
FreeVulkanMemory(uint32_t memoryType,VkDeviceSize size,VkDeviceMemory hMemory)15487 void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
15488 {
15489 // Informative callback.
15490 if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
15491 {
15492 (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);
15493 }
15494
15495 // VULKAN CALL vkFreeMemory.
15496 (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
15497
15498 const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
15499 --m_Budget.m_BlockCount[heapIndex];
15500 m_Budget.m_BlockBytes[heapIndex] -= size;
15501
15502 --m_DeviceMemoryCount;
15503 }
15504
BindVulkanBuffer(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkBuffer buffer,const void * pNext)15505 VkResult VmaAllocator_T::BindVulkanBuffer(
15506 VkDeviceMemory memory,
15507 VkDeviceSize memoryOffset,
15508 VkBuffer buffer,
15509 const void* pNext)
15510 {
15511 if(pNext != VMA_NULL)
15512 {
15513 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
15514 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
15515 m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)
15516 {
15517 VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };
15518 bindBufferMemoryInfo.pNext = pNext;
15519 bindBufferMemoryInfo.buffer = buffer;
15520 bindBufferMemoryInfo.memory = memory;
15521 bindBufferMemoryInfo.memoryOffset = memoryOffset;
15522 return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
15523 }
15524 else
15525 #endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
15526 {
15527 return VK_ERROR_EXTENSION_NOT_PRESENT;
15528 }
15529 }
15530 else
15531 {
15532 return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);
15533 }
15534 }
15535
BindVulkanImage(VkDeviceMemory memory,VkDeviceSize memoryOffset,VkImage image,const void * pNext)15536 VkResult VmaAllocator_T::BindVulkanImage(
15537 VkDeviceMemory memory,
15538 VkDeviceSize memoryOffset,
15539 VkImage image,
15540 const void* pNext)
15541 {
15542 if(pNext != VMA_NULL)
15543 {
15544 #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2
15545 if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&
15546 m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)
15547 {
15548 VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };
15549 bindBufferMemoryInfo.pNext = pNext;
15550 bindBufferMemoryInfo.image = image;
15551 bindBufferMemoryInfo.memory = memory;
15552 bindBufferMemoryInfo.memoryOffset = memoryOffset;
15553 return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);
15554 }
15555 else
15556 #endif // #if VMA_BIND_MEMORY2
15557 {
15558 return VK_ERROR_EXTENSION_NOT_PRESENT;
15559 }
15560 }
15561 else
15562 {
15563 return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);
15564 }
15565 }
15566
Map(VmaAllocation hAllocation,void ** ppData)15567 VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
15568 {
15569 switch(hAllocation->GetType())
15570 {
15571 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15572 {
15573 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15574 char *pBytes = VMA_NULL;
15575 VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
15576 if(res == VK_SUCCESS)
15577 {
15578 *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
15579 hAllocation->BlockAllocMap();
15580 }
15581 return res;
15582 }
15583 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15584 return hAllocation->DedicatedAllocMap(this, ppData);
15585 default:
15586 VMA_ASSERT(0);
15587 return VK_ERROR_MEMORY_MAP_FAILED;
15588 }
15589 }
15590
Unmap(VmaAllocation hAllocation)15591 void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
15592 {
15593 switch(hAllocation->GetType())
15594 {
15595 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15596 {
15597 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15598 hAllocation->BlockAllocUnmap();
15599 pBlock->Unmap(this, 1);
15600 }
15601 break;
15602 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15603 hAllocation->DedicatedAllocUnmap(this);
15604 break;
15605 default:
15606 VMA_ASSERT(0);
15607 }
15608 }
15609
BindBufferMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkBuffer hBuffer,const void * pNext)15610 VkResult VmaAllocator_T::BindBufferMemory(
15611 VmaAllocation hAllocation,
15612 VkDeviceSize allocationLocalOffset,
15613 VkBuffer hBuffer,
15614 const void* pNext)
15615 {
15616 VkResult res = VK_SUCCESS;
15617 switch(hAllocation->GetType())
15618 {
15619 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15620 res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);
15621 break;
15622 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15623 {
15624 VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
15625 VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block.");
15626 res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);
15627 break;
15628 }
15629 default:
15630 VMA_ASSERT(0);
15631 }
15632 return res;
15633 }
15634
BindImageMemory(VmaAllocation hAllocation,VkDeviceSize allocationLocalOffset,VkImage hImage,const void * pNext)15635 VkResult VmaAllocator_T::BindImageMemory(
15636 VmaAllocation hAllocation,
15637 VkDeviceSize allocationLocalOffset,
15638 VkImage hImage,
15639 const void* pNext)
15640 {
15641 VkResult res = VK_SUCCESS;
15642 switch(hAllocation->GetType())
15643 {
15644 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15645 res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);
15646 break;
15647 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15648 {
15649 VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
15650 VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block.");
15651 res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);
15652 break;
15653 }
15654 default:
15655 VMA_ASSERT(0);
15656 }
15657 return res;
15658 }
15659
FlushOrInvalidateAllocation(VmaAllocation hAllocation,VkDeviceSize offset,VkDeviceSize size,VMA_CACHE_OPERATION op)15660 VkResult VmaAllocator_T::FlushOrInvalidateAllocation(
15661 VmaAllocation hAllocation,
15662 VkDeviceSize offset, VkDeviceSize size,
15663 VMA_CACHE_OPERATION op)
15664 {
15665 VkResult res = VK_SUCCESS;
15666
15667 VkMappedMemoryRange memRange = {};
15668 if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))
15669 {
15670 switch(op)
15671 {
15672 case VMA_CACHE_FLUSH:
15673 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
15674 break;
15675 case VMA_CACHE_INVALIDATE:
15676 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
15677 break;
15678 default:
15679 VMA_ASSERT(0);
15680 }
15681 }
15682 // else: Just ignore this call.
15683 return res;
15684 }
15685
FlushOrInvalidateAllocations(uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes,VMA_CACHE_OPERATION op)15686 VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
15687 uint32_t allocationCount,
15688 const VmaAllocation* allocations,
15689 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
15690 VMA_CACHE_OPERATION op)
15691 {
15692 typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;
15693 typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;
15694 RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));
15695
15696 for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
15697 {
15698 const VmaAllocation alloc = allocations[allocIndex];
15699 const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;
15700 const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;
15701 VkMappedMemoryRange newRange;
15702 if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))
15703 {
15704 ranges.push_back(newRange);
15705 }
15706 }
15707
15708 VkResult res = VK_SUCCESS;
15709 if(!ranges.empty())
15710 {
15711 switch(op)
15712 {
15713 case VMA_CACHE_FLUSH:
15714 res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
15715 break;
15716 case VMA_CACHE_INVALIDATE:
15717 res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());
15718 break;
15719 default:
15720 VMA_ASSERT(0);
15721 }
15722 }
15723 // else: Just ignore this call.
15724 return res;
15725 }
15726
FreeDedicatedMemory(const VmaAllocation allocation)15727 void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
15728 {
15729 VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
15730
15731 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15732 VmaPool parentPool = allocation->GetParentPool();
15733 if(parentPool == VK_NULL_HANDLE)
15734 {
15735 // Default pool
15736 m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
15737 }
15738 else
15739 {
15740 // Custom pool
15741 parentPool->m_DedicatedAllocations.Unregister(allocation);
15742 }
15743
15744 VkDeviceMemory hMemory = allocation->GetMemory();
15745
15746 /*
15747 There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
15748 before vkFreeMemory.
15749
15750 if(allocation->GetMappedData() != VMA_NULL)
15751 {
15752 (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
15753 }
15754 */
15755
15756 FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
15757
15758 m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());
15759 m_AllocationObjectAllocator.Free(allocation);
15760
15761 VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
15762 }
15763
CalculateGpuDefragmentationMemoryTypeBits()15764 uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const
15765 {
15766 VkBufferCreateInfo dummyBufCreateInfo;
15767 VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);
15768
15769 uint32_t memoryTypeBits = 0;
15770
15771 // Create buffer.
15772 VkBuffer buf = VK_NULL_HANDLE;
15773 VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(
15774 m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);
15775 if(res == VK_SUCCESS)
15776 {
15777 // Query for supported memory types.
15778 VkMemoryRequirements memReq;
15779 (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);
15780 memoryTypeBits = memReq.memoryTypeBits;
15781
15782 // Destroy buffer.
15783 (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());
15784 }
15785
15786 return memoryTypeBits;
15787 }
15788
CalculateGlobalMemoryTypeBits()15789 uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const
15790 {
15791 // Make sure memory information is already fetched.
15792 VMA_ASSERT(GetMemoryTypeCount() > 0);
15793
15794 uint32_t memoryTypeBits = UINT32_MAX;
15795
15796 if(!m_UseAmdDeviceCoherentMemory)
15797 {
15798 // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.
15799 for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15800 {
15801 if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
15802 {
15803 memoryTypeBits &= ~(1u << memTypeIndex);
15804 }
15805 }
15806 }
15807
15808 return memoryTypeBits;
15809 }
15810
GetFlushOrInvalidateRange(VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size,VkMappedMemoryRange & outRange)15811 bool VmaAllocator_T::GetFlushOrInvalidateRange(
15812 VmaAllocation allocation,
15813 VkDeviceSize offset, VkDeviceSize size,
15814 VkMappedMemoryRange& outRange) const
15815 {
15816 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
15817 if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
15818 {
15819 const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
15820 const VkDeviceSize allocationSize = allocation->GetSize();
15821 VMA_ASSERT(offset <= allocationSize);
15822
15823 outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
15824 outRange.pNext = VMA_NULL;
15825 outRange.memory = allocation->GetMemory();
15826
15827 switch(allocation->GetType())
15828 {
15829 case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
15830 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15831 if(size == VK_WHOLE_SIZE)
15832 {
15833 outRange.size = allocationSize - outRange.offset;
15834 }
15835 else
15836 {
15837 VMA_ASSERT(offset + size <= allocationSize);
15838 outRange.size = VMA_MIN(
15839 VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),
15840 allocationSize - outRange.offset);
15841 }
15842 break;
15843 case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
15844 {
15845 // 1. Still within this allocation.
15846 outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
15847 if(size == VK_WHOLE_SIZE)
15848 {
15849 size = allocationSize - offset;
15850 }
15851 else
15852 {
15853 VMA_ASSERT(offset + size <= allocationSize);
15854 }
15855 outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);
15856
15857 // 2. Adjust to whole block.
15858 const VkDeviceSize allocationOffset = allocation->GetOffset();
15859 VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
15860 const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();
15861 outRange.offset += allocationOffset;
15862 outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);
15863
15864 break;
15865 }
15866 default:
15867 VMA_ASSERT(0);
15868 }
15869 return true;
15870 }
15871 return false;
15872 }
15873
15874 #if VMA_MEMORY_BUDGET
UpdateVulkanBudget()15875 void VmaAllocator_T::UpdateVulkanBudget()
15876 {
15877 VMA_ASSERT(m_UseExtMemoryBudget);
15878
15879 VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };
15880
15881 VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
15882 VmaPnextChainPushFront(&memProps, &budgetProps);
15883
15884 GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);
15885
15886 {
15887 VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);
15888
15889 for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
15890 {
15891 m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];
15892 m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];
15893 m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();
15894
15895 // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.
15896 if(m_Budget.m_VulkanBudget[heapIndex] == 0)
15897 {
15898 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.
15899 }
15900 else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)
15901 {
15902 m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;
15903 }
15904 if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)
15905 {
15906 m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];
15907 }
15908 }
15909 m_Budget.m_OperationsSinceBudgetFetch = 0;
15910 }
15911 }
15912 #endif // VMA_MEMORY_BUDGET
15913
FillAllocation(const VmaAllocation hAllocation,uint8_t pattern)15914 void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
15915 {
15916 if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
15917 hAllocation->IsMappingAllowed() &&
15918 (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
15919 {
15920 void* pData = VMA_NULL;
15921 VkResult res = Map(hAllocation, &pData);
15922 if(res == VK_SUCCESS)
15923 {
15924 memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
15925 FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
15926 Unmap(hAllocation);
15927 }
15928 else
15929 {
15930 VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
15931 }
15932 }
15933 }
15934
GetGpuDefragmentationMemoryTypeBits()15935 uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
15936 {
15937 uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();
15938 if(memoryTypeBits == UINT32_MAX)
15939 {
15940 memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();
15941 m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);
15942 }
15943 return memoryTypeBits;
15944 }
15945
15946 #if VMA_STATS_STRING_ENABLED
PrintDetailedMap(VmaJsonWriter & json)15947 void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
15948 {
15949 json.WriteString("DefaultPools");
15950 json.BeginObject();
15951 {
15952 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15953 {
15954 VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];
15955 VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
15956 if (pBlockVector != VMA_NULL)
15957 {
15958 json.BeginString("Type ");
15959 json.ContinueString(memTypeIndex);
15960 json.EndString();
15961 json.BeginObject();
15962 {
15963 json.WriteString("PreferredBlockSize");
15964 json.WriteNumber(pBlockVector->GetPreferredBlockSize());
15965
15966 json.WriteString("Blocks");
15967 pBlockVector->PrintDetailedMap(json);
15968
15969 json.WriteString("DedicatedAllocations");
15970 dedicatedAllocList.BuildStatsString(json);
15971 }
15972 json.EndObject();
15973 }
15974 }
15975 }
15976 json.EndObject();
15977
15978 json.WriteString("CustomPools");
15979 json.BeginObject();
15980 {
15981 VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
15982 if (!m_Pools.IsEmpty())
15983 {
15984 for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
15985 {
15986 bool displayType = true;
15987 size_t index = 0;
15988 for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
15989 {
15990 VmaBlockVector& blockVector = pool->m_BlockVector;
15991 if (blockVector.GetMemoryTypeIndex() == memTypeIndex)
15992 {
15993 if (displayType)
15994 {
15995 json.BeginString("Type ");
15996 json.ContinueString(memTypeIndex);
15997 json.EndString();
15998 json.BeginArray();
15999 displayType = false;
16000 }
16001
16002 json.BeginObject();
16003 {
16004 json.WriteString("Name");
16005 json.BeginString();
16006 json.ContinueString_Size(index++);
16007 if (pool->GetName())
16008 {
16009 json.ContinueString(" - ");
16010 json.ContinueString(pool->GetName());
16011 }
16012 json.EndString();
16013
16014 json.WriteString("PreferredBlockSize");
16015 json.WriteNumber(blockVector.GetPreferredBlockSize());
16016
16017 json.WriteString("Blocks");
16018 blockVector.PrintDetailedMap(json);
16019
16020 json.WriteString("DedicatedAllocations");
16021 pool->m_DedicatedAllocations.BuildStatsString(json);
16022 }
16023 json.EndObject();
16024 }
16025 }
16026
16027 if (!displayType)
16028 json.EndArray();
16029 }
16030 }
16031 }
16032 json.EndObject();
16033 }
16034 #endif // VMA_STATS_STRING_ENABLED
16035 #endif // _VMA_ALLOCATOR_T_FUNCTIONS
16036
16037
16038 #ifndef _VMA_PUBLIC_INTERFACE
vmaCreateAllocator(const VmaAllocatorCreateInfo * pCreateInfo,VmaAllocator * pAllocator)16039 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(
16040 const VmaAllocatorCreateInfo* pCreateInfo,
16041 VmaAllocator* pAllocator)
16042 {
16043 VMA_ASSERT(pCreateInfo && pAllocator);
16044 VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||
16045 (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));
16046 VMA_DEBUG_LOG("vmaCreateAllocator");
16047 *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
16048 VkResult result = (*pAllocator)->Init(pCreateInfo);
16049 if(result < 0)
16050 {
16051 vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);
16052 *pAllocator = VK_NULL_HANDLE;
16053 }
16054 return result;
16055 }
16056
vmaDestroyAllocator(VmaAllocator allocator)16057 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(
16058 VmaAllocator allocator)
16059 {
16060 if(allocator != VK_NULL_HANDLE)
16061 {
16062 VMA_DEBUG_LOG("vmaDestroyAllocator");
16063 VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
16064 vma_delete(&allocationCallbacks, allocator);
16065 }
16066 }
16067
vmaGetAllocatorInfo(VmaAllocator allocator,VmaAllocatorInfo * pAllocatorInfo)16068 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)
16069 {
16070 VMA_ASSERT(allocator && pAllocatorInfo);
16071 pAllocatorInfo->instance = allocator->m_hInstance;
16072 pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();
16073 pAllocatorInfo->device = allocator->m_hDevice;
16074 }
16075
vmaGetPhysicalDeviceProperties(VmaAllocator allocator,const VkPhysicalDeviceProperties ** ppPhysicalDeviceProperties)16076 VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(
16077 VmaAllocator allocator,
16078 const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
16079 {
16080 VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
16081 *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
16082 }
16083
vmaGetMemoryProperties(VmaAllocator allocator,const VkPhysicalDeviceMemoryProperties ** ppPhysicalDeviceMemoryProperties)16084 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(
16085 VmaAllocator allocator,
16086 const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
16087 {
16088 VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
16089 *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
16090 }
16091
vmaGetMemoryTypeProperties(VmaAllocator allocator,uint32_t memoryTypeIndex,VkMemoryPropertyFlags * pFlags)16092 VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(
16093 VmaAllocator allocator,
16094 uint32_t memoryTypeIndex,
16095 VkMemoryPropertyFlags* pFlags)
16096 {
16097 VMA_ASSERT(allocator && pFlags);
16098 VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
16099 *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
16100 }
16101
vmaSetCurrentFrameIndex(VmaAllocator allocator,uint32_t frameIndex)16102 VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(
16103 VmaAllocator allocator,
16104 uint32_t frameIndex)
16105 {
16106 VMA_ASSERT(allocator);
16107
16108 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16109
16110 allocator->SetCurrentFrameIndex(frameIndex);
16111 }
16112
vmaCalculateStatistics(VmaAllocator allocator,VmaTotalStatistics * pStats)16113 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(
16114 VmaAllocator allocator,
16115 VmaTotalStatistics* pStats)
16116 {
16117 VMA_ASSERT(allocator && pStats);
16118 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16119 allocator->CalculateStatistics(pStats);
16120 }
16121
vmaFreeEmptyBlock(VmaAllocator allocator)16122 VMA_CALL_PRE void VMA_CALL_POST vmaFreeEmptyBlock(
16123 VmaAllocator allocator)
16124 {
16125 VMA_ASSERT(allocator);
16126 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16127 allocator->FreeEmptyBlock();
16128 }
16129
vmaGetHeapBudgets(VmaAllocator allocator,VmaBudget * pBudgets)16130 VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(
16131 VmaAllocator allocator,
16132 VmaBudget* pBudgets)
16133 {
16134 VMA_ASSERT(allocator && pBudgets);
16135 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16136 allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount());
16137 }
16138
16139 #if VMA_STATS_STRING_ENABLED
16140
vmaBuildStatsString(VmaAllocator allocator,char ** ppStatsString,VkBool32 detailedMap)16141 VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
16142 VmaAllocator allocator,
16143 char** ppStatsString,
16144 VkBool32 detailedMap)
16145 {
16146 VMA_ASSERT(allocator && ppStatsString);
16147 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16148
16149 VmaStringBuilder sb(allocator->GetAllocationCallbacks());
16150 {
16151 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
16152 allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());
16153
16154 VmaTotalStatistics stats;
16155 allocator->CalculateStatistics(&stats);
16156
16157 VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
16158 json.BeginObject();
16159 {
16160 json.WriteString("General");
16161 json.BeginObject();
16162 {
16163 const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties;
16164 const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps;
16165
16166 json.WriteString("API");
16167 json.WriteString("Vulkan");
16168
16169 json.WriteString("apiVersion");
16170 json.BeginString();
16171 json.ContinueString(VK_API_VERSION_MAJOR(deviceProperties.apiVersion));
16172 json.ContinueString(".");
16173 json.ContinueString(VK_API_VERSION_MINOR(deviceProperties.apiVersion));
16174 json.ContinueString(".");
16175 json.ContinueString(VK_API_VERSION_PATCH(deviceProperties.apiVersion));
16176 json.EndString();
16177
16178 json.WriteString("GPU");
16179 json.WriteString(deviceProperties.deviceName);
16180 json.WriteString("deviceType");
16181 json.WriteNumber(static_cast<uint32_t>(deviceProperties.deviceType));
16182
16183 json.WriteString("maxMemoryAllocationCount");
16184 json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount);
16185 json.WriteString("bufferImageGranularity");
16186 json.WriteNumber(deviceProperties.limits.bufferImageGranularity);
16187 json.WriteString("nonCoherentAtomSize");
16188 json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize);
16189
16190 json.WriteString("memoryHeapCount");
16191 json.WriteNumber(memoryProperties.memoryHeapCount);
16192 json.WriteString("memoryTypeCount");
16193 json.WriteNumber(memoryProperties.memoryTypeCount);
16194 }
16195 json.EndObject();
16196 }
16197 {
16198 json.WriteString("Total");
16199 VmaPrintDetailedStatistics(json, stats.total);
16200 }
16201 {
16202 json.WriteString("MemoryInfo");
16203 json.BeginObject();
16204 {
16205 for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
16206 {
16207 json.BeginString("Heap ");
16208 json.ContinueString(heapIndex);
16209 json.EndString();
16210 json.BeginObject();
16211 {
16212 const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex];
16213 json.WriteString("Flags");
16214 json.BeginArray(true);
16215 {
16216 if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
16217 json.WriteString("DEVICE_LOCAL");
16218 #if VMA_VULKAN_VERSION >= 1001000
16219 if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT)
16220 json.WriteString("MULTI_INSTANCE");
16221 #endif
16222
16223 VkMemoryHeapFlags flags = heapInfo.flags &
16224 ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT
16225 #if VMA_VULKAN_VERSION >= 1001000
16226 | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT
16227 #endif
16228 );
16229 if (flags != 0)
16230 json.WriteNumber(flags);
16231 }
16232 json.EndArray();
16233
16234 json.WriteString("Size");
16235 json.WriteNumber(heapInfo.size);
16236
16237 json.WriteString("Budget");
16238 json.BeginObject();
16239 {
16240 json.WriteString("BudgetBytes");
16241 json.WriteNumber(budgets[heapIndex].budget);
16242 json.WriteString("UsageBytes");
16243 json.WriteNumber(budgets[heapIndex].usage);
16244 }
16245 json.EndObject();
16246
16247 json.WriteString("Stats");
16248 VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);
16249
16250 json.WriteString("MemoryPools");
16251 json.BeginObject();
16252 {
16253 for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
16254 {
16255 if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
16256 {
16257 json.BeginString("Type ");
16258 json.ContinueString(typeIndex);
16259 json.EndString();
16260 json.BeginObject();
16261 {
16262 json.WriteString("Flags");
16263 json.BeginArray(true);
16264 {
16265 VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
16266 if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
16267 json.WriteString("DEVICE_LOCAL");
16268 if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
16269 json.WriteString("HOST_VISIBLE");
16270 if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
16271 json.WriteString("HOST_COHERENT");
16272 if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
16273 json.WriteString("HOST_CACHED");
16274 if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
16275 json.WriteString("LAZILY_ALLOCATED");
16276 #if VMA_VULKAN_VERSION >= 1001000
16277 if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT)
16278 json.WriteString("PROTECTED");
16279 #endif
16280 #if VK_AMD_device_coherent_memory
16281 if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY)
16282 json.WriteString("DEVICE_COHERENT_AMD");
16283 if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)
16284 json.WriteString("DEVICE_UNCACHED_AMD");
16285 #endif
16286
16287 flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
16288 #if VMA_VULKAN_VERSION >= 1001000
16289 | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
16290 #endif
16291 #if VK_AMD_device_coherent_memory
16292 | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY
16293 | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY
16294 #endif
16295 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
16296 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
16297 | VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
16298 if (flags != 0)
16299 json.WriteNumber(flags);
16300 }
16301 json.EndArray();
16302
16303 json.WriteString("Stats");
16304 VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);
16305 }
16306 json.EndObject();
16307 }
16308 }
16309
16310 }
16311 json.EndObject();
16312 }
16313 json.EndObject();
16314 }
16315 }
16316 json.EndObject();
16317 }
16318
16319 if (detailedMap == VK_TRUE)
16320 allocator->PrintDetailedMap(json);
16321
16322 json.EndObject();
16323 }
16324
16325 *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());
16326 }
16327
vmaFreeStatsString(VmaAllocator allocator,char * pStatsString)16328 VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(
16329 VmaAllocator allocator,
16330 char* pStatsString)
16331 {
16332 if(pStatsString != VMA_NULL)
16333 {
16334 VMA_ASSERT(allocator);
16335 VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);
16336 }
16337 }
16338
16339 #endif // VMA_STATS_STRING_ENABLED
16340
16341 /*
16342 This function is not protected by any mutex because it just reads immutable data.
16343 */
vmaFindMemoryTypeIndex(VmaAllocator allocator,uint32_t memoryTypeBits,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)16344 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(
16345 VmaAllocator allocator,
16346 uint32_t memoryTypeBits,
16347 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16348 uint32_t* pMemoryTypeIndex)
16349 {
16350 VMA_ASSERT(allocator != VK_NULL_HANDLE);
16351 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
16352 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
16353
16354 return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, UINT32_MAX, pMemoryTypeIndex);
16355 }
16356
vmaFindMemoryTypeIndexForBufferInfo(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)16357 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(
16358 VmaAllocator allocator,
16359 const VkBufferCreateInfo* pBufferCreateInfo,
16360 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16361 uint32_t* pMemoryTypeIndex)
16362 {
16363 VMA_ASSERT(allocator != VK_NULL_HANDLE);
16364 VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
16365 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
16366 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
16367
16368 const VkDevice hDev = allocator->m_hDevice;
16369 const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
16370 VkResult res;
16371
16372 #if VMA_VULKAN_VERSION >= 1003000
16373 if(funcs->vkGetDeviceBufferMemoryRequirements)
16374 {
16375 // Can query straight from VkBufferCreateInfo :)
16376 VkDeviceBufferMemoryRequirements devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS};
16377 devBufMemReq.pCreateInfo = pBufferCreateInfo;
16378
16379 VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
16380 (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq);
16381
16382 res = allocator->FindMemoryTypeIndex(
16383 memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);
16384 }
16385 else
16386 #endif // #if VMA_VULKAN_VERSION >= 1003000
16387 {
16388 // Must create a dummy buffer to query :(
16389 VkBuffer hBuffer = VK_NULL_HANDLE;
16390 res = funcs->vkCreateBuffer(
16391 hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
16392 if(res == VK_SUCCESS)
16393 {
16394 VkMemoryRequirements memReq = {};
16395 funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq);
16396
16397 res = allocator->FindMemoryTypeIndex(
16398 memReq.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);
16399
16400 funcs->vkDestroyBuffer(
16401 hDev, hBuffer, allocator->GetAllocationCallbacks());
16402 }
16403 }
16404 return res;
16405 }
16406
vmaFindMemoryTypeIndexForImageInfo(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,uint32_t * pMemoryTypeIndex)16407 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(
16408 VmaAllocator allocator,
16409 const VkImageCreateInfo* pImageCreateInfo,
16410 const VmaAllocationCreateInfo* pAllocationCreateInfo,
16411 uint32_t* pMemoryTypeIndex)
16412 {
16413 VMA_ASSERT(allocator != VK_NULL_HANDLE);
16414 VMA_ASSERT(pImageCreateInfo != VMA_NULL);
16415 VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
16416 VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
16417
16418 const VkDevice hDev = allocator->m_hDevice;
16419 const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();
16420 VkResult res;
16421
16422 #if VMA_VULKAN_VERSION >= 1003000
16423 if(funcs->vkGetDeviceImageMemoryRequirements)
16424 {
16425 // Can query straight from VkImageCreateInfo :)
16426 VkDeviceImageMemoryRequirements devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS};
16427 devImgMemReq.pCreateInfo = pImageCreateInfo;
16428 VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&
16429 "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect.");
16430
16431 VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
16432 (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq);
16433
16434 res = allocator->FindMemoryTypeIndex(
16435 memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);
16436 }
16437 else
16438 #endif // #if VMA_VULKAN_VERSION >= 1003000
16439 {
16440 // Must create a dummy image to query :(
16441 VkImage hImage = VK_NULL_HANDLE;
16442 res = funcs->vkCreateImage(
16443 hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
16444 if(res == VK_SUCCESS)
16445 {
16446 VkMemoryRequirements memReq = {};
16447 funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq);
16448
16449 res = allocator->FindMemoryTypeIndex(
16450 memReq.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);
16451
16452 funcs->vkDestroyImage(
16453 hDev, hImage, allocator->GetAllocationCallbacks());
16454 }
16455 }
16456 return res;
16457 }
16458
vmaCreatePool(VmaAllocator allocator,const VmaPoolCreateInfo * pCreateInfo,VmaPool * pPool)16459 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(
16460 VmaAllocator allocator,
16461 const VmaPoolCreateInfo* pCreateInfo,
16462 VmaPool* pPool)
16463 {
16464 VMA_ASSERT(allocator && pCreateInfo && pPool);
16465
16466 VMA_DEBUG_LOG("vmaCreatePool");
16467
16468 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16469
16470 return allocator->CreatePool(pCreateInfo, pPool);
16471 }
16472
vmaDestroyPool(VmaAllocator allocator,VmaPool pool)16473 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(
16474 VmaAllocator allocator,
16475 VmaPool pool)
16476 {
16477 VMA_ASSERT(allocator);
16478
16479 if(pool == VK_NULL_HANDLE)
16480 {
16481 return;
16482 }
16483
16484 VMA_DEBUG_LOG("vmaDestroyPool");
16485
16486 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16487
16488 allocator->DestroyPool(pool);
16489 }
16490
vmaGetPoolStatistics(VmaAllocator allocator,VmaPool pool,VmaStatistics * pPoolStats)16491 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(
16492 VmaAllocator allocator,
16493 VmaPool pool,
16494 VmaStatistics* pPoolStats)
16495 {
16496 VMA_ASSERT(allocator && pool && pPoolStats);
16497
16498 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16499
16500 allocator->GetPoolStatistics(pool, pPoolStats);
16501 }
16502
vmaCalculatePoolStatistics(VmaAllocator allocator,VmaPool pool,VmaDetailedStatistics * pPoolStats)16503 VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(
16504 VmaAllocator allocator,
16505 VmaPool pool,
16506 VmaDetailedStatistics* pPoolStats)
16507 {
16508 VMA_ASSERT(allocator && pool && pPoolStats);
16509
16510 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16511
16512 allocator->CalculatePoolStatistics(pool, pPoolStats);
16513 }
16514
vmaCheckPoolCorruption(VmaAllocator allocator,VmaPool pool)16515 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
16516 {
16517 VMA_ASSERT(allocator && pool);
16518
16519 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16520
16521 VMA_DEBUG_LOG("vmaCheckPoolCorruption");
16522
16523 return allocator->CheckPoolCorruption(pool);
16524 }
16525
vmaGetPoolName(VmaAllocator allocator,VmaPool pool,const char ** ppName)16526 VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(
16527 VmaAllocator allocator,
16528 VmaPool pool,
16529 const char** ppName)
16530 {
16531 VMA_ASSERT(allocator && pool && ppName);
16532
16533 VMA_DEBUG_LOG("vmaGetPoolName");
16534
16535 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16536
16537 *ppName = pool->GetName();
16538 }
16539
vmaSetPoolName(VmaAllocator allocator,VmaPool pool,const char * pName)16540 VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(
16541 VmaAllocator allocator,
16542 VmaPool pool,
16543 const char* pName)
16544 {
16545 VMA_ASSERT(allocator && pool);
16546
16547 VMA_DEBUG_LOG("vmaSetPoolName");
16548
16549 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16550
16551 pool->SetName(pName);
16552 }
16553
vmaAllocateMemory(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16554 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(
16555 VmaAllocator allocator,
16556 const VkMemoryRequirements* pVkMemoryRequirements,
16557 const VmaAllocationCreateInfo* pCreateInfo,
16558 VmaAllocation* pAllocation,
16559 VmaAllocationInfo* pAllocationInfo)
16560 {
16561 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
16562
16563 VMA_DEBUG_LOG("vmaAllocateMemory");
16564
16565 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16566
16567 VkResult result = allocator->AllocateMemory(
16568 *pVkMemoryRequirements,
16569 false, // requiresDedicatedAllocation
16570 false, // prefersDedicatedAllocation
16571 VK_NULL_HANDLE, // dedicatedBuffer
16572 VK_NULL_HANDLE, // dedicatedImage
16573 UINT32_MAX, // dedicatedBufferImageUsage
16574 *pCreateInfo,
16575 VMA_SUBALLOCATION_TYPE_UNKNOWN,
16576 1, // allocationCount
16577 pAllocation);
16578
16579 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16580 {
16581 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16582 }
16583
16584 return result;
16585 }
16586
vmaAllocateMemoryPages(VmaAllocator allocator,const VkMemoryRequirements * pVkMemoryRequirements,const VmaAllocationCreateInfo * pCreateInfo,size_t allocationCount,VmaAllocation * pAllocations,VmaAllocationInfo * pAllocationInfo)16587 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(
16588 VmaAllocator allocator,
16589 const VkMemoryRequirements* pVkMemoryRequirements,
16590 const VmaAllocationCreateInfo* pCreateInfo,
16591 size_t allocationCount,
16592 VmaAllocation* pAllocations,
16593 VmaAllocationInfo* pAllocationInfo)
16594 {
16595 if(allocationCount == 0)
16596 {
16597 return VK_SUCCESS;
16598 }
16599
16600 VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
16601
16602 VMA_DEBUG_LOG("vmaAllocateMemoryPages");
16603
16604 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16605
16606 VkResult result = allocator->AllocateMemory(
16607 *pVkMemoryRequirements,
16608 false, // requiresDedicatedAllocation
16609 false, // prefersDedicatedAllocation
16610 VK_NULL_HANDLE, // dedicatedBuffer
16611 VK_NULL_HANDLE, // dedicatedImage
16612 UINT32_MAX, // dedicatedBufferImageUsage
16613 *pCreateInfo,
16614 VMA_SUBALLOCATION_TYPE_UNKNOWN,
16615 allocationCount,
16616 pAllocations);
16617
16618 if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
16619 {
16620 for(size_t i = 0; i < allocationCount; ++i)
16621 {
16622 allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
16623 }
16624 }
16625
16626 return result;
16627 }
16628
vmaAllocateMemoryForBuffer(VmaAllocator allocator,VkBuffer buffer,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16629 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(
16630 VmaAllocator allocator,
16631 VkBuffer buffer,
16632 const VmaAllocationCreateInfo* pCreateInfo,
16633 VmaAllocation* pAllocation,
16634 VmaAllocationInfo* pAllocationInfo)
16635 {
16636 VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16637
16638 VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
16639
16640 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16641
16642 VkMemoryRequirements vkMemReq = {};
16643 bool requiresDedicatedAllocation = false;
16644 bool prefersDedicatedAllocation = false;
16645 allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
16646 requiresDedicatedAllocation,
16647 prefersDedicatedAllocation);
16648
16649 VkResult result = allocator->AllocateMemory(
16650 vkMemReq,
16651 requiresDedicatedAllocation,
16652 prefersDedicatedAllocation,
16653 buffer, // dedicatedBuffer
16654 VK_NULL_HANDLE, // dedicatedImage
16655 UINT32_MAX, // dedicatedBufferImageUsage
16656 *pCreateInfo,
16657 VMA_SUBALLOCATION_TYPE_BUFFER,
16658 1, // allocationCount
16659 pAllocation);
16660
16661 if(pAllocationInfo && result == VK_SUCCESS)
16662 {
16663 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16664 }
16665
16666 return result;
16667 }
16668
vmaAllocateMemoryForImage(VmaAllocator allocator,VkImage image,const VmaAllocationCreateInfo * pCreateInfo,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)16669 VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(
16670 VmaAllocator allocator,
16671 VkImage image,
16672 const VmaAllocationCreateInfo* pCreateInfo,
16673 VmaAllocation* pAllocation,
16674 VmaAllocationInfo* pAllocationInfo)
16675 {
16676 VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
16677
16678 VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
16679
16680 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16681
16682 VkMemoryRequirements vkMemReq = {};
16683 bool requiresDedicatedAllocation = false;
16684 bool prefersDedicatedAllocation = false;
16685 allocator->GetImageMemoryRequirements(image, vkMemReq,
16686 requiresDedicatedAllocation, prefersDedicatedAllocation);
16687
16688 VkResult result = allocator->AllocateMemory(
16689 vkMemReq,
16690 requiresDedicatedAllocation,
16691 prefersDedicatedAllocation,
16692 VK_NULL_HANDLE, // dedicatedBuffer
16693 image, // dedicatedImage
16694 UINT32_MAX, // dedicatedBufferImageUsage
16695 *pCreateInfo,
16696 VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
16697 1, // allocationCount
16698 pAllocation);
16699
16700 if(pAllocationInfo && result == VK_SUCCESS)
16701 {
16702 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
16703 }
16704
16705 return result;
16706 }
16707
vmaFreeMemory(VmaAllocator allocator,VmaAllocation allocation)16708 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(
16709 VmaAllocator allocator,
16710 VmaAllocation allocation)
16711 {
16712 VMA_ASSERT(allocator);
16713
16714 if(allocation == VK_NULL_HANDLE)
16715 {
16716 return;
16717 }
16718
16719 VMA_DEBUG_LOG("vmaFreeMemory");
16720
16721 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16722
16723 allocator->FreeMemory(
16724 1, // allocationCount
16725 &allocation);
16726 }
16727
vmaFreeMemoryPages(VmaAllocator allocator,size_t allocationCount,const VmaAllocation * pAllocations)16728 VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(
16729 VmaAllocator allocator,
16730 size_t allocationCount,
16731 const VmaAllocation* pAllocations)
16732 {
16733 if(allocationCount == 0)
16734 {
16735 return;
16736 }
16737
16738 VMA_ASSERT(allocator);
16739
16740 VMA_DEBUG_LOG("vmaFreeMemoryPages");
16741
16742 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16743
16744 allocator->FreeMemory(allocationCount, pAllocations);
16745 }
16746
vmaGetAllocationInfo(VmaAllocator allocator,VmaAllocation allocation,VmaAllocationInfo * pAllocationInfo)16747 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(
16748 VmaAllocator allocator,
16749 VmaAllocation allocation,
16750 VmaAllocationInfo* pAllocationInfo)
16751 {
16752 VMA_ASSERT(allocator && allocation && pAllocationInfo);
16753
16754 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16755
16756 allocator->GetAllocationInfo(allocation, pAllocationInfo);
16757 }
16758
vmaSetAllocationUserData(VmaAllocator allocator,VmaAllocation allocation,void * pUserData)16759 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(
16760 VmaAllocator allocator,
16761 VmaAllocation allocation,
16762 void* pUserData)
16763 {
16764 VMA_ASSERT(allocator && allocation);
16765
16766 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16767
16768 allocation->SetUserData(allocator, pUserData);
16769 }
16770
vmaSetAllocationName(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,const char * VMA_NULLABLE pName)16771 VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(
16772 VmaAllocator VMA_NOT_NULL allocator,
16773 VmaAllocation VMA_NOT_NULL allocation,
16774 const char* VMA_NULLABLE pName)
16775 {
16776 allocation->SetName(allocator, pName);
16777 }
16778
vmaGetAllocationMemoryProperties(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,VkMemoryPropertyFlags * VMA_NOT_NULL pFlags)16779 VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(
16780 VmaAllocator VMA_NOT_NULL allocator,
16781 VmaAllocation VMA_NOT_NULL allocation,
16782 VkMemoryPropertyFlags* VMA_NOT_NULL pFlags)
16783 {
16784 VMA_ASSERT(allocator && allocation && pFlags);
16785 const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
16786 *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
16787 }
16788
vmaMapMemory(VmaAllocator allocator,VmaAllocation allocation,void ** ppData)16789 VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(
16790 VmaAllocator allocator,
16791 VmaAllocation allocation,
16792 void** ppData)
16793 {
16794 VMA_ASSERT(allocator && allocation && ppData);
16795
16796 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16797
16798 return allocator->Map(allocation, ppData);
16799 }
16800
vmaUnmapMemory(VmaAllocator allocator,VmaAllocation allocation)16801 VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(
16802 VmaAllocator allocator,
16803 VmaAllocation allocation)
16804 {
16805 VMA_ASSERT(allocator && allocation);
16806
16807 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16808
16809 allocator->Unmap(allocation);
16810 }
16811
vmaFlushAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16812 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
16813 VmaAllocator allocator,
16814 VmaAllocation allocation,
16815 VkDeviceSize offset,
16816 VkDeviceSize size)
16817 {
16818 VMA_ASSERT(allocator && allocation);
16819
16820 VMA_DEBUG_LOG("vmaFlushAllocation");
16821
16822 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16823
16824 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
16825
16826 return res;
16827 }
16828
vmaInvalidateAllocation(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize offset,VkDeviceSize size)16829 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
16830 VmaAllocator allocator,
16831 VmaAllocation allocation,
16832 VkDeviceSize offset,
16833 VkDeviceSize size)
16834 {
16835 VMA_ASSERT(allocator && allocation);
16836
16837 VMA_DEBUG_LOG("vmaInvalidateAllocation");
16838
16839 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16840
16841 const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
16842
16843 return res;
16844 }
16845
vmaFlushAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)16846 VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
16847 VmaAllocator allocator,
16848 uint32_t allocationCount,
16849 const VmaAllocation* allocations,
16850 const VkDeviceSize* offsets,
16851 const VkDeviceSize* sizes)
16852 {
16853 VMA_ASSERT(allocator);
16854
16855 if(allocationCount == 0)
16856 {
16857 return VK_SUCCESS;
16858 }
16859
16860 VMA_ASSERT(allocations);
16861
16862 VMA_DEBUG_LOG("vmaFlushAllocations");
16863
16864 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16865
16866 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
16867
16868 return res;
16869 }
16870
vmaInvalidateAllocations(VmaAllocator allocator,uint32_t allocationCount,const VmaAllocation * allocations,const VkDeviceSize * offsets,const VkDeviceSize * sizes)16871 VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
16872 VmaAllocator allocator,
16873 uint32_t allocationCount,
16874 const VmaAllocation* allocations,
16875 const VkDeviceSize* offsets,
16876 const VkDeviceSize* sizes)
16877 {
16878 VMA_ASSERT(allocator);
16879
16880 if(allocationCount == 0)
16881 {
16882 return VK_SUCCESS;
16883 }
16884
16885 VMA_ASSERT(allocations);
16886
16887 VMA_DEBUG_LOG("vmaInvalidateAllocations");
16888
16889 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16890
16891 const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
16892
16893 return res;
16894 }
16895
vmaCheckCorruption(VmaAllocator allocator,uint32_t memoryTypeBits)16896 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
16897 VmaAllocator allocator,
16898 uint32_t memoryTypeBits)
16899 {
16900 VMA_ASSERT(allocator);
16901
16902 VMA_DEBUG_LOG("vmaCheckCorruption");
16903
16904 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16905
16906 return allocator->CheckCorruption(memoryTypeBits);
16907 }
16908
vmaBeginDefragmentation(VmaAllocator allocator,const VmaDefragmentationInfo * pInfo,VmaDefragmentationContext * pContext)16909 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(
16910 VmaAllocator allocator,
16911 const VmaDefragmentationInfo* pInfo,
16912 VmaDefragmentationContext* pContext)
16913 {
16914 VMA_ASSERT(allocator && pInfo && pContext);
16915
16916 VMA_DEBUG_LOG("vmaBeginDefragmentation");
16917
16918 if (pInfo->pool != VMA_NULL)
16919 {
16920 // Check if run on supported algorithms
16921 if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
16922 return VK_ERROR_FEATURE_NOT_PRESENT;
16923 }
16924
16925 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16926
16927 *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo);
16928 return VK_SUCCESS;
16929 }
16930
vmaEndDefragmentation(VmaAllocator allocator,VmaDefragmentationContext context,VmaDefragmentationStats * pStats)16931 VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(
16932 VmaAllocator allocator,
16933 VmaDefragmentationContext context,
16934 VmaDefragmentationStats* pStats)
16935 {
16936 VMA_ASSERT(allocator && context);
16937
16938 VMA_DEBUG_LOG("vmaEndDefragmentation");
16939
16940 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16941
16942 if (pStats)
16943 context->GetStats(*pStats);
16944 vma_delete(allocator, context);
16945 }
16946
vmaBeginDefragmentationPass(VmaAllocator VMA_NOT_NULL allocator,VmaDefragmentationContext VMA_NOT_NULL context,VmaDefragmentationPassMoveInfo * VMA_NOT_NULL pPassInfo)16947 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(
16948 VmaAllocator VMA_NOT_NULL allocator,
16949 VmaDefragmentationContext VMA_NOT_NULL context,
16950 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
16951 {
16952 VMA_ASSERT(context && pPassInfo);
16953
16954 VMA_DEBUG_LOG("vmaBeginDefragmentationPass");
16955
16956 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16957
16958 return context->DefragmentPassBegin(*pPassInfo);
16959 }
16960
vmaEndDefragmentationPass(VmaAllocator VMA_NOT_NULL allocator,VmaDefragmentationContext VMA_NOT_NULL context,VmaDefragmentationPassMoveInfo * VMA_NOT_NULL pPassInfo)16961 VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(
16962 VmaAllocator VMA_NOT_NULL allocator,
16963 VmaDefragmentationContext VMA_NOT_NULL context,
16964 VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)
16965 {
16966 VMA_ASSERT(context && pPassInfo);
16967
16968 VMA_DEBUG_LOG("vmaEndDefragmentationPass");
16969
16970 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16971
16972 return context->DefragmentPassEnd(*pPassInfo);
16973 }
16974
vmaBindBufferMemory(VmaAllocator allocator,VmaAllocation allocation,VkBuffer buffer)16975 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(
16976 VmaAllocator allocator,
16977 VmaAllocation allocation,
16978 VkBuffer buffer)
16979 {
16980 VMA_ASSERT(allocator && allocation && buffer);
16981
16982 VMA_DEBUG_LOG("vmaBindBufferMemory");
16983
16984 VMA_DEBUG_GLOBAL_MUTEX_LOCK
16985
16986 return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);
16987 }
16988
vmaBindBufferMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkBuffer buffer,const void * pNext)16989 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(
16990 VmaAllocator allocator,
16991 VmaAllocation allocation,
16992 VkDeviceSize allocationLocalOffset,
16993 VkBuffer buffer,
16994 const void* pNext)
16995 {
16996 VMA_ASSERT(allocator && allocation && buffer);
16997
16998 VMA_DEBUG_LOG("vmaBindBufferMemory2");
16999
17000 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17001
17002 return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);
17003 }
17004
vmaBindImageMemory(VmaAllocator allocator,VmaAllocation allocation,VkImage image)17005 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(
17006 VmaAllocator allocator,
17007 VmaAllocation allocation,
17008 VkImage image)
17009 {
17010 VMA_ASSERT(allocator && allocation && image);
17011
17012 VMA_DEBUG_LOG("vmaBindImageMemory");
17013
17014 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17015
17016 return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);
17017 }
17018
vmaBindImageMemory2(VmaAllocator allocator,VmaAllocation allocation,VkDeviceSize allocationLocalOffset,VkImage image,const void * pNext)17019 VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(
17020 VmaAllocator allocator,
17021 VmaAllocation allocation,
17022 VkDeviceSize allocationLocalOffset,
17023 VkImage image,
17024 const void* pNext)
17025 {
17026 VMA_ASSERT(allocator && allocation && image);
17027
17028 VMA_DEBUG_LOG("vmaBindImageMemory2");
17029
17030 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17031
17032 return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);
17033 }
17034
vmaCreateBuffer(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17035 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(
17036 VmaAllocator allocator,
17037 const VkBufferCreateInfo* pBufferCreateInfo,
17038 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17039 VkBuffer* pBuffer,
17040 VmaAllocation* pAllocation,
17041 VmaAllocationInfo* pAllocationInfo)
17042 {
17043 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
17044
17045 if(pBufferCreateInfo->size == 0)
17046 {
17047 return VK_ERROR_INITIALIZATION_FAILED;
17048 }
17049 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
17050 !allocator->m_UseKhrBufferDeviceAddress)
17051 {
17052 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
17053 return VK_ERROR_INITIALIZATION_FAILED;
17054 }
17055
17056 VMA_DEBUG_LOG("vmaCreateBuffer");
17057
17058 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17059
17060 *pBuffer = VK_NULL_HANDLE;
17061 *pAllocation = VK_NULL_HANDLE;
17062
17063 // 1. Create VkBuffer.
17064 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
17065 allocator->m_hDevice,
17066 pBufferCreateInfo,
17067 allocator->GetAllocationCallbacks(),
17068 pBuffer);
17069 if(res >= 0)
17070 {
17071 // 2. vkGetBufferMemoryRequirements.
17072 VkMemoryRequirements vkMemReq = {};
17073 bool requiresDedicatedAllocation = false;
17074 bool prefersDedicatedAllocation = false;
17075 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
17076 requiresDedicatedAllocation, prefersDedicatedAllocation);
17077
17078 // 3. Allocate memory using allocator.
17079 res = allocator->AllocateMemory(
17080 vkMemReq,
17081 requiresDedicatedAllocation,
17082 prefersDedicatedAllocation,
17083 *pBuffer, // dedicatedBuffer
17084 VK_NULL_HANDLE, // dedicatedImage
17085 pBufferCreateInfo->usage, // dedicatedBufferImageUsage
17086 *pAllocationCreateInfo,
17087 VMA_SUBALLOCATION_TYPE_BUFFER,
17088 1, // allocationCount
17089 pAllocation);
17090
17091 if(res >= 0)
17092 {
17093 // 3. Bind buffer with memory.
17094 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
17095 {
17096 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
17097 }
17098 if(res >= 0)
17099 {
17100 // All steps succeeded.
17101 #if VMA_STATS_STRING_ENABLED
17102 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
17103 #endif
17104 if(pAllocationInfo != VMA_NULL)
17105 {
17106 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17107 }
17108
17109 return VK_SUCCESS;
17110 }
17111 allocator->FreeMemory(
17112 1, // allocationCount
17113 pAllocation);
17114 *pAllocation = VK_NULL_HANDLE;
17115 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17116 *pBuffer = VK_NULL_HANDLE;
17117 return res;
17118 }
17119 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17120 *pBuffer = VK_NULL_HANDLE;
17121 return res;
17122 }
17123 return res;
17124 }
17125
vmaCreateBufferWithAlignment(VmaAllocator allocator,const VkBufferCreateInfo * pBufferCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkDeviceSize minAlignment,VkBuffer * pBuffer,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17126 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(
17127 VmaAllocator allocator,
17128 const VkBufferCreateInfo* pBufferCreateInfo,
17129 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17130 VkDeviceSize minAlignment,
17131 VkBuffer* pBuffer,
17132 VmaAllocation* pAllocation,
17133 VmaAllocationInfo* pAllocationInfo)
17134 {
17135 VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);
17136
17137 if(pBufferCreateInfo->size == 0)
17138 {
17139 return VK_ERROR_INITIALIZATION_FAILED;
17140 }
17141 if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
17142 !allocator->m_UseKhrBufferDeviceAddress)
17143 {
17144 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
17145 return VK_ERROR_INITIALIZATION_FAILED;
17146 }
17147
17148 VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");
17149
17150 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17151
17152 *pBuffer = VK_NULL_HANDLE;
17153 *pAllocation = VK_NULL_HANDLE;
17154
17155 // 1. Create VkBuffer.
17156 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
17157 allocator->m_hDevice,
17158 pBufferCreateInfo,
17159 allocator->GetAllocationCallbacks(),
17160 pBuffer);
17161 if(res >= 0)
17162 {
17163 // 2. vkGetBufferMemoryRequirements.
17164 VkMemoryRequirements vkMemReq = {};
17165 bool requiresDedicatedAllocation = false;
17166 bool prefersDedicatedAllocation = false;
17167 allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
17168 requiresDedicatedAllocation, prefersDedicatedAllocation);
17169
17170 // 2a. Include minAlignment
17171 vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);
17172
17173 // 3. Allocate memory using allocator.
17174 res = allocator->AllocateMemory(
17175 vkMemReq,
17176 requiresDedicatedAllocation,
17177 prefersDedicatedAllocation,
17178 *pBuffer, // dedicatedBuffer
17179 VK_NULL_HANDLE, // dedicatedImage
17180 pBufferCreateInfo->usage, // dedicatedBufferImageUsage
17181 *pAllocationCreateInfo,
17182 VMA_SUBALLOCATION_TYPE_BUFFER,
17183 1, // allocationCount
17184 pAllocation);
17185
17186 if(res >= 0)
17187 {
17188 // 3. Bind buffer with memory.
17189 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
17190 {
17191 res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);
17192 }
17193 if(res >= 0)
17194 {
17195 // All steps succeeded.
17196 #if VMA_STATS_STRING_ENABLED
17197 (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
17198 #endif
17199 if(pAllocationInfo != VMA_NULL)
17200 {
17201 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17202 }
17203
17204 return VK_SUCCESS;
17205 }
17206 allocator->FreeMemory(
17207 1, // allocationCount
17208 pAllocation);
17209 *pAllocation = VK_NULL_HANDLE;
17210 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17211 *pBuffer = VK_NULL_HANDLE;
17212 return res;
17213 }
17214 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17215 *pBuffer = VK_NULL_HANDLE;
17216 return res;
17217 }
17218 return res;
17219 }
17220
vmaCreateAliasingBuffer(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,const VkBufferCreateInfo * VMA_NOT_NULL pBufferCreateInfo,VkBuffer VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pBuffer)17221 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(
17222 VmaAllocator VMA_NOT_NULL allocator,
17223 VmaAllocation VMA_NOT_NULL allocation,
17224 const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,
17225 VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)
17226 {
17227 VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation);
17228
17229 VMA_DEBUG_LOG("vmaCreateAliasingBuffer");
17230
17231 *pBuffer = VK_NULL_HANDLE;
17232
17233 if (pBufferCreateInfo->size == 0)
17234 {
17235 return VK_ERROR_INITIALIZATION_FAILED;
17236 }
17237 if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&
17238 !allocator->m_UseKhrBufferDeviceAddress)
17239 {
17240 VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");
17241 return VK_ERROR_INITIALIZATION_FAILED;
17242 }
17243
17244 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17245
17246 // 1. Create VkBuffer.
17247 VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
17248 allocator->m_hDevice,
17249 pBufferCreateInfo,
17250 allocator->GetAllocationCallbacks(),
17251 pBuffer);
17252 if (res >= 0)
17253 {
17254 // 2. Bind buffer with memory.
17255 res = allocator->BindBufferMemory(allocation, 0, *pBuffer, VMA_NULL);
17256 if (res >= 0)
17257 {
17258 return VK_SUCCESS;
17259 }
17260 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
17261 }
17262 return res;
17263 }
17264
vmaDestroyBuffer(VmaAllocator allocator,VkBuffer buffer,VmaAllocation allocation)17265 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(
17266 VmaAllocator allocator,
17267 VkBuffer buffer,
17268 VmaAllocation allocation)
17269 {
17270 VMA_ASSERT(allocator);
17271
17272 if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
17273 {
17274 return;
17275 }
17276
17277 VMA_DEBUG_LOG("vmaDestroyBuffer");
17278
17279 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17280
17281 if(buffer != VK_NULL_HANDLE)
17282 {
17283 (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
17284 }
17285
17286 if(allocation != VK_NULL_HANDLE)
17287 {
17288 allocator->FreeMemory(
17289 1, // allocationCount
17290 &allocation);
17291 }
17292 }
17293
vmaCreateImage(VmaAllocator allocator,const VkImageCreateInfo * pImageCreateInfo,const VmaAllocationCreateInfo * pAllocationCreateInfo,VkImage * pImage,VmaAllocation * pAllocation,VmaAllocationInfo * pAllocationInfo)17294 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(
17295 VmaAllocator allocator,
17296 const VkImageCreateInfo* pImageCreateInfo,
17297 const VmaAllocationCreateInfo* pAllocationCreateInfo,
17298 VkImage* pImage,
17299 VmaAllocation* pAllocation,
17300 VmaAllocationInfo* pAllocationInfo)
17301 {
17302 VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
17303
17304 if(pImageCreateInfo->extent.width == 0 ||
17305 pImageCreateInfo->extent.height == 0 ||
17306 pImageCreateInfo->extent.depth == 0 ||
17307 pImageCreateInfo->mipLevels == 0 ||
17308 pImageCreateInfo->arrayLayers == 0)
17309 {
17310 return VK_ERROR_INITIALIZATION_FAILED;
17311 }
17312
17313 VMA_DEBUG_LOG("vmaCreateImage");
17314
17315 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17316
17317 *pImage = VK_NULL_HANDLE;
17318 *pAllocation = VK_NULL_HANDLE;
17319
17320 // 1. Create VkImage.
17321 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
17322 allocator->m_hDevice,
17323 pImageCreateInfo,
17324 allocator->GetAllocationCallbacks(),
17325 pImage);
17326 if(res >= 0)
17327 {
17328 VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
17329 VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
17330 VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
17331
17332 // 2. Allocate memory using allocator.
17333 VkMemoryRequirements vkMemReq = {};
17334 bool requiresDedicatedAllocation = false;
17335 bool prefersDedicatedAllocation = false;
17336 allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
17337 requiresDedicatedAllocation, prefersDedicatedAllocation);
17338
17339 res = allocator->AllocateMemory(
17340 vkMemReq,
17341 requiresDedicatedAllocation,
17342 prefersDedicatedAllocation,
17343 VK_NULL_HANDLE, // dedicatedBuffer
17344 *pImage, // dedicatedImage
17345 pImageCreateInfo->usage, // dedicatedBufferImageUsage
17346 *pAllocationCreateInfo,
17347 suballocType,
17348 1, // allocationCount
17349 pAllocation);
17350
17351 if(res >= 0)
17352 {
17353 // 3. Bind image with memory.
17354 if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)
17355 {
17356 res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);
17357 }
17358 if(res >= 0)
17359 {
17360 // All steps succeeded.
17361 #if VMA_STATS_STRING_ENABLED
17362 (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
17363 #endif
17364 if(pAllocationInfo != VMA_NULL)
17365 {
17366 allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
17367 }
17368
17369 return VK_SUCCESS;
17370 }
17371 allocator->FreeMemory(
17372 1, // allocationCount
17373 pAllocation);
17374 *pAllocation = VK_NULL_HANDLE;
17375 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
17376 *pImage = VK_NULL_HANDLE;
17377 return res;
17378 }
17379 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
17380 *pImage = VK_NULL_HANDLE;
17381 return res;
17382 }
17383 return res;
17384 }
17385
vmaCreateAliasingImage(VmaAllocator VMA_NOT_NULL allocator,VmaAllocation VMA_NOT_NULL allocation,const VkImageCreateInfo * VMA_NOT_NULL pImageCreateInfo,VkImage VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pImage)17386 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(
17387 VmaAllocator VMA_NOT_NULL allocator,
17388 VmaAllocation VMA_NOT_NULL allocation,
17389 const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,
17390 VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)
17391 {
17392 VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation);
17393
17394 *pImage = VK_NULL_HANDLE;
17395
17396 VMA_DEBUG_LOG("vmaCreateImage");
17397
17398 if (pImageCreateInfo->extent.width == 0 ||
17399 pImageCreateInfo->extent.height == 0 ||
17400 pImageCreateInfo->extent.depth == 0 ||
17401 pImageCreateInfo->mipLevels == 0 ||
17402 pImageCreateInfo->arrayLayers == 0)
17403 {
17404 return VK_ERROR_INITIALIZATION_FAILED;
17405 }
17406
17407 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17408
17409 // 1. Create VkImage.
17410 VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
17411 allocator->m_hDevice,
17412 pImageCreateInfo,
17413 allocator->GetAllocationCallbacks(),
17414 pImage);
17415 if (res >= 0)
17416 {
17417 // 2. Bind image with memory.
17418 res = allocator->BindImageMemory(allocation, 0, *pImage, VMA_NULL);
17419 if (res >= 0)
17420 {
17421 return VK_SUCCESS;
17422 }
17423 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
17424 }
17425 return res;
17426 }
17427
vmaDestroyImage(VmaAllocator VMA_NOT_NULL allocator,VkImage VMA_NULLABLE_NON_DISPATCHABLE image,VmaAllocation VMA_NULLABLE allocation)17428 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(
17429 VmaAllocator VMA_NOT_NULL allocator,
17430 VkImage VMA_NULLABLE_NON_DISPATCHABLE image,
17431 VmaAllocation VMA_NULLABLE allocation)
17432 {
17433 VMA_ASSERT(allocator);
17434
17435 if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
17436 {
17437 return;
17438 }
17439
17440 VMA_DEBUG_LOG("vmaDestroyImage");
17441
17442 VMA_DEBUG_GLOBAL_MUTEX_LOCK
17443
17444 if(image != VK_NULL_HANDLE)
17445 {
17446 (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
17447 }
17448 if(allocation != VK_NULL_HANDLE)
17449 {
17450 allocator->FreeMemory(
17451 1, // allocationCount
17452 &allocation);
17453 }
17454 }
17455
vmaCreateVirtualBlock(const VmaVirtualBlockCreateInfo * VMA_NOT_NULL pCreateInfo,VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)17456 VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(
17457 const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,
17458 VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)
17459 {
17460 VMA_ASSERT(pCreateInfo && pVirtualBlock);
17461 VMA_ASSERT(pCreateInfo->size > 0);
17462 VMA_DEBUG_LOG("vmaCreateVirtualBlock");
17463 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17464 *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);
17465 VkResult res = (*pVirtualBlock)->Init();
17466 if(res < 0)
17467 {
17468 vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);
17469 *pVirtualBlock = VK_NULL_HANDLE;
17470 }
17471 return res;
17472 }
17473
vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)17474 VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)
17475 {
17476 if(virtualBlock != VK_NULL_HANDLE)
17477 {
17478 VMA_DEBUG_LOG("vmaDestroyVirtualBlock");
17479 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17480 VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.
17481 vma_delete(&allocationCallbacks, virtualBlock);
17482 }
17483 }
17484
vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)17485 VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
17486 {
17487 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17488 VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");
17489 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17490 return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;
17491 }
17492
vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,VmaVirtualAllocationInfo * VMA_NOT_NULL pVirtualAllocInfo)17493 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17494 VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)
17495 {
17496 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);
17497 VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");
17498 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17499 virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo);
17500 }
17501
vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,const VmaVirtualAllocationCreateInfo * VMA_NOT_NULL pCreateInfo,VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE * VMA_NOT_NULL pAllocation,VkDeviceSize * VMA_NULLABLE pOffset)17502 VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17503 const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,
17504 VkDeviceSize* VMA_NULLABLE pOffset)
17505 {
17506 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL);
17507 VMA_DEBUG_LOG("vmaVirtualAllocate");
17508 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17509 return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);
17510 }
17511
vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)17512 VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)
17513 {
17514 if(allocation != VK_NULL_HANDLE)
17515 {
17516 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17517 VMA_DEBUG_LOG("vmaVirtualFree");
17518 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17519 virtualBlock->Free(allocation);
17520 }
17521 }
17522
vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)17523 VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)
17524 {
17525 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17526 VMA_DEBUG_LOG("vmaClearVirtualBlock");
17527 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17528 virtualBlock->Clear();
17529 }
17530
vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,void * VMA_NULLABLE pUserData)17531 VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17532 VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData)
17533 {
17534 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17535 VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");
17536 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17537 virtualBlock->SetAllocationUserData(allocation, pUserData);
17538 }
17539
vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaStatistics * VMA_NOT_NULL pStats)17540 VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17541 VmaStatistics* VMA_NOT_NULL pStats)
17542 {
17543 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
17544 VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics");
17545 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17546 virtualBlock->GetStatistics(*pStats);
17547 }
17548
vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,VmaDetailedStatistics * VMA_NOT_NULL pStats)17549 VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17550 VmaDetailedStatistics* VMA_NOT_NULL pStats)
17551 {
17552 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);
17553 VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics");
17554 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17555 virtualBlock->CalculateDetailedStatistics(*pStats);
17556 }
17557
17558 #if VMA_STATS_STRING_ENABLED
17559
vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE * VMA_NOT_NULL ppStatsString,VkBool32 detailedMap)17560 VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17561 char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)
17562 {
17563 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);
17564 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17565 const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();
17566 VmaStringBuilder sb(allocationCallbacks);
17567 virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);
17568 *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());
17569 }
17570
vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,char * VMA_NULLABLE pStatsString)17571 VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,
17572 char* VMA_NULLABLE pStatsString)
17573 {
17574 if(pStatsString != VMA_NULL)
17575 {
17576 VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);
17577 VMA_DEBUG_GLOBAL_MUTEX_LOCK;
17578 VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);
17579 }
17580 }
17581 #endif // VMA_STATS_STRING_ENABLED
17582 #endif // _VMA_PUBLIC_INTERFACE
17583 #endif // VMA_IMPLEMENTATION
17584
17585 /**
17586 \page quick_start Quick start
17587
17588 \section quick_start_project_setup Project setup
17589
17590 Vulkan Memory Allocator comes in form of a "stb-style" single header file.
17591 You don't need to build it as a separate library project.
17592 You can add this file directly to your project and submit it to code repository next to your other source files.
17593
17594 "Single header" doesn't mean that everything is contained in C/C++ declarations,
17595 like it tends to be in case of inline functions or C++ templates.
17596 It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
17597 If you don't do it properly, you will get linker errors.
17598
17599 To do it properly:
17600
17601 -# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
17602 This includes declarations of all members of the library.
17603 -# In exactly one CPP file define following macro before this include.
17604 It enables also internal definitions.
17605
17606 \code
17607 #define VMA_IMPLEMENTATION
17608 #include "vk_mem_alloc.h"
17609 \endcode
17610
17611 It may be a good idea to create dedicated CPP file just for this purpose.
17612
17613 This library includes header `<vulkan/vulkan.h>`, which in turn
17614 includes `<windows.h>` on Windows. If you need some specific macros defined
17615 before including these headers (like `WIN32_LEAN_AND_MEAN` or
17616 `WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
17617 them before every `#include` of this library.
17618
17619 This library is written in C++, but has C-compatible interface.
17620 Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
17621 implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
17622 Some features of C++14 used. STL containers, RTTI, or C++ exceptions are not used.
17623
17624
17625 \section quick_start_initialization Initialization
17626
17627 At program startup:
17628
17629 -# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.
17630 -# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
17631 calling vmaCreateAllocator().
17632
17633 Only members `physicalDevice`, `device`, `instance` are required.
17634 However, you should inform the library which Vulkan version do you use by setting
17635 VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable
17636 by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address).
17637 Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.
17638
17639 You may need to configure importing Vulkan functions. There are 3 ways to do this:
17640
17641 -# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows):
17642 - You don't need to do anything.
17643 - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default.
17644 -# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`,
17645 `vkGetDeviceProcAddr` (this is the option presented in the example below):
17646 - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1.
17647 - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr,
17648 VmaVulkanFunctions::vkGetDeviceProcAddr.
17649 - The library will fetch pointers to all other functions it needs internally.
17650 -# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like
17651 [Volk](https://github.com/zeux/volk):
17652 - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.
17653 - Pass these pointers via structure #VmaVulkanFunctions.
17654
17655 \code
17656 VmaVulkanFunctions vulkanFunctions = {};
17657 vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
17658 vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
17659
17660 VmaAllocatorCreateInfo allocatorCreateInfo = {};
17661 allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
17662 allocatorCreateInfo.physicalDevice = physicalDevice;
17663 allocatorCreateInfo.device = device;
17664 allocatorCreateInfo.instance = instance;
17665 allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
17666
17667 VmaAllocator allocator;
17668 vmaCreateAllocator(&allocatorCreateInfo, &allocator);
17669 \endcode
17670
17671
17672 \section quick_start_resource_allocation Resource allocation
17673
17674 When you want to create a buffer or image:
17675
17676 -# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
17677 -# Fill VmaAllocationCreateInfo structure.
17678 -# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
17679 already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory.
17680
17681 \code
17682 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17683 bufferInfo.size = 65536;
17684 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17685
17686 VmaAllocationCreateInfo allocInfo = {};
17687 allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
17688
17689 VkBuffer buffer;
17690 VmaAllocation allocation;
17691 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17692 \endcode
17693
17694 Don't forget to destroy your objects when no longer needed:
17695
17696 \code
17697 vmaDestroyBuffer(allocator, buffer, allocation);
17698 vmaDestroyAllocator(allocator);
17699 \endcode
17700
17701
17702 \page choosing_memory_type Choosing memory type
17703
17704 Physical devices in Vulkan support various combinations of memory heaps and
17705 types. Help with choosing correct and optimal memory type for your specific
17706 resource is one of the key features of this library. You can use it by filling
17707 appropriate members of VmaAllocationCreateInfo structure, as described below.
17708 You can also combine multiple methods.
17709
17710 -# If you just want to find memory type index that meets your requirements, you
17711 can use function: vmaFindMemoryTypeIndexForBufferInfo(),
17712 vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex().
17713 -# If you want to allocate a region of device memory without association with any
17714 specific image or buffer, you can use function vmaAllocateMemory(). Usage of
17715 this function is not recommended and usually not needed.
17716 vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,
17717 which may be useful for sparse binding.
17718 -# If you already have a buffer or an image created, you want to allocate memory
17719 for it and then you will bind it yourself, you can use function
17720 vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
17721 For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()
17722 or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().
17723 -# **This is the easiest and recommended way to use this library:**
17724 If you want to create a buffer or an image, allocate memory for it and bind
17725 them together, all in one call, you can use function vmaCreateBuffer(),
17726 vmaCreateImage().
17727
17728 When using 3. or 4., the library internally queries Vulkan for memory types
17729 supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
17730 and uses only one of these types.
17731
17732 If no memory type can be found that meets all the requirements, these functions
17733 return `VK_ERROR_FEATURE_NOT_PRESENT`.
17734
17735 You can leave VmaAllocationCreateInfo structure completely filled with zeros.
17736 It means no requirements are specified for memory type.
17737 It is valid, although not very useful.
17738
17739 \section choosing_memory_type_usage Usage
17740
17741 The easiest way to specify memory requirements is to fill member
17742 VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
17743 It defines high level, common usage types.
17744 Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically.
17745
17746 For example, if you want to create a uniform buffer that will be filled using
17747 transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can
17748 do it using following code. The buffer will most likely end up in a memory type with
17749 `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device.
17750
17751 \code
17752 VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17753 bufferInfo.size = 65536;
17754 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
17755
17756 VmaAllocationCreateInfo allocInfo = {};
17757 allocInfo.usage = VMA_MEMORY_USAGE_AUTO;
17758
17759 VkBuffer buffer;
17760 VmaAllocation allocation;
17761 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17762 \endcode
17763
17764 If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory
17765 on systems with discrete graphics card that have the memories separate, you can use
17766 #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST.
17767
17768 When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory,
17769 you also need to specify one of the host access flags:
17770 #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
17771 This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
17772 so you can map it.
17773
17774 For example, a staging buffer that will be filled via mapped pointer and then
17775 used as a source of transfer to the buffer decribed previously can be created like this.
17776 It will likely and up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT`
17777 but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM).
17778
17779 \code
17780 VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17781 stagingBufferInfo.size = 65536;
17782 stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
17783
17784 VmaAllocationCreateInfo stagingAllocInfo = {};
17785 stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;
17786 stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
17787
17788 VkBuffer stagingBuffer;
17789 VmaAllocation stagingAllocation;
17790 vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr);
17791 \endcode
17792
17793 For more examples of creating different kinds of resources, see chapter \ref usage_patterns.
17794
17795 Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows
17796 about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed,
17797 so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc.
17798 If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting
17799 memory type, as decribed below.
17800
17801 \note
17802 Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`,
17803 `VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`)
17804 are still available and work same way as in previous versions of the library
17805 for backward compatibility, but they are not recommended.
17806
17807 \section choosing_memory_type_required_preferred_flags Required and preferred flags
17808
17809 You can specify more detailed requirements by filling members
17810 VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
17811 with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
17812 if you want to create a buffer that will be persistently mapped on host (so it
17813 must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
17814 use following code:
17815
17816 \code
17817 VmaAllocationCreateInfo allocInfo = {};
17818 allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
17819 allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
17820 allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
17821
17822 VkBuffer buffer;
17823 VmaAllocation allocation;
17824 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17825 \endcode
17826
17827 A memory type is chosen that has all the required flags and as many preferred
17828 flags set as possible.
17829
17830 Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags,
17831 plus some extra "magic" (heuristics).
17832
17833 \section choosing_memory_type_explicit_memory_types Explicit memory types
17834
17835 If you inspected memory types available on the physical device and you have
17836 a preference for memory types that you want to use, you can fill member
17837 VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
17838 means that a memory type with that index is allowed to be used for the
17839 allocation. Special value 0, just like `UINT32_MAX`, means there are no
17840 restrictions to memory type index.
17841
17842 Please note that this member is NOT just a memory type index.
17843 Still you can use it to choose just one, specific memory type.
17844 For example, if you already determined that your buffer should be created in
17845 memory type 2, use following code:
17846
17847 \code
17848 uint32_t memoryTypeIndex = 2;
17849
17850 VmaAllocationCreateInfo allocInfo = {};
17851 allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
17852
17853 VkBuffer buffer;
17854 VmaAllocation allocation;
17855 vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
17856 \endcode
17857
17858
17859 \section choosing_memory_type_custom_memory_pools Custom memory pools
17860
17861 If you allocate from custom memory pool, all the ways of specifying memory
17862 requirements described above are not applicable and the aforementioned members
17863 of VmaAllocationCreateInfo structure are ignored. Memory type is selected
17864 explicitly when creating the pool and then used to make all the allocations from
17865 that pool. For further details, see \ref custom_memory_pools.
17866
17867 \section choosing_memory_type_dedicated_allocations Dedicated allocations
17868
17869 Memory for allocations is reserved out of larger block of `VkDeviceMemory`
17870 allocated from Vulkan internally. That is the main feature of this whole library.
17871 You can still request a separate memory block to be created for an allocation,
17872 just like you would do in a trivial solution without using any allocator.
17873 In that case, a buffer or image is always bound to that memory at offset 0.
17874 This is called a "dedicated allocation".
17875 You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
17876 The library can also internally decide to use dedicated allocation in some cases, e.g.:
17877
17878 - When the size of the allocation is large.
17879 - When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled
17880 and it reports that dedicated allocation is required or recommended for the resource.
17881 - When allocation of next big memory block fails due to not enough device memory,
17882 but allocation with the exact requested size succeeds.
17883
17884
17885 \page memory_mapping Memory mapping
17886
17887 To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
17888 to be able to read from it or write to it in CPU code.
17889 Mapping is possible only of memory allocated from a memory type that has
17890 `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
17891 Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
17892 You can use them directly with memory allocated by this library,
17893 but it is not recommended because of following issue:
17894 Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
17895 This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
17896 Because of this, Vulkan Memory Allocator provides following facilities:
17897
17898 \note If you want to be able to map an allocation, you need to specify one of the flags
17899 #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT
17900 in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable
17901 when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values.
17902 For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable,
17903 but they can still be used for consistency.
17904
17905 \section memory_mapping_mapping_functions Mapping functions
17906
17907 The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
17908 They are safer and more convenient to use than standard Vulkan functions.
17909 You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
17910 You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
17911 The way it is implemented is that the library always maps entire memory block, not just region of the allocation.
17912 For further details, see description of vmaMapMemory() function.
17913 Example:
17914
17915 \code
17916 // Having these objects initialized:
17917 struct ConstantBuffer
17918 {
17919 ...
17920 };
17921 ConstantBuffer constantBufferData = ...
17922
17923 VmaAllocator allocator = ...
17924 VkBuffer constantBuffer = ...
17925 VmaAllocation constantBufferAllocation = ...
17926
17927 // You can map and fill your buffer using following code:
17928
17929 void* mappedData;
17930 vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
17931 memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
17932 vmaUnmapMemory(allocator, constantBufferAllocation);
17933 \endcode
17934
17935 When mapping, you may see a warning from Vulkan validation layer similar to this one:
17936
17937 <i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>
17938
17939 It happens because the library maps entire `VkDeviceMemory` block, where different
17940 types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
17941 You can safely ignore it if you are sure you access only memory of the intended
17942 object that you wanted to map.
17943
17944
17945 \section memory_mapping_persistently_mapped_memory Persistently mapped memory
17946
17947 Kepping your memory persistently mapped is generally OK in Vulkan.
17948 You don't need to unmap it before using its data on the GPU.
17949 The library provides a special feature designed for that:
17950 Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
17951 VmaAllocationCreateInfo::flags stay mapped all the time,
17952 so you can just access CPU pointer to it any time
17953 without a need to call any "map" or "unmap" function.
17954 Example:
17955
17956 \code
17957 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
17958 bufCreateInfo.size = sizeof(ConstantBuffer);
17959 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
17960
17961 VmaAllocationCreateInfo allocCreateInfo = {};
17962 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
17963 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
17964 VMA_ALLOCATION_CREATE_MAPPED_BIT;
17965
17966 VkBuffer buf;
17967 VmaAllocation alloc;
17968 VmaAllocationInfo allocInfo;
17969 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
17970
17971 // Buffer is already mapped. You can access its memory.
17972 memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
17973 \endcode
17974
17975 \note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up
17976 in a mappable memory type.
17977 For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or
17978 #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
17979 #VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation.
17980 For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading.
17981
17982 \section memory_mapping_cache_control Cache flush and invalidate
17983
17984 Memory in Vulkan doesn't need to be unmapped before using it on GPU,
17985 but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
17986 you need to manually **invalidate** cache before reading of mapped pointer
17987 and **flush** cache after writing to mapped pointer.
17988 Map/unmap operations don't do that automatically.
17989 Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
17990 `vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
17991 functions that refer to given allocation object: vmaFlushAllocation(),
17992 vmaInvalidateAllocation(),
17993 or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().
17994
17995 Regions of memory specified for flush/invalidate must be aligned to
17996 `VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
17997 In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
17998 within blocks are aligned to this value, so their offsets are always multiply of
17999 `nonCoherentAtomSize` and two different allocations never share same "line" of this size.
18000
18001 Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
18002 currently provide `HOST_COHERENT` flag on all memory types that are
18003 `HOST_VISIBLE`, so on PC you may not need to bother.
18004
18005
18006 \page staying_within_budget Staying within budget
18007
18008 When developing a graphics-intensive game or program, it is important to avoid allocating
18009 more GPU memory than it is physically available. When the memory is over-committed,
18010 various bad things can happen, depending on the specific GPU, graphics driver, and
18011 operating system:
18012
18013 - It may just work without any problems.
18014 - The application may slow down because some memory blocks are moved to system RAM
18015 and the GPU has to access them through PCI Express bus.
18016 - A new allocation may take very long time to complete, even few seconds, and possibly
18017 freeze entire system.
18018 - The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18019 - It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`
18020 returned somewhere later.
18021
18022 \section staying_within_budget_querying_for_budget Querying for budget
18023
18024 To query for current memory usage and available budget, use function vmaGetHeapBudgets().
18025 Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.
18026
18027 Please note that this function returns different information and works faster than
18028 vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every
18029 allocation, while vmaCalculateStatistics() is intended to be used rarely,
18030 only to obtain statistical information, e.g. for debugging purposes.
18031
18032 It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information
18033 about the budget from Vulkan device. VMA is able to use this extension automatically.
18034 When not enabled, the allocator behaves same way, but then it estimates current usage
18035 and available budget based on its internal information and Vulkan memory heap sizes,
18036 which may be less precise. In order to use this extension:
18037
18038 1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2
18039 required by it are available and enable them. Please note that the first is a device
18040 extension and the second is instance extension!
18041 2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.
18042 3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from
18043 Vulkan inside of it to avoid overhead of querying it with every allocation.
18044
18045 \section staying_within_budget_controlling_memory_usage Controlling memory usage
18046
18047 There are many ways in which you can try to stay within the budget.
18048
18049 First, when making new allocation requires allocating a new memory block, the library
18050 tries not to exceed the budget automatically. If a block with default recommended size
18051 (e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even
18052 dedicated memory for just this resource.
18053
18054 If the size of the requested resource plus current memory usage is more than the
18055 budget, by default the library still tries to create it, leaving it to the Vulkan
18056 implementation whether the allocation succeeds or fails. You can change this behavior
18057 by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is
18058 not made if it would exceed the budget or if the budget is already exceeded.
18059 VMA then tries to make the allocation from the next eligible Vulkan memory type.
18060 The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18061 Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag
18062 when creating resources that are not essential for the application (e.g. the texture
18063 of a specific object) and not to pass it when creating critically important resources
18064 (e.g. render targets).
18065
18066 On AMD graphics cards there is a custom vendor extension available: <b>VK_AMD_memory_overallocation_behavior</b>
18067 that allows to control the behavior of the Vulkan implementation in out-of-memory cases -
18068 whether it should fail with an error code or still allow the allocation.
18069 Usage of this extension involves only passing extra structure on Vulkan device creation,
18070 so it is out of scope of this library.
18071
18072 Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure
18073 a new allocation is created only when it fits inside one of the existing memory blocks.
18074 If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
18075 This also ensures that the function call is very fast because it never goes to Vulkan
18076 to obtain a new block.
18077
18078 \note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount
18079 set to more than 0 will currently try to allocate memory blocks without checking whether they
18080 fit within budget.
18081
18082
18083 \page resource_aliasing Resource aliasing (overlap)
18084
18085 New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
18086 management, give an opportunity to alias (overlap) multiple resources in the
18087 same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
18088 It can be useful to save video memory, but it must be used with caution.
18089
18090 For example, if you know the flow of your whole render frame in advance, you
18091 are going to use some intermediate textures or buffers only during a small range of render passes,
18092 and you know these ranges don't overlap in time, you can bind these resources to
18093 the same place in memory, even if they have completely different parameters (width, height, format etc.).
18094
18095 
18096
18097 Such scenario is possible using VMA, but you need to create your images manually.
18098 Then you need to calculate parameters of an allocation to be made using formula:
18099
18100 - allocation size = max(size of each image)
18101 - allocation alignment = max(alignment of each image)
18102 - allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)
18103
18104 Following example shows two different images bound to the same place in memory,
18105 allocated to fit largest of them.
18106
18107 \code
18108 // A 512x512 texture to be sampled.
18109 VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18110 img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;
18111 img1CreateInfo.extent.width = 512;
18112 img1CreateInfo.extent.height = 512;
18113 img1CreateInfo.extent.depth = 1;
18114 img1CreateInfo.mipLevels = 10;
18115 img1CreateInfo.arrayLayers = 1;
18116 img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
18117 img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18118 img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18119 img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
18120 img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18121
18122 // A full screen texture to be used as color attachment.
18123 VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18124 img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;
18125 img2CreateInfo.extent.width = 1920;
18126 img2CreateInfo.extent.height = 1080;
18127 img2CreateInfo.extent.depth = 1;
18128 img2CreateInfo.mipLevels = 1;
18129 img2CreateInfo.arrayLayers = 1;
18130 img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
18131 img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18132 img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18133 img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
18134 img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18135
18136 VkImage img1;
18137 res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);
18138 VkImage img2;
18139 res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);
18140
18141 VkMemoryRequirements img1MemReq;
18142 vkGetImageMemoryRequirements(device, img1, &img1MemReq);
18143 VkMemoryRequirements img2MemReq;
18144 vkGetImageMemoryRequirements(device, img2, &img2MemReq);
18145
18146 VkMemoryRequirements finalMemReq = {};
18147 finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);
18148 finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);
18149 finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;
18150 // Validate if(finalMemReq.memoryTypeBits != 0)
18151
18152 VmaAllocationCreateInfo allocCreateInfo = {};
18153 allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
18154
18155 VmaAllocation alloc;
18156 res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);
18157
18158 res = vmaBindImageMemory(allocator, alloc, img1);
18159 res = vmaBindImageMemory(allocator, alloc, img2);
18160
18161 // You can use img1, img2 here, but not at the same time!
18162
18163 vmaFreeMemory(allocator, alloc);
18164 vkDestroyImage(allocator, img2, nullptr);
18165 vkDestroyImage(allocator, img1, nullptr);
18166 \endcode
18167
18168 Remember that using resources that alias in memory requires proper synchronization.
18169 You need to issue a memory barrier to make sure commands that use `img1` and `img2`
18170 don't overlap on GPU timeline.
18171 You also need to treat a resource after aliasing as uninitialized - containing garbage data.
18172 For example, if you use `img1` and then want to use `img2`, you need to issue
18173 an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.
18174
18175 Additional considerations:
18176
18177 - Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.
18178 See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.
18179 - You can create more complex layout where different images and buffers are bound
18180 at different offsets inside one large allocation. For example, one can imagine
18181 a big texture used in some render passes, aliasing with a set of many small buffers
18182 used between in some further passes. To bind a resource at non-zero offset in an allocation,
18183 use vmaBindBufferMemory2() / vmaBindImageMemory2().
18184 - Before allocating memory for the resources you want to alias, check `memoryTypeBits`
18185 returned in memory requirements of each resource to make sure the bits overlap.
18186 Some GPUs may expose multiple memory types suitable e.g. only for buffers or
18187 images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your
18188 resources may be disjoint. Aliasing them is not possible in that case.
18189
18190
18191 \page custom_memory_pools Custom memory pools
18192
18193 A memory pool contains a number of `VkDeviceMemory` blocks.
18194 The library automatically creates and manages default pool for each memory type available on the device.
18195 Default memory pool automatically grows in size.
18196 Size of allocated blocks is also variable and managed automatically.
18197
18198 You can create custom pool and allocate memory out of it.
18199 It can be useful if you want to:
18200
18201 - Keep certain kind of allocations separate from others.
18202 - Enforce particular, fixed size of Vulkan memory blocks.
18203 - Limit maximum amount of Vulkan memory allocated for that pool.
18204 - Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
18205 - Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in
18206 #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.
18207 - Perform defragmentation on a specific subset of your allocations.
18208
18209 To use custom memory pools:
18210
18211 -# Fill VmaPoolCreateInfo structure.
18212 -# Call vmaCreatePool() to obtain #VmaPool handle.
18213 -# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
18214 You don't need to specify any other parameters of this structure, like `usage`.
18215
18216 Example:
18217
18218 \code
18219 // Find memoryTypeIndex for the pool.
18220 VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18221 sampleBufCreateInfo.size = 0x10000; // Doesn't matter.
18222 sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18223
18224 VmaAllocationCreateInfo sampleAllocCreateInfo = {};
18225 sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18226
18227 uint32_t memTypeIndex;
18228 VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator,
18229 &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex);
18230 // Check res...
18231
18232 // Create a pool that can have at most 2 blocks, 128 MiB each.
18233 VmaPoolCreateInfo poolCreateInfo = {};
18234 poolCreateInfo.memoryTypeIndex = memTypeIndex;
18235 poolCreateInfo.blockSize = 128ull * 1024 * 1024;
18236 poolCreateInfo.maxBlockCount = 2;
18237
18238 VmaPool pool;
18239 res = vmaCreatePool(allocator, &poolCreateInfo, &pool);
18240 // Check res...
18241
18242 // Allocate a buffer out of it.
18243 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18244 bufCreateInfo.size = 1024;
18245 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18246
18247 VmaAllocationCreateInfo allocCreateInfo = {};
18248 allocCreateInfo.pool = pool;
18249
18250 VkBuffer buf;
18251 VmaAllocation alloc;
18252 res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
18253 // Check res...
18254 \endcode
18255
18256 You have to free all allocations made from this pool before destroying it.
18257
18258 \code
18259 vmaDestroyBuffer(allocator, buf, alloc);
18260 vmaDestroyPool(allocator, pool);
18261 \endcode
18262
18263 New versions of this library support creating dedicated allocations in custom pools.
18264 It is supported only when VmaPoolCreateInfo::blockSize = 0.
18265 To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and
18266 VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
18267
18268 \note Excessive use of custom pools is a common mistake when using this library.
18269 Custom pools may be useful for special purposes - when you want to
18270 keep certain type of resources separate e.g. to reserve minimum amount of memory
18271 for them or limit maximum amount of memory they can occupy. For most
18272 resources this is not needed and so it is not recommended to create #VmaPool
18273 objects and allocations out of them. Allocating from the default pool is sufficient.
18274
18275
18276 \section custom_memory_pools_MemTypeIndex Choosing memory type index
18277
18278 When creating a pool, you must explicitly specify memory type index.
18279 To find the one suitable for your buffers or images, you can use helper functions
18280 vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
18281 You need to provide structures with example parameters of buffers or images
18282 that you are going to create in that pool.
18283
18284 \code
18285 VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
18286 exampleBufCreateInfo.size = 1024; // Doesn't matter
18287 exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
18288
18289 VmaAllocationCreateInfo allocCreateInfo = {};
18290 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18291
18292 uint32_t memTypeIndex;
18293 vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
18294
18295 VmaPoolCreateInfo poolCreateInfo = {};
18296 poolCreateInfo.memoryTypeIndex = memTypeIndex;
18297 // ...
18298 \endcode
18299
18300 When creating buffers/images allocated in that pool, provide following parameters:
18301
18302 - `VkBufferCreateInfo`: Prefer to pass same parameters as above.
18303 Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
18304 Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
18305 or the other way around.
18306 - VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
18307 Other members are ignored anyway.
18308
18309 \section linear_algorithm Linear allocation algorithm
18310
18311 Each Vulkan memory block managed by this library has accompanying metadata that
18312 keeps track of used and unused regions. By default, the metadata structure and
18313 algorithm tries to find best place for new allocations among free regions to
18314 optimize memory usage. This way you can allocate and free objects in any order.
18315
18316 
18317
18318 Sometimes there is a need to use simpler, linear allocation algorithm. You can
18319 create custom pool that uses such algorithm by adding flag
18320 #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
18321 #VmaPool object. Then an alternative metadata management is used. It always
18322 creates new allocations after last one and doesn't reuse free regions after
18323 allocations freed in the middle. It results in better allocation performance and
18324 less memory consumed by metadata.
18325
18326 
18327
18328 With this one flag, you can create a custom pool that can be used in many ways:
18329 free-at-once, stack, double stack, and ring buffer. See below for details.
18330 You don't need to specify explicitly which of these options you are going to use - it is detected automatically.
18331
18332 \subsection linear_algorithm_free_at_once Free-at-once
18333
18334 In a pool that uses linear algorithm, you still need to free all the allocations
18335 individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
18336 them in any order. New allocations are always made after last one - free space
18337 in the middle is not reused. However, when you release all the allocation and
18338 the pool becomes empty, allocation starts from the beginning again. This way you
18339 can use linear algorithm to speed up creation of allocations that you are going
18340 to release all at once.
18341
18342 
18343
18344 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
18345 value that allows multiple memory blocks.
18346
18347 \subsection linear_algorithm_stack Stack
18348
18349 When you free an allocation that was created last, its space can be reused.
18350 Thanks to this, if you always release allocations in the order opposite to their
18351 creation (LIFO - Last In First Out), you can achieve behavior of a stack.
18352
18353 
18354
18355 This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
18356 value that allows multiple memory blocks.
18357
18358 \subsection linear_algorithm_double_stack Double stack
18359
18360 The space reserved by a custom pool with linear algorithm may be used by two
18361 stacks:
18362
18363 - First, default one, growing up from offset 0.
18364 - Second, "upper" one, growing down from the end towards lower offsets.
18365
18366 To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
18367 to VmaAllocationCreateInfo::flags.
18368
18369 
18370
18371 Double stack is available only in pools with one memory block -
18372 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
18373
18374 When the two stacks' ends meet so there is not enough space between them for a
18375 new allocation, such allocation fails with usual
18376 `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
18377
18378 \subsection linear_algorithm_ring_buffer Ring buffer
18379
18380 When you free some allocations from the beginning and there is not enough free space
18381 for a new one at the end of a pool, allocator's "cursor" wraps around to the
18382 beginning and starts allocation there. Thanks to this, if you always release
18383 allocations in the same order as you created them (FIFO - First In First Out),
18384 you can achieve behavior of a ring buffer / queue.
18385
18386 
18387
18388 Ring buffer is available only in pools with one memory block -
18389 VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
18390
18391 \note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.
18392
18393
18394 \page defragmentation Defragmentation
18395
18396 Interleaved allocations and deallocations of many objects of varying size can
18397 cause fragmentation over time, which can lead to a situation where the library is unable
18398 to find a continuous range of free memory for a new allocation despite there is
18399 enough free space, just scattered across many small free ranges between existing
18400 allocations.
18401
18402 To mitigate this problem, you can use defragmentation feature.
18403 It doesn't happen automatically though and needs your cooperation,
18404 because VMA is a low level library that only allocates memory.
18405 It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures.
18406 It cannot copy their contents as it doesn't record any commands to a command buffer.
18407
18408 Example:
18409
18410 \code
18411 VmaDefragmentationInfo defragInfo = {};
18412 defragInfo.pool = myPool;
18413 defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT;
18414
18415 VmaDefragmentationContext defragCtx;
18416 VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx);
18417 // Check res...
18418
18419 for(;;)
18420 {
18421 VmaDefragmentationPassMoveInfo pass;
18422 res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass);
18423 if(res == VK_SUCCESS)
18424 break;
18425 else if(res != VK_INCOMPLETE)
18426 // Handle error...
18427
18428 for(uint32_t i = 0; i < pass.moveCount; ++i)
18429 {
18430 // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents.
18431 VmaAllocationInfo allocInfo;
18432 vmaGetAllocationInfo(allocator, pMoves[i].srcAllocation, &allocInfo);
18433 MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData;
18434
18435 // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset.
18436 VkImageCreateInfo imgCreateInfo = ...
18437 VkImage newImg;
18438 res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg);
18439 // Check res...
18440 res = vmaBindImageMemory(allocator, pMoves[i].dstTmpAllocation, newImg);
18441 // Check res...
18442
18443 // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place.
18444 vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...);
18445 }
18446
18447 // Make sure the copy commands finished executing.
18448 vkWaitForFences(...);
18449
18450 // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation.
18451 for(uint32_t i = 0; i < pass.moveCount; ++i)
18452 {
18453 // ...
18454 vkDestroyImage(device, resData->img, nullptr);
18455 }
18456
18457 // Update appropriate descriptors to point to the new places...
18458
18459 res = vmaEndDefragmentationPass(allocator, defragCtx, &pass);
18460 if(res == VK_SUCCESS)
18461 break;
18462 else if(res != VK_INCOMPLETE)
18463 // Handle error...
18464 }
18465
18466 vmaEndDefragmentation(allocator, defragCtx, nullptr);
18467 \endcode
18468
18469 Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage()
18470 create/destroy an allocation and a buffer/image at once, these are just a shortcut for
18471 creating the resource, allocating memory, and binding them together.
18472 Defragmentation works on memory allocations only. You must handle the rest manually.
18473 Defragmentation is an iterative process that should repreat "passes" as long as related functions
18474 return `VK_INCOMPLETE` not `VK_SUCCESS`.
18475 In each pass:
18476
18477 1. vmaBeginDefragmentationPass() function call:
18478 - Calculates and returns the list of allocations to be moved in this pass.
18479 Note this can be a time-consuming process.
18480 - Reserves destination memory for them by creating temporary destination allocations
18481 that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo().
18482 2. Inside the pass, **you should**:
18483 - Inspect the returned list of allocations to be moved.
18484 - Create new buffers/images and bind them at the returned destination temporary allocations.
18485 - Copy data from source to destination resources if necessary.
18486 - Destroy the source buffers/images, but NOT their allocations.
18487 3. vmaEndDefragmentationPass() function call:
18488 - Frees the source memory reserved for the allocations that are moved.
18489 - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory.
18490 - Frees `VkDeviceMemory` blocks that became empty.
18491
18492 Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter.
18493 Defragmentation algorithm tries to move all suitable allocations.
18494 You can, however, refuse to move some of them inside a defragmentation pass, by setting
18495 `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
18496 This is not recommended and may result in suboptimal packing of the allocations after defragmentation.
18497 If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.
18498
18499 Inside a pass, for each allocation that should be moved:
18500
18501 - You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`.
18502 - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass().
18503 - If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared,
18504 filled, and used temporarily in each rendering frame, you can just recreate this image
18505 without copying its data.
18506 - If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU
18507 using `memcpy()`.
18508 - If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.
18509 This will cancel the move.
18510 - vmaEndDefragmentationPass() will then free the destination memory
18511 not the source memory of the allocation, leaving it unchanged.
18512 - If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time),
18513 you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.
18514 - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object.
18515
18516 You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool
18517 (like in the example above) or all the default pools by setting this member to null.
18518
18519 Defragmentation is always performed in each pool separately.
18520 Allocations are never moved between different Vulkan memory types.
18521 The size of the destination memory reserved for a moved allocation is the same as the original one.
18522 Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation.
18523 Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones.
18524
18525 You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved
18526 in each pass, e.g. to call it in sync with render frames and not to experience too big hitches.
18527 See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass.
18528
18529 It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA
18530 usage, possibly from multiple threads, with the exception that allocations
18531 returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended.
18532
18533 <b>Mapping</b> is preserved on allocations that are moved during defragmentation.
18534 Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations
18535 are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried
18536 using VmaAllocationInfo::pMappedData.
18537
18538 \note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.
18539
18540
18541 \page statistics Statistics
18542
18543 This library contains several functions that return information about its internal state,
18544 especially the amount of memory allocated from Vulkan.
18545
18546 \section statistics_numeric_statistics Numeric statistics
18547
18548 If you need to obtain basic statistics about memory usage per heap, together with current budget,
18549 you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget.
18550 This is useful to keep track of memory usage and stay withing budget
18551 (see also \ref staying_within_budget).
18552 Example:
18553
18554 \code
18555 uint32_t heapIndex = ...
18556
18557 VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
18558 vmaGetHeapBudgets(allocator, budgets);
18559
18560 printf("My heap currently has %u allocations taking %llu B,\n",
18561 budgets[heapIndex].statistics.allocationCount,
18562 budgets[heapIndex].statistics.allocationBytes);
18563 printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n",
18564 budgets[heapIndex].statistics.blockCount,
18565 budgets[heapIndex].statistics.blockBytes);
18566 printf("Vulkan reports total usage %llu B with budget %llu B.\n",
18567 budgets[heapIndex].usage,
18568 budgets[heapIndex].budget);
18569 \endcode
18570
18571 You can query for more detailed statistics per memory heap, type, and totals,
18572 including minimum and maximum allocation size and unused range size,
18573 by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics.
18574 This function is slower though, as it has to traverse all the internal data structures,
18575 so it should be used only for debugging purposes.
18576
18577 You can query for statistics of a custom pool using function vmaGetPoolStatistics()
18578 or vmaCalculatePoolStatistics().
18579
18580 You can query for information about a specific allocation using function vmaGetAllocationInfo().
18581 It fill structure #VmaAllocationInfo.
18582
18583 \section statistics_json_dump JSON dump
18584
18585 You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
18586 The result is guaranteed to be correct JSON.
18587 It uses ANSI encoding.
18588 Any strings provided by user (see [Allocation names](@ref allocation_names))
18589 are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
18590 this JSON string can be treated as using this encoding.
18591 It must be freed using function vmaFreeStatsString().
18592
18593 The format of this JSON string is not part of official documentation of the library,
18594 but it will not change in backward-incompatible way without increasing library major version number
18595 and appropriate mention in changelog.
18596
18597 The JSON string contains all the data that can be obtained using vmaCalculateStatistics().
18598 It can also contain detailed map of allocated memory blocks and their regions -
18599 free and occupied by allocations.
18600 This allows e.g. to visualize the memory or assess fragmentation.
18601
18602
18603 \page allocation_annotation Allocation names and user data
18604
18605 \section allocation_user_data Allocation user data
18606
18607 You can annotate allocations with your own information, e.g. for debugging purposes.
18608 To do that, fill VmaAllocationCreateInfo::pUserData field when creating
18609 an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,
18610 some handle, index, key, ordinal number or any other value that would associate
18611 the allocation with your custom metadata.
18612 It it useful to identify appropriate data structures in your engine given #VmaAllocation,
18613 e.g. when doing \ref defragmentation.
18614
18615 \code
18616 VkBufferCreateInfo bufCreateInfo = ...
18617
18618 MyBufferMetadata* pMetadata = CreateBufferMetadata();
18619
18620 VmaAllocationCreateInfo allocCreateInfo = {};
18621 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
18622 allocCreateInfo.pUserData = pMetadata;
18623
18624 VkBuffer buffer;
18625 VmaAllocation allocation;
18626 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
18627 \endcode
18628
18629 The pointer may be later retrieved as VmaAllocationInfo::pUserData:
18630
18631 \code
18632 VmaAllocationInfo allocInfo;
18633 vmaGetAllocationInfo(allocator, allocation, &allocInfo);
18634 MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
18635 \endcode
18636
18637 It can also be changed using function vmaSetAllocationUserData().
18638
18639 Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
18640 vmaBuildStatsString() in hexadecimal form.
18641
18642 \section allocation_names Allocation names
18643
18644 An allocation can also carry a null-terminated string, giving a name to the allocation.
18645 To set it, call vmaSetAllocationName().
18646 The library creates internal copy of the string, so the pointer you pass doesn't need
18647 to be valid for whole lifetime of the allocation. You can free it after the call.
18648
18649 \code
18650 std::string imageName = "Texture: ";
18651 imageName += fileName;
18652 vmaSetAllocationName(allocator, allocation, imageName.c_str());
18653 \endcode
18654
18655 The string can be later retrieved by inspecting VmaAllocationInfo::pName.
18656 It is also printed in JSON report created by vmaBuildStatsString().
18657
18658 \note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.
18659 You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.
18660
18661
18662 \page virtual_allocator Virtual allocator
18663
18664 As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".
18665 It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".
18666 You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.
18667 A common use case is sub-allocation of pieces of one large GPU buffer.
18668
18669 \section virtual_allocator_creating_virtual_block Creating virtual block
18670
18671 To use this functionality, there is no main "allocator" object.
18672 You don't need to have #VmaAllocator object created.
18673 All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator:
18674
18675 -# Fill in #VmaVirtualBlockCreateInfo structure.
18676 -# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.
18677
18678 Example:
18679
18680 \code
18681 VmaVirtualBlockCreateInfo blockCreateInfo = {};
18682 blockCreateInfo.size = 1048576; // 1 MB
18683
18684 VmaVirtualBlock block;
18685 VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);
18686 \endcode
18687
18688 \section virtual_allocator_making_virtual_allocations Making virtual allocations
18689
18690 #VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions
18691 using the same code as the main Vulkan memory allocator.
18692 Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type
18693 that represents an opaque handle to an allocation withing the virtual block.
18694
18695 In order to make such allocation:
18696
18697 -# Fill in #VmaVirtualAllocationCreateInfo structure.
18698 -# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation.
18699 You can also receive `VkDeviceSize offset` that was assigned to the allocation.
18700
18701 Example:
18702
18703 \code
18704 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
18705 allocCreateInfo.size = 4096; // 4 KB
18706
18707 VmaVirtualAllocation alloc;
18708 VkDeviceSize offset;
18709 res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset);
18710 if(res == VK_SUCCESS)
18711 {
18712 // Use the 4 KB of your memory starting at offset.
18713 }
18714 else
18715 {
18716 // Allocation failed - no space for it could be found. Handle this error!
18717 }
18718 \endcode
18719
18720 \section virtual_allocator_deallocation Deallocation
18721
18722 When no longer needed, an allocation can be freed by calling vmaVirtualFree().
18723 You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate()
18724 called for the same #VmaVirtualBlock.
18725
18726 When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().
18727 All allocations must be freed before the block is destroyed, which is checked internally by an assert.
18728 However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once -
18729 a feature not available in normal Vulkan memory allocator. Example:
18730
18731 \code
18732 vmaVirtualFree(block, alloc);
18733 vmaDestroyVirtualBlock(block);
18734 \endcode
18735
18736 \section virtual_allocator_allocation_parameters Allocation parameters
18737
18738 You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().
18739 Its default value is null.
18740 It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some
18741 larger data structure containing more information. Example:
18742
18743 \code
18744 struct CustomAllocData
18745 {
18746 std::string m_AllocName;
18747 };
18748 CustomAllocData* allocData = new CustomAllocData();
18749 allocData->m_AllocName = "My allocation 1";
18750 vmaSetVirtualAllocationUserData(block, alloc, allocData);
18751 \endcode
18752
18753 The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function
18754 vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.
18755 If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!
18756 Example:
18757
18758 \code
18759 VmaVirtualAllocationInfo allocInfo;
18760 vmaGetVirtualAllocationInfo(block, alloc, &allocInfo);
18761 delete (CustomAllocData*)allocInfo.pUserData;
18762
18763 vmaVirtualFree(block, alloc);
18764 \endcode
18765
18766 \section virtual_allocator_alignment_and_units Alignment and units
18767
18768 It feels natural to express sizes and offsets in bytes.
18769 If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member
18770 VmaVirtualAllocationCreateInfo::alignment to request it. Example:
18771
18772 \code
18773 VmaVirtualAllocationCreateInfo allocCreateInfo = {};
18774 allocCreateInfo.size = 4096; // 4 KB
18775 allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B
18776
18777 VmaVirtualAllocation alloc;
18778 res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr);
18779 \endcode
18780
18781 Alignments of different allocations made from one block may vary.
18782 However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,
18783 you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.
18784 It might be more convenient, but you need to make sure to use this new unit consistently in all the places:
18785
18786 - VmaVirtualBlockCreateInfo::size
18787 - VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment
18788 - Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset
18789
18790 \section virtual_allocator_statistics Statistics
18791
18792 You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics()
18793 (to get brief statistics that are fast to calculate)
18794 or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate).
18795 The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator.
18796 Example:
18797
18798 \code
18799 VmaStatistics stats;
18800 vmaGetVirtualBlockStatistics(block, &stats);
18801 printf("My virtual block has %llu bytes used by %u virtual allocations\n",
18802 stats.allocationBytes, stats.allocationCount);
18803 \endcode
18804
18805 You can also request a full list of allocations and free regions as a string in JSON format by calling
18806 vmaBuildVirtualBlockStatsString().
18807 Returned string must be later freed using vmaFreeVirtualBlockStatsString().
18808 The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.
18809
18810 \section virtual_allocator_additional_considerations Additional considerations
18811
18812 The "virtual allocator" functionality is implemented on a level of individual memory blocks.
18813 Keeping track of a whole collection of blocks, allocating new ones when out of free space,
18814 deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.
18815
18816 Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.
18817 See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).
18818 You can find their description in chapter \ref custom_memory_pools.
18819 Allocation strategies are also supported.
18820 See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).
18821
18822 Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:
18823 buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.
18824
18825
18826 \page debugging_memory_usage Debugging incorrect memory usage
18827
18828 If you suspect a bug with memory usage, like usage of uninitialized memory or
18829 memory being overwritten out of bounds of an allocation,
18830 you can use debug features of this library to verify this.
18831
18832 \section debugging_memory_usage_initialization Memory initialization
18833
18834 If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
18835 you can enable automatic memory initialization to verify this.
18836 To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
18837
18838 \code
18839 #define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
18840 #include "vk_mem_alloc.h"
18841 \endcode
18842
18843 It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`.
18844 Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
18845 Memory is automatically mapped and unmapped if necessary.
18846
18847 If you find these values while debugging your program, good chances are that you incorrectly
18848 read Vulkan memory that is allocated but not initialized, or already freed, respectively.
18849
18850 Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped.
18851 It works also with dedicated allocations.
18852
18853 \section debugging_memory_usage_margins Margins
18854
18855 By default, allocations are laid out in memory blocks next to each other if possible
18856 (considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
18857
18858 
18859
18860 Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
18861 number of bytes as a margin after every allocation.
18862
18863 \code
18864 #define VMA_DEBUG_MARGIN 16
18865 #include "vk_mem_alloc.h"
18866 \endcode
18867
18868 
18869
18870 If your bug goes away after enabling margins, it means it may be caused by memory
18871 being overwritten outside of allocation boundaries. It is not 100% certain though.
18872 Change in application behavior may also be caused by different order and distribution
18873 of allocations across memory blocks after margins are applied.
18874
18875 Margins work with all types of memory.
18876
18877 Margin is applied only to allocations made out of memory blocks and not to dedicated
18878 allocations, which have their own memory block of specific size.
18879 It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
18880 or those automatically decided to put into dedicated allocations, e.g. due to its
18881 large size or recommended by VK_KHR_dedicated_allocation extension.
18882
18883 Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
18884
18885 Note that enabling margins increases memory usage and fragmentation.
18886
18887 Margins do not apply to \ref virtual_allocator.
18888
18889 \section debugging_memory_usage_corruption_detection Corruption detection
18890
18891 You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
18892 of contents of the margins.
18893
18894 \code
18895 #define VMA_DEBUG_MARGIN 16
18896 #define VMA_DEBUG_DETECT_CORRUPTION 1
18897 #include "vk_mem_alloc.h"
18898 \endcode
18899
18900 When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
18901 (it must be multiply of 4) after every allocation is filled with a magic number.
18902 This idea is also know as "canary".
18903 Memory is automatically mapped and unmapped if necessary.
18904
18905 This number is validated automatically when the allocation is destroyed.
18906 If it is not equal to the expected value, `VMA_ASSERT()` is executed.
18907 It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
18908 which indicates a serious bug.
18909
18910 You can also explicitly request checking margins of all allocations in all memory blocks
18911 that belong to specified memory types by using function vmaCheckCorruption(),
18912 or in memory blocks that belong to specified custom pool, by using function
18913 vmaCheckPoolCorruption().
18914
18915 Margin validation (corruption detection) works only for memory types that are
18916 `HOST_VISIBLE` and `HOST_COHERENT`.
18917
18918
18919 \page opengl_interop OpenGL Interop
18920
18921 VMA provides some features that help with interoperability with OpenGL.
18922
18923 \section opengl_interop_exporting_memory Exporting memory
18924
18925 If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library:
18926
18927 It is recommended to create \ref custom_memory_pools for such allocations.
18928 Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext
18929 while creating the custom pool.
18930 Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,
18931 not only while creating it, as no copy of the structure is made,
18932 but its original pointer is used for each allocation instead.
18933
18934 If you want to export all memory allocated by the library from certain memory types,
18935 also dedicated allocations or other allocations made from default pools,
18936 an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.
18937 It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library
18938 through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.
18939 Please note that new versions of the library also support dedicated allocations created in custom pools.
18940
18941 You should not mix these two methods in a way that allows to apply both to the same memory type.
18942 Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.
18943
18944
18945 \section opengl_interop_custom_alignment Custom alignment
18946
18947 Buffers or images exported to a different API like OpenGL may require a different alignment,
18948 higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.
18949 To impose such alignment:
18950
18951 It is recommended to create \ref custom_memory_pools for such allocations.
18952 Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation
18953 to be made out of this pool.
18954 The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image
18955 from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.
18956
18957 If you want to create a buffer with a specific minimum alignment out of default pools,
18958 use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.
18959
18960 Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated
18961 allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.
18962 Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.
18963
18964
18965 \page usage_patterns Recommended usage patterns
18966
18967 Vulkan gives great flexibility in memory allocation.
18968 This chapter shows the most common patterns.
18969
18970 See also slides from talk:
18971 [Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)
18972
18973
18974 \section usage_patterns_gpu_only GPU-only resource
18975
18976 <b>When:</b>
18977 Any resources that you frequently write and read on GPU,
18978 e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
18979 images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
18980
18981 <b>What to do:</b>
18982 Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
18983
18984 \code
18985 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
18986 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
18987 imgCreateInfo.extent.width = 3840;
18988 imgCreateInfo.extent.height = 2160;
18989 imgCreateInfo.extent.depth = 1;
18990 imgCreateInfo.mipLevels = 1;
18991 imgCreateInfo.arrayLayers = 1;
18992 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
18993 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
18994 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
18995 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
18996 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
18997
18998 VmaAllocationCreateInfo allocCreateInfo = {};
18999 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19000 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
19001 allocCreateInfo.priority = 1.0f;
19002
19003 VkImage img;
19004 VmaAllocation alloc;
19005 vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);
19006 \endcode
19007
19008 <b>Also consider:</b>
19009 Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
19010 especially if they are large or if you plan to destroy and recreate them with different sizes
19011 e.g. when display resolution changes.
19012 Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
19013 When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation
19014 to decrease chances to be evicted to system memory by the operating system.
19015
19016 \section usage_patterns_staging_copy_upload Staging copy for upload
19017
19018 <b>When:</b>
19019 A "staging" buffer than you want to map and fill from CPU code, then use as a source od transfer
19020 to some GPU resource.
19021
19022 <b>What to do:</b>
19023 Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT.
19024 Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`.
19025
19026 \code
19027 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19028 bufCreateInfo.size = 65536;
19029 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
19030
19031 VmaAllocationCreateInfo allocCreateInfo = {};
19032 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19033 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
19034 VMA_ALLOCATION_CREATE_MAPPED_BIT;
19035
19036 VkBuffer buf;
19037 VmaAllocation alloc;
19038 VmaAllocationInfo allocInfo;
19039 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19040
19041 ...
19042
19043 memcpy(allocInfo.pMappedData, myData, myDataSize);
19044 \endcode
19045
19046 <b>Also consider:</b>
19047 You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped
19048 using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above.
19049
19050
19051 \section usage_patterns_readback Readback
19052
19053 <b>When:</b>
19054 Buffers for data written by or transferred from the GPU that you want to read back on the CPU,
19055 e.g. results of some computations.
19056
19057 <b>What to do:</b>
19058 Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
19059 Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
19060 and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.
19061
19062 \code
19063 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19064 bufCreateInfo.size = 65536;
19065 bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19066
19067 VmaAllocationCreateInfo allocCreateInfo = {};
19068 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19069 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |
19070 VMA_ALLOCATION_CREATE_MAPPED_BIT;
19071
19072 VkBuffer buf;
19073 VmaAllocation alloc;
19074 VmaAllocationInfo allocInfo;
19075 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19076
19077 ...
19078
19079 const float* downloadedData = (const float*)allocInfo.pMappedData;
19080 \endcode
19081
19082
19083 \section usage_patterns_advanced_data_uploading Advanced data uploading
19084
19085 For resources that you frequently write on CPU via mapped pointer and
19086 freqnently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible:
19087
19088 -# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory,
19089 even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card,
19090 and make the device reach out to that resource directly.
19091 - Reads performed by the device will then go through PCI Express bus.
19092 The performace of this access may be limited, but it may be fine depending on the size
19093 of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity
19094 of access.
19095 -# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips),
19096 a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL`
19097 (fast to access from the GPU). Then, it is likely the best choice for such type of resource.
19098 -# Systems with a discrete graphics card and separate video memory may or may not expose
19099 a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR).
19100 If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS)
19101 that is available to CPU for mapping.
19102 - Writes performed by the host to that memory go through PCI Express bus.
19103 The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0,
19104 as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads.
19105 -# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory,
19106 a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them.
19107
19108 Thankfully, VMA offers an aid to create and use such resources in the the way optimal
19109 for the current Vulkan device. To help the library make the best choice,
19110 use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with
19111 #VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT.
19112 It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR),
19113 but if no such memory type is available or allocation from it fails
19114 (PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS),
19115 it will fall back to `DEVICE_LOCAL` memory for fast GPU access.
19116 It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`,
19117 so you need to create another "staging" allocation and perform explicit transfers.
19118
19119 \code
19120 VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19121 bufCreateInfo.size = 65536;
19122 bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
19123
19124 VmaAllocationCreateInfo allocCreateInfo = {};
19125 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19126 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
19127 VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
19128 VMA_ALLOCATION_CREATE_MAPPED_BIT;
19129
19130 VkBuffer buf;
19131 VmaAllocation alloc;
19132 VmaAllocationInfo allocInfo;
19133 vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
19134
19135 VkMemoryPropertyFlags memPropFlags;
19136 vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);
19137
19138 if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
19139 {
19140 // Allocation ended up in a mappable memory and is already mapped - write to it directly.
19141
19142 // [Executed in runtime]:
19143 memcpy(allocInfo.pMappedData, myData, myDataSize);
19144 }
19145 else
19146 {
19147 // Allocation ended up in a non-mappable memory - need to transfer.
19148 VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
19149 stagingBufCreateInfo.size = 65536;
19150 stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
19151
19152 VmaAllocationCreateInfo stagingAllocCreateInfo = {};
19153 stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19154 stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
19155 VMA_ALLOCATION_CREATE_MAPPED_BIT;
19156
19157 VkBuffer stagingBuf;
19158 VmaAllocation stagingAlloc;
19159 VmaAllocationInfo stagingAllocInfo;
19160 vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,
19161 &stagingBuf, &stagingAlloc, stagingAllocInfo);
19162
19163 // [Executed in runtime]:
19164 memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);
19165 //vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT
19166 VkBufferCopy bufCopy = {
19167 0, // srcOffset
19168 0, // dstOffset,
19169 myDataSize); // size
19170 vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);
19171 }
19172 \endcode
19173
19174 \section usage_patterns_other_use_cases Other use cases
19175
19176 Here are some other, less obvious use cases and their recommended settings:
19177
19178 - An image that is used only as transfer source and destination, but it should stay on the device,
19179 as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame,
19180 for temporal antialiasing or other temporal effects.
19181 - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`
19182 - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO
19183 - An image that is used only as transfer source and destination, but it should be placed
19184 in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict
19185 least recently used textures from VRAM.
19186 - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`
19187 - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST,
19188 as VMA needs a hint here to differentiate from the previous case.
19189 - A buffer that you want to map and write from the CPU, directly read from the GPU
19190 (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or
19191 host memory due to its large size.
19192 - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT`
19193 - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST
19194 - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT
19195
19196
19197 \page configuration Configuration
19198
19199 Please check "CONFIGURATION SECTION" in the code to find macros that you can define
19200 before each include of this file or change directly in this file to provide
19201 your own implementation of basic facilities like assert, `min()` and `max()` functions,
19202 mutex, atomic etc.
19203 The library uses its own implementation of containers by default, but you can switch to using
19204 STL containers instead.
19205
19206 For example, define `VMA_ASSERT(expr)` before including the library to provide
19207 custom implementation of the assertion, compatible with your project.
19208 By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration
19209 and empty otherwise.
19210
19211 \section config_Vulkan_functions Pointers to Vulkan functions
19212
19213 There are multiple ways to import pointers to Vulkan functions in the library.
19214 In the simplest case you don't need to do anything.
19215 If the compilation or linking of your program or the initialization of the #VmaAllocator
19216 doesn't work for you, you can try to reconfigure it.
19217
19218 First, the allocator tries to fetch pointers to Vulkan functions linked statically,
19219 like this:
19220
19221 \code
19222 m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;
19223 \endcode
19224
19225 If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.
19226
19227 Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.
19228 You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or
19229 by using a helper library like [volk](https://github.com/zeux/volk).
19230
19231 Third, VMA tries to fetch remaining pointers that are still null by calling
19232 `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.
19233 You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr.
19234 Other pointers will be fetched automatically.
19235 If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.
19236
19237 Finally, all the function pointers required by the library (considering selected
19238 Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.
19239
19240
19241 \section custom_memory_allocator Custom host memory allocator
19242
19243 If you use custom allocator for CPU memory rather than default operator `new`
19244 and `delete` from C++, you can make this library using your allocator as well
19245 by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
19246 functions will be passed to Vulkan, as well as used by the library itself to
19247 make any CPU-side allocations.
19248
19249 \section allocation_callbacks Device memory allocation callbacks
19250
19251 The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
19252 You can setup callbacks to be informed about these calls, e.g. for the purpose
19253 of gathering some statistics. To do it, fill optional member
19254 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
19255
19256 \section heap_memory_limit Device heap memory limit
19257
19258 When device memory of certain heap runs out of free space, new allocations may
19259 fail (returning error code) or they may succeed, silently pushing some existing_
19260 memory blocks from GPU VRAM to system RAM (which degrades performance). This
19261 behavior is implementation-dependent - it depends on GPU vendor and graphics
19262 driver.
19263
19264 On AMD cards it can be controlled while creating Vulkan device object by using
19265 VK_AMD_memory_overallocation_behavior extension, if available.
19266
19267 Alternatively, if you want to test how your program behaves with limited amount of Vulkan device
19268 memory available without switching your graphics card to one that really has
19269 smaller VRAM, you can use a feature of this library intended for this purpose.
19270 To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
19271
19272
19273
19274 \page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
19275
19276 VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
19277 performance on some GPUs. It augments Vulkan API with possibility to query
19278 driver whether it prefers particular buffer or image to have its own, dedicated
19279 allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
19280 to do some internal optimizations. The extension is supported by this library.
19281 It will be used automatically when enabled.
19282
19283 It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version
19284 and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion,
19285 you are all set.
19286
19287 Otherwise, if you want to use it as an extension:
19288
19289 1 . When creating Vulkan device, check if following 2 device extensions are
19290 supported (call `vkEnumerateDeviceExtensionProperties()`).
19291 If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
19292
19293 - VK_KHR_get_memory_requirements2
19294 - VK_KHR_dedicated_allocation
19295
19296 If you enabled these extensions:
19297
19298 2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
19299 your #VmaAllocator to inform the library that you enabled required extensions
19300 and you want the library to use them.
19301
19302 \code
19303 allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
19304
19305 vmaCreateAllocator(&allocatorInfo, &allocator);
19306 \endcode
19307
19308 That is all. The extension will be automatically used whenever you create a
19309 buffer using vmaCreateBuffer() or image using vmaCreateImage().
19310
19311 When using the extension together with Vulkan Validation Layer, you will receive
19312 warnings like this:
19313
19314 _vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._
19315
19316 It is OK, you should just ignore it. It happens because you use function
19317 `vkGetBufferMemoryRequirements2KHR()` instead of standard
19318 `vkGetBufferMemoryRequirements()`, while the validation layer seems to be
19319 unaware of it.
19320
19321 To learn more about this extension, see:
19322
19323 - [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)
19324 - [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
19325
19326
19327
19328 \page vk_ext_memory_priority VK_EXT_memory_priority
19329
19330 VK_EXT_memory_priority is a device extension that allows to pass additional "priority"
19331 value to Vulkan memory allocations that the implementation may use prefer certain
19332 buffers and images that are critical for performance to stay in device-local memory
19333 in cases when the memory is over-subscribed, while some others may be moved to the system memory.
19334
19335 VMA offers convenient usage of this extension.
19336 If you enable it, you can pass "priority" parameter when creating allocations or custom pools
19337 and the library automatically passes the value to Vulkan using this extension.
19338
19339 If you want to use this extension in connection with VMA, follow these steps:
19340
19341 \section vk_ext_memory_priority_initialization Initialization
19342
19343 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
19344 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority".
19345
19346 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
19347 Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
19348 Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true.
19349
19350 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority"
19351 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
19352
19353 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
19354 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
19355 Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to
19356 `VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`.
19357
19358 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
19359 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
19360 to VmaAllocatorCreateInfo::flags.
19361
19362 \section vk_ext_memory_priority_usage Usage
19363
19364 When using this extension, you should initialize following member:
19365
19366 - VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
19367 - VmaPoolCreateInfo::priority when creating a custom pool.
19368
19369 It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`.
19370 Memory allocated with higher value can be treated by the Vulkan implementation as higher priority
19371 and so it can have lower chances of being pushed out to system memory, experiencing degraded performance.
19372
19373 It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images
19374 as dedicated and set high priority to them. For example:
19375
19376 \code
19377 VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
19378 imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
19379 imgCreateInfo.extent.width = 3840;
19380 imgCreateInfo.extent.height = 2160;
19381 imgCreateInfo.extent.depth = 1;
19382 imgCreateInfo.mipLevels = 1;
19383 imgCreateInfo.arrayLayers = 1;
19384 imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
19385 imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
19386 imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
19387 imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
19388 imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
19389
19390 VmaAllocationCreateInfo allocCreateInfo = {};
19391 allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
19392 allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
19393 allocCreateInfo.priority = 1.0f;
19394
19395 VkImage img;
19396 VmaAllocation alloc;
19397 vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);
19398 \endcode
19399
19400 `priority` member is ignored in the following situations:
19401
19402 - Allocations created in custom pools: They inherit the priority, along with all other allocation parameters
19403 from the parametrs passed in #VmaPoolCreateInfo when the pool was created.
19404 - Allocations created in default pools: They inherit the priority from the parameters
19405 VMA used when creating default pools, which means `priority == 0.5f`.
19406
19407
19408 \page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory
19409
19410 VK_AMD_device_coherent_memory is a device extension that enables access to
19411 additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and
19412 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for
19413 allocation of buffers intended for writing "breadcrumb markers" in between passes
19414 or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.
19415
19416 When the extension is available but has not been enabled, Vulkan physical device
19417 still exposes those memory types, but their usage is forbidden. VMA automatically
19418 takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt
19419 to allocate memory of such type is made.
19420
19421 If you want to use this extension in connection with VMA, follow these steps:
19422
19423 \section vk_amd_device_coherent_memory_initialization Initialization
19424
19425 1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
19426 Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".
19427
19428 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
19429 Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
19430 Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.
19431
19432 3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"
19433 to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
19434
19435 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
19436 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
19437 Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to
19438 `VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.
19439
19440 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
19441 have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT
19442 to VmaAllocatorCreateInfo::flags.
19443
19444 \section vk_amd_device_coherent_memory_usage Usage
19445
19446 After following steps described above, you can create VMA allocations and custom pools
19447 out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible
19448 devices. There are multiple ways to do it, for example:
19449
19450 - You can request or prefer to allocate out of such memory types by adding
19451 `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags
19452 or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with
19453 other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.
19454 - If you manually found memory type index to use for this purpose, force allocation
19455 from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.
19456
19457 \section vk_amd_device_coherent_memory_more_information More information
19458
19459 To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html)
19460
19461 Example use of this extension can be found in the code of the sample and test suite
19462 accompanying this library.
19463
19464
19465 \page enabling_buffer_device_address Enabling buffer device address
19466
19467 Device extension VK_KHR_buffer_device_address
19468 allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code.
19469 It has been promoted to core Vulkan 1.2.
19470
19471 If you want to use this feature in connection with VMA, follow these steps:
19472
19473 \section enabling_buffer_device_address_initialization Initialization
19474
19475 1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device.
19476 Check if the extension is supported - if returned array of `VkExtensionProperties` contains
19477 "VK_KHR_buffer_device_address".
19478
19479 2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.
19480 Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned.
19481 Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true.
19482
19483 3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add
19484 "VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.
19485
19486 4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.
19487 Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.
19488 Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to
19489 `VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`.
19490
19491 5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you
19492 have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT
19493 to VmaAllocatorCreateInfo::flags.
19494
19495 \section enabling_buffer_device_address_usage Usage
19496
19497 After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA.
19498 The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to
19499 allocated memory blocks wherever it might be needed.
19500
19501 Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`.
19502 The second part of this functionality related to "capture and replay" is not supported,
19503 as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage.
19504
19505 \section enabling_buffer_device_address_more_information More information
19506
19507 To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address)
19508
19509 Example use of this extension can be found in the code of the sample and test suite
19510 accompanying this library.
19511
19512 \page general_considerations General considerations
19513
19514 \section general_considerations_thread_safety Thread safety
19515
19516 - The library has no global state, so separate #VmaAllocator objects can be used
19517 independently.
19518 There should be no need to create multiple such objects though - one per `VkDevice` is enough.
19519 - By default, all calls to functions that take #VmaAllocator as first parameter
19520 are safe to call from multiple threads simultaneously because they are
19521 synchronized internally when needed.
19522 This includes allocation and deallocation from default memory pool, as well as custom #VmaPool.
19523 - When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
19524 flag, calls to functions that take such #VmaAllocator object must be
19525 synchronized externally.
19526 - Access to a #VmaAllocation object must be externally synchronized. For example,
19527 you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
19528 threads at the same time if you pass the same #VmaAllocation object to these
19529 functions.
19530 - #VmaVirtualBlock is not safe to be used from multiple threads simultaneously.
19531
19532 \section general_considerations_versioning_and_compatibility Versioning and compatibility
19533
19534 The library uses [**Semantic Versioning**](https://semver.org/),
19535 which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where:
19536
19537 - Incremented Patch version means a release is backward- and forward-compatible,
19538 introducing only some internal improvements, bug fixes, optimizations etc.
19539 or changes that are out of scope of the official API described in this documentation.
19540 - Incremented Minor version means a release is backward-compatible,
19541 so existing code that uses the library should continue to work, while some new
19542 symbols could have been added: new structures, functions, new values in existing
19543 enums and bit flags, new structure members, but not new function parameters.
19544 - Incrementing Major version means a release could break some backward compatibility.
19545
19546 All changes between official releases are documented in file "CHANGELOG.md".
19547
19548 \warning Backward compatiblity is considered on the level of C++ source code, not binary linkage.
19549 Adding new members to existing structures is treated as backward compatible if initializing
19550 the new members to binary zero results in the old behavior.
19551 You should always fully initialize all library structures to zeros and not rely on their
19552 exact binary size.
19553
19554 \section general_considerations_validation_layer_warnings Validation layer warnings
19555
19556 When using this library, you can meet following types of warnings issued by
19557 Vulkan validation layer. They don't necessarily indicate a bug, so you may need
19558 to just ignore them.
19559
19560 - *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
19561 - It happens when VK_KHR_dedicated_allocation extension is enabled.
19562 `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
19563 - *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.*
19564 - It happens when you map a buffer or image, because the library maps entire
19565 `VkDeviceMemory` block, where different types of images and buffers may end
19566 up together, especially on GPUs with unified memory like Intel.
19567 - *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
19568 - It may happen when you use [defragmentation](@ref defragmentation).
19569
19570 \section general_considerations_allocation_algorithm Allocation algorithm
19571
19572 The library uses following algorithm for allocation, in order:
19573
19574 -# Try to find free range of memory in existing blocks.
19575 -# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
19576 -# If failed, try to create such block with size / 2, size / 4, size / 8.
19577 -# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
19578 just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
19579 -# If failed, choose other memory type that meets the requirements specified in
19580 VmaAllocationCreateInfo and go to point 1.
19581 -# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
19582
19583 \section general_considerations_features_not_supported Features not supported
19584
19585 Features deliberately excluded from the scope of this library:
19586
19587 -# **Data transfer.** Uploading (streaming) and downloading data of buffers and images
19588 between CPU and GPU memory and related synchronization is responsibility of the user.
19589 Defining some "texture" object that would automatically stream its data from a
19590 staging copy in CPU memory to GPU memory would rather be a feature of another,
19591 higher-level library implemented on top of VMA.
19592 VMA doesn't record any commands to a `VkCommandBuffer`. It just allocates memory.
19593 -# **Recreation of buffers and images.** Although the library has functions for
19594 buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to
19595 recreate these objects yourself after defragmentation. That is because the big
19596 structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
19597 #VmaAllocation object.
19598 -# **Handling CPU memory allocation failures.** When dynamically creating small C++
19599 objects in CPU memory (not Vulkan memory), allocation failures are not checked
19600 and handled gracefully, because that would complicate code significantly and
19601 is usually not needed in desktop PC applications anyway.
19602 Success of an allocation is just checked with an assert.
19603 -# **Code free of any compiler warnings.** Maintaining the library to compile and
19604 work correctly on so many different platforms is hard enough. Being free of
19605 any warnings, on any version of any compiler, is simply not feasible.
19606 There are many preprocessor macros that make some variables unused, function parameters unreferenced,
19607 or conditional expressions constant in some configurations.
19608 The code of this library should not be bigger or more complicated just to silence these warnings.
19609 It is recommended to disable such warnings instead.
19610 -# This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but
19611 are not going to be included into this repository.
19612 */
19613